From d9ccd62a8bcaa1d4b38f7ef6ba004f359f0042b5 Mon Sep 17 00:00:00 2001 From: Jingoo Han Date: Thu, 5 Dec 2013 19:21:10 -0800 Subject: Input: use dev_get_platdata() Use the wrapper function for retrieving the platform data instead of accessing dev->platform_data directly. This is a cosmetic change to make the code simpler and enhance the readability. Signed-off-by: Jingoo Han Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 59aa24002c7..37ea05741d2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1130,7 +1130,7 @@ static void mxt_input_close(struct input_dev *dev) static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct mxt_platform_data *pdata = client->dev.platform_data; + const struct mxt_platform_data *pdata = dev_get_platdata(&client->dev); struct mxt_data *data; struct input_dev *input_dev; int error; -- cgit v1.2.3-70-g09d2 From efbafbd5ebb9601d2cdff8698a4b65593ad21be5 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Mon, 6 Jan 2014 10:27:05 -0800 Subject: Input: delete non-required instances of include None of these files are actually using any __init type directives and hence don't need to include . Most are just a left over from __devinit and __cpuinit removal, or simply due to code getting copied from one driver to the next. Signed-off-by: Paul Gortmaker Signed-off-by: Dmitry Torokhov Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 37ea05741d2..a70400754e9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -12,7 +12,6 @@ */ #include -#include #include #include #include -- cgit v1.2.3-70-g09d2 From f0039e5145351493b87e214e1bca8e8f50a05959 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:14:49 -0700 Subject: Input: atmel_mxt_ts - remove unnecessary platform data It is not necessary to download these values to the maXTouch chip on every probe, since they are stored in NVRAM. It makes life difficult when tuning the device to keep them in sync with the config array/file, and requires a new kernel build for minor tweaks. These parameters only represent a tiny subset of the available configuration options, tracking all of these options in platform data would be a endless task. In addition, different versions of maXTouch chips may have these values in different places or may not even have them at all. Having these values also makes life more complex for device tree and other platforms where having to define a static configuration isn't helpful. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- arch/arm/mach-s5pv210/mach-goni.c | 5 ---- drivers/input/touchscreen/atmel_mxt_ts.c | 50 -------------------------------- include/linux/i2c/atmel_mxt_ts.h | 6 +--- 3 files changed, 1 insertion(+), 60 deletions(-) diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 30b24ad84f4..237f4193cb0 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 a70400754e9..7eb515caf21 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -685,54 +685,6 @@ static int mxt_make_highchg(struct mxt_data *data) return 0; } -static void mxt_handle_pdata(struct mxt_data *data) -{ - const struct mxt_platform_data *pdata = data->pdata; - u8 voltage; - - /* Set touchscreen lines */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_XSIZE, - pdata->x_line); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_YSIZE, - pdata->y_line); - - /* Set touchscreen orient */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, MXT_TOUCH_ORIENT, - pdata->orient); - - /* Set touchscreen burst length */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_BLEN, pdata->blen); - - /* Set touchscreen threshold */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_TCHTHR, pdata->threshold); - - /* Set touchscreen resolution */ - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); - mxt_write_object(data, MXT_TOUCH_MULTI_T9, - MXT_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); - - /* Set touchscreen voltage */ - if (pdata->voltage) { - if (pdata->voltage < MXT_VOLTAGE_DEFAULT) { - voltage = (MXT_VOLTAGE_DEFAULT - pdata->voltage) / - MXT_VOLTAGE_STEP; - voltage = 0xff - voltage + 1; - } else - voltage = (pdata->voltage - MXT_VOLTAGE_DEFAULT) / - MXT_VOLTAGE_STEP; - - mxt_write_object(data, MXT_SPT_CTECONFIG_T28, - MXT_CTE_VOLTAGE, voltage); - } -} - static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -840,8 +792,6 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - mxt_handle_pdata(data); - /* Backup to memory */ mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_BACKUPNV, diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 99e379b7439..eff0cdc0884 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]; -- cgit v1.2.3-70-g09d2 From 89a8864fbc0dc07537fbb00d153dc8e6f817e90e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:15:45 -0700 Subject: Input: atmel_mxt_ts - improve T19 GPIO keys handling * The mapping of the GPIO numbers into the T19 status byte varies between different maXTouch chips. Some have up to 7 GPIOs. Allowing a keycode array of up to 8 items is simpler and more generic. So replace #define with configurable number of keys which also allows the removal of is_tp. * Rename platform data parameters to include "t19" to prevent confusion with T15 key array. * Probe aborts early on when pdata is NULL, so no need to check. * Move "int i" to beginning of function (mixed declarations and code) * Use API calls rather than __set_bit() * Remove unused dev variable. Signed-off-by: Nick Dyer Acked-by: Yufeng Shen Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- drivers/input/touchscreen/atmel_mxt_ts.c | 44 ++++++++++++-------------------- include/linux/i2c/atmel_mxt_ts.h | 7 ++--- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 7eb515caf21..65df362cf32 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -180,12 +180,6 @@ #define MXT_FWRESET_TIME 175 /* msec */ -/* MXT_SPT_GPIOPWM_T19 field */ -#define MXT_GPIO0_MASK 0x04 -#define MXT_GPIO1_MASK 0x08 -#define MXT_GPIO2_MASK 0x10 -#define MXT_GPIO3_MASK 0x20 - /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa #define MXT_UNLOCK_CMD_LSB 0xdc @@ -250,7 +244,6 @@ struct mxt_data { const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info info; - bool is_tp; unsigned int irq; unsigned int max_x; @@ -515,15 +508,16 @@ static int mxt_write_object(struct mxt_data *data, static void mxt_input_button(struct mxt_data *data, struct mxt_message *message) { struct input_dev *input = data->input_dev; + const struct mxt_platform_data *pdata = data->pdata; bool button; int i; /* Active-low switch */ - for (i = 0; i < MXT_NUM_GPIO; i++) { - if (data->pdata->key_map[i] == KEY_RESERVED) + for (i = 0; i < pdata->t19_num_keys; i++) { + if (pdata->t19_keymap[i] == KEY_RESERVED) continue; - button = !(message->message[0] & MXT_GPIO0_MASK << i); - input_report_key(input, data->pdata->key_map[i], button); + button = !(message->message[0] & (1 << i)); + input_report_key(input, pdata->t19_keymap[i], button); } } @@ -1084,6 +1078,8 @@ static int mxt_probe(struct i2c_client *client, struct input_dev *input_dev; int error; unsigned int num_mt_slots; + unsigned int mt_flags = 0; + int i; if (!pdata) return -EINVAL; @@ -1096,10 +1092,7 @@ static int mxt_probe(struct i2c_client *client, goto err_free_mem; } - data->is_tp = pdata && pdata->is_tp; - - input_dev->name = (data->is_tp) ? "Atmel maXTouch Touchpad" : - "Atmel maXTouch Touchscreen"; + input_dev->name = "Atmel maXTouch Touchscreen"; snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0", client->adapter->nr, client->addr); @@ -1125,20 +1118,15 @@ static int mxt_probe(struct i2c_client *client, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - if (data->is_tp) { - int i; - __set_bit(INPUT_PROP_POINTER, input_dev->propbit); + if (pdata->t19_num_keys) { __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); - for (i = 0; i < MXT_NUM_GPIO; i++) - if (pdata->key_map[i] != KEY_RESERVED) - __set_bit(pdata->key_map[i], input_dev->keybit); + for (i = 0; i < pdata->t19_num_keys; i++) + if (pdata->t19_keymap[i] != KEY_RESERVED) + input_set_capability(input_dev, EV_KEY, + pdata->t19_keymap[i]); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUINTTAP, input_dev->keybit); + mt_flags |= INPUT_MT_POINTER; input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM); input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM); @@ -1146,6 +1134,8 @@ static int mxt_probe(struct i2c_client *client, MXT_PIXELS_PER_MM); input_abs_set_res(input_dev, ABS_MT_POSITION_Y, MXT_PIXELS_PER_MM); + + input_dev->name = "Atmel maXTouch Touchpad"; } /* For single touch */ @@ -1158,7 +1148,7 @@ static int mxt_probe(struct i2c_client *client, /* For multi touch */ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1; - error = input_mt_init_slots(input_dev, num_mt_slots, 0); + error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags); if (error) goto err_free_object; input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index eff0cdc0884..d26080dc606 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 */ -- cgit v1.2.3-70-g09d2 From c1cdf788aeff2027aae1f3059ec15154769a6258 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:16:07 -0700 Subject: 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 65df362cf32..0cff8bb2ad7 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; } -- cgit v1.2.3-70-g09d2 From a20c2ea371861e08ebf8d77ef09b7a5b4ee7c4e0 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Sat, 6 Jul 2013 22:16:26 -0700 Subject: 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 0cff8bb2ad7..40af02c2611 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); -- cgit v1.2.3-70-g09d2 From 4f12798d4b001f4a0c319365385b66461d1f4a5a Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sat, 6 Jul 2013 22:17:00 -0700 Subject: 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 40af02c2611..f42ccebb166 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -14,6 +14,8 @@ */ #include +#include +#include #include #include #include @@ -246,16 +248,19 @@ struct mxt_data { const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info info; - unsigned int irq; unsigned int max_x; unsigned int max_y; + bool in_bootloader; /* Cached parameters from object table */ u8 T6_reportid; u8 T9_reportid_min; u8 T9_reportid_max; u8 T19_reportid; + + /* for fw update in bootloader */ + struct completion bl_completion; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -339,12 +344,50 @@ static void mxt_dump_message(struct device *dev, message->reportid, 7, message->message); } -static int mxt_check_bootloader(struct i2c_client *client, - unsigned int state) +static int mxt_wait_for_chg(struct mxt_data *data, unsigned int timeout_ms) { + struct device *dev = &data->client->dev; + struct completion *comp = &data->bl_completion; + unsigned long timeout = msecs_to_jiffies(timeout_ms); + long ret; + + ret = wait_for_completion_interruptible_timeout(comp, timeout); + if (ret < 0) { + return ret; + } else if (ret == 0) { + dev_err(dev, "Wait for completion timed out.\n"); + return -ETIMEDOUT; + } + return 0; +} + +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +{ + struct i2c_client *client = data->client; u8 val; + int ret; recheck: + if (state != MXT_WAITING_BOOTLOAD_CMD) { + /* + * In application update mode, the interrupt + * line signals state transitions. We must wait for the + * CHG assertion before reading the status byte. + * Once the status byte has been read, the line is deasserted. + */ + ret = mxt_wait_for_chg(data, 300); + if (ret) { + /* + * TODO: handle -ERESTARTSYS better by terminating + * fw update process before returning to userspace + * by writing length 0x000 to device (iff we are in + * WAITING_FRAME_DATA state). + */ + dev_err(&client->dev, "Update wait error %d\n", ret); + return ret; + } + } + if (i2c_master_recv(client, &val, 1) != 1) { dev_err(&client->dev, "%s: i2c recv failed\n", __func__); return -EIO; @@ -590,9 +633,8 @@ static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg) return (id >= data->T9_reportid_min && id <= data->T9_reportid_max); } -static irqreturn_t mxt_interrupt(int irq, void *dev_id) +static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) { - struct mxt_data *data = dev_id; struct mxt_message message; const u8 *payload = &message.message[0]; struct device *dev = &data->client->dev; @@ -632,6 +674,19 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t mxt_interrupt(int irq, void *dev_id) +{ + struct mxt_data *data = dev_id; + + if (data->in_bootloader) { + /* bootloader state transition completion */ + complete(&data->bl_completion); + return IRQ_HANDLED; + } + + return mxt_process_messages_until_invalid(data); +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -947,6 +1002,8 @@ static int mxt_load_fw(struct device *dev, const char *fn) } /* Change to the bootloader mode */ + data->in_bootloader = true; + mxt_write_object(data, MXT_GEN_COMMAND_T6, MXT_COMMAND_RESET, MXT_BOOT_VALUE); msleep(MXT_RESET_TIME); @@ -957,18 +1014,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) else client->addr = MXT_BOOT_HIGH; - ret = mxt_check_bootloader(client, MXT_WAITING_BOOTLOAD_CMD); + INIT_COMPLETION(data->bl_completion); + + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); if (ret) - goto out; + goto disable_irq; /* Unlock bootloader */ mxt_unlock_bootloader(client); while (pos < fw->size) { - ret = mxt_check_bootloader(client, - MXT_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); if (ret) - goto out; + goto disable_irq; frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); @@ -980,17 +1038,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) /* Write one frame to device */ mxt_fw_write(client, fw->data + pos, frame_size); - ret = mxt_check_bootloader(client, - MXT_FRAME_CRC_PASS); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); if (ret) - goto out; + goto disable_irq; pos += frame_size; dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); } -out: + data->in_bootloader = false; + +disable_irq: + disable_irq(data->irq); release_firmware(fw); /* Change to slave address of application */ @@ -1009,8 +1069,6 @@ static ssize_t mxt_update_fw_store(struct device *dev, struct mxt_data *data = dev_get_drvdata(dev); int error; - disable_irq(data->irq); - error = mxt_load_fw(dev, MXT_FW_NAME); if (error) { dev_err(dev, "The firmware update failed(%d)\n", error); @@ -1024,13 +1082,13 @@ static ssize_t mxt_update_fw_store(struct device *dev, mxt_free_object_table(data); mxt_initialize(data); - } - enable_irq(data->irq); + enable_irq(data->irq); - error = mxt_make_highchg(data); - if (error) - return error; + error = mxt_make_highchg(data); + if (error) + return error; + } return count; } @@ -1120,6 +1178,8 @@ static int mxt_probe(struct i2c_client *client, data->pdata = pdata; data->irq = client->irq; + init_completion(&data->bl_completion); + mxt_calc_resolution(data); error = mxt_initialize(data); -- cgit v1.2.3-70-g09d2 From 0fd587db342aebd4123714d05d082b5a77dbaa25 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Sat, 6 Jul 2013 22:17:18 -0700 Subject: 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 f42ccebb166..b9ef87a09cb 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 @@ recheck: * 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); -- cgit v1.2.3-70-g09d2 From fb3af1c77ecb6e559d293ba1e902130d1e371577 Mon Sep 17 00:00:00 2001 From: Iiro Valkonen Date: Sat, 6 Jul 2013 22:18:03 -0700 Subject: 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 b9ef87a09cb..c59444e9235 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 @@ recheck: * CHG assertion before reading the status byte. * Once the status byte has been read, the line is deasserted. */ - ret = mxt_wait_for_chg(data, MXT_FW_CHG_TIMEOUT); + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_CHG_TIMEOUT); if (ret) { /* * TODO: handle -ERESTARTSYS better by terminating @@ -654,6 +667,9 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) unsigned csum = mxt_extract_T6_csum(&payload[1]); dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", status, csum); + + if (status & MXT_T6_STATUS_RESET) + complete(&data->reset_completion); } else if (mxt_is_T9_message(data, &message)) { int id = reportid - data->T9_reportid_min; mxt_input_touchevent(data, &message, id); @@ -687,6 +703,59 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) return mxt_process_messages_until_invalid(data); } +static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, + u8 value, bool wait) +{ + u16 reg; + u8 command_register; + int timeout_counter = 0; + int ret; + + reg = data->T6_address + cmd_offset; + + ret = mxt_write_reg(data->client, reg, value); + if (ret) + return ret; + + if (!wait) + return 0; + + do { + msleep(20); + ret = __mxt_read_reg(data->client, reg, 1, &command_register); + if (ret) + return ret; + } while (command_register != 0 && timeout_counter++ <= 100); + + if (timeout_counter > 100) { + dev_err(&data->client->dev, "Command failed!\n"); + return -EIO; + } + + return 0; +} + +static int mxt_soft_reset(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int ret = 0; + + dev_info(dev, "Resetting chip\n"); + + INIT_COMPLETION(data->reset_completion); + + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_RESET_VALUE, false); + if (ret) + return ret; + + ret = mxt_wait_for_completion(data, &data->reset_completion, + MXT_RESET_TIMEOUT); + if (ret) + return ret; + + return 0; +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -800,6 +869,7 @@ static int mxt_get_object_table(struct mxt_data *data) switch (object->type) { case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; + data->T6_address = object->start_address; break; case MXT_TOUCH_MULTI_T9: data->T9_reportid_min = min_id; @@ -853,16 +923,10 @@ static int mxt_initialize(struct mxt_data *data) if (error) goto err_free_object_table; - /* Backup to memory */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_BACKUPNV, - MXT_BACKUP_VALUE); - msleep(MXT_BACKUP_TIME); - - /* Soft reset */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, 1); - msleep(MXT_RESET_TIME); + error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, + MXT_BACKUP_VALUE, false); + if (!error) + mxt_soft_reset(data); /* Update matrix size at info struct */ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); @@ -1004,8 +1068,10 @@ static int mxt_load_fw(struct device *dev, const char *fn) /* Change to the bootloader mode */ data->in_bootloader = true; - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, MXT_BOOT_VALUE); + ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; + msleep(MXT_RESET_TIME); /* Change to slave address of bootloader */ @@ -1048,7 +1114,8 @@ static int mxt_load_fw(struct device *dev, const char *fn) } /* Wait for flash. */ - ret = mxt_wait_for_chg(data, MXT_FW_RESET_TIME); + ret = mxt_wait_for_completion(data, &data->bl_completion, + MXT_FW_RESET_TIME); if (ret) goto disable_irq; @@ -1057,12 +1124,13 @@ static int mxt_load_fw(struct device *dev, const char *fn) * the CHG line after bootloading has finished, so ignore potential * errors. */ - mxt_wait_for_chg(data, MXT_FW_RESET_TIME); + mxt_wait_for_completion(data, &data->bl_completion, MXT_FW_RESET_TIME); data->in_bootloader = false; disable_irq: disable_irq(data->irq); +release_firmware: release_firmware(fw); /* Change to slave address of application */ @@ -1187,6 +1255,7 @@ static int mxt_probe(struct i2c_client *client, data->irq = client->irq; init_completion(&data->bl_completion); + init_completion(&data->reset_completion); mxt_calc_resolution(data); @@ -1314,11 +1383,7 @@ static int mxt_resume(struct device *dev) struct mxt_data *data = i2c_get_clientdata(client); struct input_dev *input_dev = data->input_dev; - /* Soft reset */ - mxt_write_object(data, MXT_GEN_COMMAND_T6, - MXT_COMMAND_RESET, 1); - - msleep(MXT_RESET_TIME); + mxt_soft_reset(data); mutex_lock(&input_dev->mutex); -- cgit v1.2.3-70-g09d2 From 61cadc58c3f1a5c8594c906f791e7aa2a6323fb7 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:18:27 -0700 Subject: 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 c59444e9235..6cfc56223a4 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -420,7 +420,8 @@ recheck: } 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; -- cgit v1.2.3-70-g09d2 From 85c876cdf5c9c003ae764d88f01a71fca7c99ca1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:04 -0700 Subject: 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 6cfc56223a4..0ff28dd4e7c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -188,6 +188,7 @@ #define MXT_BACKUP_TIME 50 /* msec */ #define MXT_RESET_TIME 200 /* msec */ #define MXT_RESET_TIMEOUT 3000 /* msec */ +#define MXT_CRC_TIMEOUT 1000 /* msec */ #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ @@ -259,6 +260,7 @@ struct mxt_data { unsigned int max_x; unsigned int max_y; bool in_bootloader; + u32 config_crc; /* Cached parameters from object table */ u8 T6_reportid; @@ -272,6 +274,9 @@ struct mxt_data { /* for reset handling */ struct completion reset_completion; + + /* for config update handling */ + struct completion crc_completion; }; static size_t mxt_obj_size(const struct mxt_object *obj) @@ -636,7 +641,7 @@ static void mxt_input_touchevent(struct mxt_data *data, } } -static unsigned mxt_extract_T6_csum(const u8 *csum) +static u16 mxt_extract_T6_csum(const u8 *csum) { return csum[0] | (csum[1] << 8) | (csum[2] << 16); } @@ -654,6 +659,7 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) struct device *dev = &data->client->dev; u8 reportid; bool update_input = false; + u32 crc; do { if (mxt_read_message(data, &message)) { @@ -665,9 +671,15 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) if (reportid == data->T6_reportid) { u8 status = payload[0]; - unsigned csum = mxt_extract_T6_csum(&payload[1]); + + crc = mxt_extract_T6_csum(&payload[1]); + if (crc != data->config_crc) { + data->config_crc = crc; + complete(&data->crc_completion); + } + dev_dbg(dev, "Status: %02x Config Checksum: %06x\n", - status, csum); + status, data->config_crc); if (status & MXT_T6_STATUS_RESET) complete(&data->reset_completion); @@ -757,6 +769,24 @@ static int mxt_soft_reset(struct mxt_data *data) return 0; } +static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) +{ + /* + * On failure, CRC is set to 0 and config will always be + * downloaded. + */ + data->config_crc = 0; + INIT_COMPLETION(data->crc_completion); + + mxt_t6_command(data, cmd, value, true); + + /* + * Wait for crc message. On failure, CRC is set to 0 and config will + * always be downloaded. + */ + mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); +} + static int mxt_check_reg_init(struct mxt_data *data) { const struct mxt_platform_data *pdata = data->pdata; @@ -771,6 +801,16 @@ static int mxt_check_reg_init(struct mxt_data *data) return 0; } + mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); + + if (data->config_crc == pdata->config_crc) { + dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); + return 0; + } + + dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", + data->config_crc, pdata->config_crc); + for (i = 0; i < data->info.object_num; i++) { object = data->object_table + i; @@ -790,6 +830,14 @@ static int mxt_check_reg_init(struct mxt_data *data) index += size; } + mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); + + ret = mxt_soft_reset(data); + if (ret) + return ret; + + dev_info(dev, "Config successfully updated\n"); + return 0; } @@ -929,11 +977,6 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; } - error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV, - MXT_BACKUP_VALUE, false); - if (!error) - mxt_soft_reset(data); - /* Update matrix size at info struct */ error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val); if (error) @@ -1263,6 +1306,7 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->bl_completion); init_completion(&data->reset_completion); + init_completion(&data->crc_completion); mxt_calc_resolution(data); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index d26080dc606..9f92135b662 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; -- cgit v1.2.3-70-g09d2 From e4c0272a02eb81a14b02a7dd1ed4e28d460043d0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:33 -0700 Subject: Input: atmel_mxt_ts - download device config using firmware loader The existing implementation which encodes the configuration as a binary blob in platform data is unsatisfactory since it requires a kernel recompile for the configuration to be changed, and it doesn't deal well with firmware changes that move values around on the chip. Atmel define an ASCII format for the configuration which can be exported from their tools. This patch implements a parser for that format which loads the configuration via the firmware loader and sends it to the MXT chip. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- drivers/input/touchscreen/atmel_mxt_ts.c | 234 +++++++++++++++++++++++-------- include/linux/i2c/atmel_mxt_ts.h | 4 - 2 files changed, 176 insertions(+), 62 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 0ff28dd4e7c..2a591918c8c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -35,8 +35,10 @@ #define MXT_BOOT_LOW 0x24 #define MXT_BOOT_HIGH 0x25 -/* Firmware */ +/* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" +#define MXT_CFG_NAME "maxtouch.cfg" +#define MXT_CFG_MAGIC "OBP_RAW V1" /* Registers */ #define MXT_INFO 0x00 @@ -322,37 +324,6 @@ static bool mxt_object_readable(unsigned int type) } } -static bool mxt_object_writable(unsigned int type) -{ - switch (type) { - case MXT_GEN_COMMAND_T6: - case MXT_GEN_POWER_T7: - case MXT_GEN_ACQUIRE_T8: - case MXT_TOUCH_MULTI_T9: - case MXT_TOUCH_KEYARRAY_T15: - case MXT_TOUCH_PROXIMITY_T23: - case MXT_TOUCH_PROXKEY_T52: - case MXT_PROCI_GRIPFACE_T20: - case MXT_PROCG_NOISE_T22: - case MXT_PROCI_ONETOUCH_T24: - case MXT_PROCI_TWOTOUCH_T27: - case MXT_PROCI_GRIP_T40: - case MXT_PROCI_PALM_T41: - case MXT_PROCI_TOUCHSUPPRESSION_T42: - case MXT_PROCI_STYLUS_T47: - case MXT_PROCG_NOISESUPPRESSION_T48: - case MXT_SPT_COMMSCONFIG_T18: - case MXT_SPT_GPIOPWM_T19: - case MXT_SPT_SELFTEST_T25: - case MXT_SPT_CTECONFIG_T28: - case MXT_SPT_DIGITIZER_T43: - case MXT_SPT_CTECONFIG_T46: - return true; - default: - return false; - } -} - static void mxt_dump_message(struct device *dev, struct mxt_message *message) { @@ -546,7 +517,7 @@ mxt_get_object(struct mxt_data *data, u8 type) return object; } - dev_err(&data->client->dev, "Invalid object type T%u\n", type); + dev_warn(&data->client->dev, "Invalid object type T%u\n", type); return NULL; } @@ -787,58 +758,205 @@ static void mxt_update_crc(struct mxt_data *data, u8 cmd, u8 value) mxt_wait_for_completion(data, &data->crc_completion, MXT_CRC_TIMEOUT); } +/* + * mxt_check_reg_init - download configuration to chip + * + * Atmel Raw Config File Format + * + * The first four lines of the raw config file contain: + * 1) Version + * 2) Chip ID Information (first 7 bytes of device memory) + * 3) Chip Information Block 24-bit CRC Checksum + * 4) Chip Configuration 24-bit CRC Checksum + * + * The rest of the file consists of one line per object instance: + * + * + * - 2-byte object type as hex + * - 2-byte object instance number as hex + * - 2-byte object size as hex + * - array of 1-byte hex values + */ static int mxt_check_reg_init(struct mxt_data *data) { - const struct mxt_platform_data *pdata = data->pdata; - struct mxt_object *object; struct device *dev = &data->client->dev; - int index = 0; - int i, size; + struct mxt_info cfg_info; + struct mxt_object *object; + const struct firmware *cfg = NULL; int ret; + int offset; + int pos; + int i; + u32 info_crc, config_crc; + unsigned int type, instance, size; + u8 val; + u16 reg; - if (!pdata->config) { - dev_dbg(dev, "No cfg data defined, skipping reg init\n"); + ret = request_firmware(&cfg, MXT_CFG_NAME, dev); + if (ret < 0) { + dev_err(dev, "Failure to request config file %s\n", + MXT_CFG_NAME); return 0; } mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); - if (data->config_crc == pdata->config_crc) { - dev_info(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - return 0; + if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { + dev_err(dev, "Unrecognised config file\n"); + ret = -EINVAL; + goto release; } - dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n", - data->config_crc, pdata->config_crc); + pos = strlen(MXT_CFG_MAGIC); + + /* Load information block and check */ + for (i = 0; i < sizeof(struct mxt_info); i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + (unsigned char *)&cfg_info + i, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format\n"); + ret = -EINVAL; + goto release; + } - for (i = 0; i < data->info.object_num; i++) { - object = data->object_table + i; + pos += offset; + } + + if (cfg_info.family_id != data->info.family_id) { + dev_err(dev, "Family ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + if (cfg_info.variant_id != data->info.variant_id) { + dev_err(dev, "Variant ID mismatch!\n"); + ret = -EINVAL; + goto release; + } + + if (cfg_info.version != data->info.version) + dev_err(dev, "Warning: version mismatch!\n"); + + if (cfg_info.build != data->info.build) + dev_err(dev, "Warning: build num mismatch!\n"); + + ret = sscanf(cfg->data + pos, "%x%n", &info_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Info CRC\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + /* Check config CRC */ + ret = sscanf(cfg->data + pos, "%x%n", &config_crc, &offset); + if (ret != 1) { + dev_err(dev, "Bad format: failed to parse Config CRC\n"); + ret = -EINVAL; + goto release; + } + pos += offset; - if (!mxt_object_writable(object->type)) + if (data->config_crc == config_crc) { + dev_dbg(dev, "Config CRC 0x%06X: OK\n", config_crc); + ret = 0; + goto release; + } + + dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", + data->config_crc, config_crc); + + while (pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + ret = 1; + goto release; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + ret = -EINVAL; + goto release; + } + pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + pos += offset; + } continue; + } - size = mxt_obj_size(object) * mxt_obj_instances(object); - if (index + size > pdata->config_length) { - dev_err(dev, "Not enough config data!\n"); - return -EINVAL; + if (size > mxt_obj_size(object)) { + dev_err(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); } - ret = __mxt_write_reg(data->client, object->start_address, - size, &pdata->config[index]); - if (ret) - return ret; - index += size; + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + ret = -EINVAL; + goto release; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + ret = -EINVAL; + goto release; + } + pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + ret = mxt_write_reg(data->client, reg + i, val); + if (ret) + goto release; + + } + + /* + * If firmware is upgraded, new bytes may be added to end of + * objects. It is generally forward compatible to zero these + * bytes - previous behaviour will be retained. However + * this does invalidate the CRC and will force a config + * download every time until the configuration is updated. + */ + if (size < mxt_obj_size(object)) { + dev_info(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + + for (i = size + 1; i < mxt_obj_size(object); i++) { + ret = mxt_write_reg(data->client, reg + i, 0); + if (ret) + goto release; + } + } } mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); ret = mxt_soft_reset(data); if (ret) - return ret; + goto release; dev_info(dev, "Config successfully updated\n"); - return 0; +release: + release_firmware(cfg); + return ret; } static int mxt_make_highchg(struct mxt_data *data) diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index 9f92135b662..b569bb876d1 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; -- cgit v1.2.3-70-g09d2 From 5cbaf241979308baea567ab29c67d3e6d4393252 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:19:51 -0700 Subject: 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 2a591918c8c..df64a2fd139 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; -- cgit v1.2.3-70-g09d2 From 80256b454ebb0a8d13e719c296eb389b2f412686 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:14 -0700 Subject: 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 df64a2fd139..280abdc92d7 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 @@ recheck: * 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 @@ recheck: } 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 @@ recheck: 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 @@ done: static int mxt_load_fw(struct device *dev, const char *fn) { struct mxt_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; const struct firmware *fw = NULL; unsigned int frame_size; unsigned int pos = 0; @@ -1354,6 +1409,10 @@ static int mxt_load_fw(struct device *dev, const char *fn) return ret; } + ret = mxt_lookup_bootloader_address(data); + if (ret) + goto release_firmware; + /* Change to the bootloader mode */ data->in_bootloader = true; @@ -1363,12 +1422,6 @@ static int mxt_load_fw(struct device *dev, const char *fn) msleep(MXT_RESET_TIME); - /* Change to slave address of bootloader */ - if (client->addr == MXT_APP_LOW) - client->addr = MXT_BOOT_LOW; - else - client->addr = MXT_BOOT_HIGH; - INIT_COMPLETION(data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); @@ -1376,7 +1429,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) goto disable_irq; /* Unlock bootloader */ - mxt_unlock_bootloader(client); + mxt_unlock_bootloader(data); while (pos < fw->size) { ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); @@ -1391,7 +1444,9 @@ static int mxt_load_fw(struct device *dev, const char *fn) frame_size += 2; /* Write one frame to device */ - mxt_fw_write(client, fw->data + pos, frame_size); + ret = mxt_bootloader_write(data, fw->data + pos, frame_size); + if (ret) + goto disable_irq; ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); if (ret) @@ -1421,13 +1476,6 @@ disable_irq: 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; } -- cgit v1.2.3-70-g09d2 From 63a72b26f9c79be3e28cdc60fe2c0579a6d42bc0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:36 -0700 Subject: 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 280abdc92d7..50232ef583b 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 @@ recheck: 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: -- cgit v1.2.3-70-g09d2 From 84c69b515ef3e89836e9f3463e4ff683c876de37 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:20:52 -0700 Subject: 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 50232ef583b..2e6686bb86c 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -486,8 +486,12 @@ recheck: 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); } -- cgit v1.2.3-70-g09d2 From f22b3f8d345d1964f011542e64f9b0026f1d7fca Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:21:30 -0700 Subject: 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 2e6686bb86c..1f168797a4c 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 -- cgit v1.2.3-70-g09d2 From 3e7625bebbb5f8884d309de5cc41dc365937f316 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:21:50 -0700 Subject: 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 1f168797a4c..d0cf5b4df0a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1425,6 +1425,30 @@ done: 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; -- cgit v1.2.3-70-g09d2 From 366b0538648d4bba4e99531543cea8322f79f9e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:24:31 -0700 Subject: Input: atmel_mxt_ts - read screen config from chip By reading the touchscreen configuration from the settings that the maXTouch chip is actually using, we can remove some platform data. The matrix size is not used for anything, and results in some rather confusing code to re-read it because it may change when configuration is downloaded, so don't print it out. Signed-off-by: Nick Dyer Acked-by: Benson Leung Acked-by: Yufeng Shen Signed-off-by: Dmitry Torokhov Conflicts: drivers/platform/chrome/chromeos_laptop.c --- arch/arm/mach-s5pv210/mach-goni.c | 3 - drivers/input/touchscreen/atmel_mxt_ts.c | 136 +++++++++++++++---------------- include/linux/i2c/atmel_mxt_ts.h | 14 ---- 3 files changed, 65 insertions(+), 88 deletions(-) diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 237f4193cb0..8ebc41a7243 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 d0cf5b4df0a..25fecf3a647 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 @@ err_free_object_table: return error; } -static void mxt_calc_resolution(struct mxt_data *data) -{ - unsigned int max_x = data->pdata->x_size - 1; - unsigned int max_y = data->pdata->y_size - 1; - - if (data->pdata->orient & MXT_XY_SWITCH) { - data->max_x = max_y; - data->max_y = max_x; - } else { - data->max_x = max_x; - data->max_y = max_y; - } -} - /* Firmware Version is returned as Major.Minor.Build */ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1670,8 +1666,6 @@ static int mxt_probe(struct i2c_client *client, init_completion(&data->reset_completion); init_completion(&data->crc_completion); - mxt_calc_resolution(data); - error = mxt_initialize(data); if (error) goto err_free_mem; diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index b569bb876d1..02bf6ea3170 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; -- cgit v1.2.3-70-g09d2 From 8080efdf9f7926e1662e50a8863f1a54a879f680 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:26:50 -0700 Subject: 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 25fecf3a647..092037bc93b 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 @@ release: 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) -- cgit v1.2.3-70-g09d2 From 44558141ec624e0bf4e802d130fab3dd5fc3dac1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:29:37 -0700 Subject: 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 092037bc93b..d87fc0fb33d 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); } } -- cgit v1.2.3-70-g09d2 From 055157849caf1b00d6399acfa7a44ce8da9b6eaf Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:30:11 -0700 Subject: 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 d87fc0fb33d..fe575afcfe9 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 { -- cgit v1.2.3-70-g09d2 From 1316ad433374828fa929954b5080f39f8515e720 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Sat, 6 Jul 2013 22:30:47 -0700 Subject: 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 fe575afcfe9..cec9b39efea 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; } -- cgit v1.2.3-70-g09d2 From 9f96ab77c6db0c82005eca888990a805653937bb Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 20 Feb 2014 16:35:49 +0000 Subject: 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 cec9b39efea..7e8acf38994 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 -- cgit v1.2.3-70-g09d2 From 4c69d6e569f50f37c49c5d44408e3b36811ecb01 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 16:33:43 +0000 Subject: 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 7e8acf38994..afc812c5036 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); -- cgit v1.2.3-70-g09d2 From 5076b2a8d1691d4353d8b0d72da60441b07c2975 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 4 Dec 2012 17:16:35 +0000 Subject: 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 afc812c5036..098cac64eb6 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; } -- cgit v1.2.3-70-g09d2 From 9173c0400f1b52678eb7b4810d6c00ecf3e8672f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 18:21:08 +0000 Subject: 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 098cac64eb6..3740a97d9df 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 @@ recheck: switch (state) { case MXT_WAITING_BOOTLOAD_CMD: case MXT_WAITING_FRAME_DATA: + case MXT_APP_CRC_FAIL: val &= ~MXT_BOOT_STATUS_MASK; break; case MXT_FRAME_CRC_PASS: @@ -1395,8 +1420,14 @@ static int mxt_initialize(struct mxt_data *data) int error; error = mxt_get_info(data); - if (error) - return error; + if (error) { + error = mxt_probe_bootloader(data); + if (error) + return error; + + data->in_bootloader = true; + return 0; + } /* Get object table information */ error = mxt_get_object_table(data); @@ -1570,15 +1601,19 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto release_firmware; - /* Change to the bootloader mode */ - data->in_bootloader = true; + if (!data->in_bootloader) { + /* Change to the bootloader mode */ + data->in_bootloader = true; - ret = mxt_t6_command(data, MXT_COMMAND_RESET, MXT_BOOT_VALUE, false); - if (ret) - goto release_firmware; + ret = mxt_t6_command(data, MXT_COMMAND_RESET, + MXT_BOOT_VALUE, false); + if (ret) + goto release_firmware; - msleep(MXT_RESET_TIME); + msleep(MXT_RESET_TIME); + } + mxt_free_object_table(data); INIT_COMPLETION(data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); @@ -1663,8 +1698,6 @@ static ssize_t mxt_update_fw_store(struct device *dev, } else { dev_info(dev, "The firmware update succeeded\n"); - mxt_free_object_table(data); - error = mxt_initialize(data); if (error) return error; @@ -1850,9 +1883,11 @@ static int mxt_probe(struct i2c_client *client, if (error) goto err_free_irq; - error = mxt_initialize_t9_input_device(data); - if (error) - goto err_free_object; + if (!data->in_bootloader) { + error = mxt_initialize_t9_input_device(data); + if (error) + goto err_free_object; + } error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { -- cgit v1.2.3-70-g09d2 From 649c8d377b2cb31b5f2a1d548cad9dfd239af4e0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 28 Feb 2013 18:07:02 +0000 Subject: 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 3740a97d9df..25e63600477 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -452,14 +452,15 @@ static u8 mxt_get_bootloader_version(struct mxt_data *data, u8 val) } } -static int mxt_check_bootloader(struct mxt_data *data, unsigned int state) +static int mxt_check_bootloader(struct mxt_data *data, unsigned int state, + bool wait) { struct device *dev = &data->client->dev; u8 val; int ret; recheck: - if (state != MXT_WAITING_BOOTLOAD_CMD) { + if (wait) { /* * In application update mode, the interrupt * line signals state transitions. We must wait for the @@ -1616,15 +1617,23 @@ static int mxt_load_fw(struct device *dev, const char *fn) mxt_free_object_table(data); INIT_COMPLETION(data->bl_completion); - ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD); - if (ret) - goto disable_irq; + ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); + if (ret) { + /* Bootloader may still be unlocked from previous attempt */ + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, false); + if (ret) + goto disable_irq; + } else { + dev_info(dev, "Unlocking bootloader\n"); - /* Unlock bootloader */ - mxt_unlock_bootloader(data); + /* Unlock bootloader */ + ret = mxt_unlock_bootloader(data); + if (ret) + goto disable_irq; + } while (pos < fw->size) { - ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA); + ret = mxt_check_bootloader(data, MXT_WAITING_FRAME_DATA, true); if (ret) goto disable_irq; @@ -1638,7 +1647,7 @@ static int mxt_load_fw(struct device *dev, const char *fn) if (ret) goto disable_irq; - ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS); + ret = mxt_check_bootloader(data, MXT_FRAME_CRC_PASS, true); if (ret) { retry++; -- cgit v1.2.3-70-g09d2 From 27443634531bbd536223286f093e0dfc0a1db503 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 3 Sep 2012 15:46:22 +0100 Subject: 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 25e63600477..db44f3654e4 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: -- cgit v1.2.3-70-g09d2 From e576bd50fd484cd4430122d531d3e208b14097a3 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 15 Aug 2012 12:39:31 +0100 Subject: 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 db44f3654e4..bdba1210bd7 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 @@ recheck: 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; } -- cgit v1.2.3-70-g09d2 From 59b544ecfd2bdfd5bcb8402d0ca82765f67e5aec Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 11:56:14 +0000 Subject: 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 bdba1210bd7..0c9fc6953cb 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 @@ recheck: 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 @@ err_unregister_device: 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; -- cgit v1.2.3-70-g09d2 From 86f2ca1b5fecff7d1196f4d48c2f40b216a72611 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 12:12:16 +0000 Subject: 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 0c9fc6953cb..4ccb5ebc044 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 -- cgit v1.2.3-70-g09d2 From d81c60b6c5d88e1a72316248f40855328f30ec0d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 24 Feb 2014 14:35:23 +0000 Subject: 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 4ccb5ebc044..a2b7bfe90e5 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; -- cgit v1.2.3-70-g09d2 From c8fb7fae319bbbd22a41da3b9b7de97e8544545f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 5 Dec 2012 15:02:43 +0000 Subject: 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 a2b7bfe90e5..b004d00126a 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 @@ recheck: 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; -- cgit v1.2.3-70-g09d2 From 519925e6c9e10bd02d0247ea5cb02dd908721f66 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 1 Aug 2012 16:57:14 +0100 Subject: 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 b004d00126a..4ab5641cd97 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 -- cgit v1.2.3-70-g09d2 From 75b744c45fe7006764dd703bbdbae32863e91593 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 1 Aug 2012 16:57:38 +0100 Subject: 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 4ab5641cd97..66580b9774a 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; -- cgit v1.2.3-70-g09d2 From 35df04b62139f5bfe535ff4ccca42f1469ceaf32 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 1 Jun 2012 17:55:28 -0700 Subject: 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 66580b9774a..3f9c9413682 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); -- cgit v1.2.3-70-g09d2 From e68fba3acf88936a251be67d5d093c52662df7fc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 8 Aug 2012 18:55:13 +0100 Subject: 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 3f9c9413682..4136bc3e909 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); -- cgit v1.2.3-70-g09d2 From 495bf888c3ebd008b2383e46059a308448c09019 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 9 Aug 2012 16:13:49 +0100 Subject: 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 4136bc3e909..6aaad83f1b5 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); -- cgit v1.2.3-70-g09d2 From ba10e0677efa4e8d6013b2bdfc29dce39fbb864c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 9 Aug 2012 17:32:35 +0100 Subject: 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 6aaad83f1b5..4ec1bd548f6 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 02bf6ea3170..b7d20924eb7 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 */ -- cgit v1.2.3-70-g09d2 From 4e04c362fd784e631febfe19be21bd3ac23878bc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 16 Oct 2012 17:20:48 +0100 Subject: 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 4ec1bd548f6..cad4e302865 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; -- cgit v1.2.3-70-g09d2 From ba6d107a18b0594545d6fa76cac92a97e4122d54 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 7 Dec 2012 13:07:30 +0000 Subject: 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 cad4e302865..d278fe66bff 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 @@ retry_bootloader: } } - /* 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 @@ retry_bootloader: 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"); -- cgit v1.2.3-70-g09d2 From 3d1d92f3257a44ec9fa3c4b0e4a6d2771e9b4e33 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 17:20:26 +0000 Subject: 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 d278fe66bff..721d81ed319 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 @@ retry_bootloader: } } + error = mxt_check_retrigen(data); + if (error) + goto err_free_object_table; + error = mxt_acquire_irq(data); if (error) goto err_free_object_table; -- cgit v1.2.3-70-g09d2 From 6316d4d4ecc7d19c8cbe66ba353003fe85042752 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 16:01:07 +0000 Subject: 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 721d81ed319..771c3ddefaf 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); -- cgit v1.2.3-70-g09d2 From a243a6719230201586a8f0856de532875a8e6873 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 10 Dec 2012 17:06:42 +0000 Subject: 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 771c3ddefaf..cb3e43a18df 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) -- cgit v1.2.3-70-g09d2 From 9bfdc2b6dda45eb83bf0992d0cf98f3fd98163d4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 13 Dec 2012 14:28:32 +0000 Subject: 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 cb3e43a18df..fb2122cf7bf 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 @@ retry_bootloader: 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; } -- cgit v1.2.3-70-g09d2 From 69537daa9ceb1a0f6d347f75a1c2f4e5be294c84 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 16 Jan 2013 17:25:05 +0000 Subject: 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 fb2122cf7bf..7659dc96572 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include /* Firmware files */ #define MXT_FW_NAME "maxtouch.fw" @@ -152,6 +154,9 @@ struct t9_range { #define MXT_FW_RESET_TIME 3000 /* msec */ #define MXT_FW_CHG_TIMEOUT 300 /* msec */ #define MXT_WAKEUP_TIME 25 /* msec */ +#define MXT_REGULATOR_DELAY 150 /* msec */ +#define MXT_CHG_DELAY 100 /* msec */ +#define MXT_POWERON_DELAY 150 /* msec */ /* Command to unlock bootloader */ #define MXT_UNLOCK_CMD_MSB 0xaa @@ -218,6 +223,9 @@ struct mxt_data { u8 num_stylusids; unsigned long t15_keystatus; bool use_retrigen_workaround; + bool use_regulator; + struct regulator *reg_vdd; + struct regulator *reg_avdd; /* Cached parameters from object table */ u16 T5_address; @@ -1839,6 +1847,84 @@ static int mxt_read_t9_resolution(struct mxt_data *data) return 0; } +static void mxt_regulator_enable(struct mxt_data *data) +{ + int error; + + gpio_set_value(data->pdata->gpio_reset, 0); + + error = regulator_enable(data->reg_vdd); + if (error) + return; + + error = regulator_enable(data->reg_avdd); + if (error) + return; + + msleep(MXT_REGULATOR_DELAY); + gpio_set_value(data->pdata->gpio_reset, 1); + msleep(MXT_CHG_DELAY); + +retry_wait: + INIT_COMPLETION(data->bl_completion); + data->in_bootloader = true; + error = mxt_wait_for_completion(data, &data->bl_completion, + MXT_POWERON_DELAY); + if (error == -EINTR) + goto retry_wait; + + data->in_bootloader = false; +} + +static void mxt_regulator_disable(struct mxt_data *data) +{ + regulator_disable(data->reg_vdd); + regulator_disable(data->reg_avdd); +} + +static void mxt_probe_regulators(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + + /* + * According to maXTouch power sequencing specification, RESET line + * must be kept low until some time after regulators come up to + * voltage + */ + if (!data->pdata->gpio_reset) { + dev_warn(dev, "Must have reset GPIO to use regulator support\n"); + goto fail; + } + + data->reg_vdd = regulator_get(dev, "vdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting vdd regulator\n", error); + goto fail; + } + + data->reg_avdd = regulator_get(dev, "avdd"); + if (IS_ERR(data->reg_vdd)) { + error = PTR_ERR(data->reg_vdd); + dev_err(dev, "Error %d getting avdd regulator\n", error); + goto fail_release; + } + + data->use_regulator = true; + mxt_regulator_enable(data); + + dev_dbg(dev, "Initialised regulators\n"); + return; + +fail_release: + regulator_put(data->reg_vdd); +fail: + data->reg_vdd = NULL; + data->reg_avdd = NULL; + data->use_regulator = false; +} + static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; @@ -2036,6 +2122,9 @@ static int mxt_load_fw(struct device *dev, const char *fn) goto release_firmware; if (data->suspended) { + if (data->use_regulator) + mxt_regulator_enable(data); + enable_irq(data->irq); data->suspended = false; } @@ -2200,18 +2289,25 @@ static void mxt_start(struct mxt_data *data) if (!data->suspended || data->in_bootloader) return; - /* - * Discard any touch messages still in message buffer - * from before chip went to sleep - */ - mxt_process_messages_until_invalid(data); + if (data->use_regulator) { + enable_irq(data->irq); + + mxt_regulator_enable(data); + } else { + /* + * Discard any messages still in message buffer + * from before chip went to sleep + */ + mxt_process_messages_until_invalid(data); + + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + /* Recalibrate since chip has been in deep sleep */ + mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); - /* Recalibrate since chip has been in deep sleep */ - mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false); + mxt_acquire_irq(data); + } - mxt_acquire_irq(data); data->suspended = false; } @@ -2222,7 +2318,10 @@ static void mxt_stop(struct mxt_data *data) disable_irq(data->irq); - mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); + if (data->use_regulator) + mxt_regulator_disable(data); + else + mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP); mxt_reset_slots(data); data->suspended = true; @@ -2392,7 +2491,9 @@ static int mxt_probe(struct i2c_client *client, goto err_free_mem; } - disable_irq(client->irq); + mxt_probe_regulators(data); + + disable_irq(data->irq); error = mxt_initialize(data); if (error) @@ -2432,6 +2533,8 @@ static int mxt_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); free_irq(data->irq, data); input_unregister_device(data->input_dev); + regulator_put(data->reg_avdd); + regulator_put(data->reg_vdd); mxt_free_object_table(data); kfree(data); diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h index b7d20924eb7..a01f2e86c47 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 */ -- cgit v1.2.3-70-g09d2 From 28775ab8090b33ff8414bdf47bbb0e5502dd6c95 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 12 Feb 2013 17:07:56 +0000 Subject: 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 7659dc96572..19a07cd4031 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 @@ fail: 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 @@ retry_bootloader: 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); -- cgit v1.2.3-70-g09d2 From 9cd32cf7a7ef054525b81430b17f4f86208e540f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Mar 2013 14:59:17 +0000 Subject: 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 19a07cd4031..ffaf7bafe5d 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 @@ release_firmware: 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; -- cgit v1.2.3-70-g09d2 From 5a2726751c864efd234cb2f8b287c0eed52a9607 Mon Sep 17 00:00:00 2001 From: Yufeng Shen Date: Mon, 14 May 2012 12:06:29 -0400 Subject: 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 ffaf7bafe5d..70d826a129c 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; -- cgit v1.2.3-70-g09d2 From 833521d192380cf086f03b9f98720a620136167a Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 5 Mar 2013 15:57:05 +0000 Subject: 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 70d826a129c..0f724bba765 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 @@ err_free_mem: } 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 @@ retry_bootloader: 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 @@ retry_bootloader: 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 a01f2e86c47..3422bd0d847 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 */ -- cgit v1.2.3-70-g09d2 From 31ccad35f0aa946026679386f19b3d4369310178 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 11 Apr 2013 17:48:19 +0100 Subject: 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 0f724bba765..409657a8d9f 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; -- cgit v1.2.3-70-g09d2 From 8a7db572f0383da9c6d4ee379de6e38cfc2a1b92 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 1 Oct 2013 13:43:10 +0100 Subject: 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 409657a8d9f..9ad686d9a93 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 3422bd0d847..bc74c3f4c86 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 */ -- cgit v1.2.3-70-g09d2 From a615cb3a8ea64650e900d0513274165dbbfe55e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Aug 2011 16:40:30 +0100 Subject: 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 9ad686d9a93..3e828679685 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 @@ out: 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 }; -- cgit v1.2.3-70-g09d2 From 2b33db061113841c55d9d7060a683cb11eea93d8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 23 Aug 2011 16:37:50 +0100 Subject: 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 3e828679685..ed0de37ffec 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); -- cgit v1.2.3-70-g09d2 From 7720ba602703e2157a250e9af8548cdc4020fe9d Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 31 May 2013 11:22:58 +0100 Subject: 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 ed0de37ffec..3bf504647ae 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 @@ retry_bootloader: 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, -- cgit v1.2.3-70-g09d2 From 139b6e31f10291f5d2293376b63be45084baa792 Mon Sep 17 00:00:00 2001 From: Prajosh Premdas Date: Wed, 30 Apr 2014 19:41:43 +0200 Subject: 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 3bf504647ae..2311a1c0b30 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; } -- cgit v1.2.3-70-g09d2 From e4a37de4773aa80eea005916789dcde39b1b0f78 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 20 May 2014 10:54:57 +0100 Subject: 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 2311a1c0b30..89fead5c87c 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, -- cgit v1.2.3-70-g09d2 From f24ab04cb8f30aac0df69623fd4672bda5c0dbb1 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 20 May 2014 13:46:40 +0100 Subject: 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 89fead5c87c..7bf31d3c50d 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2386,9 +2386,7 @@ retry_bootloader: 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 -- cgit v1.2.3-70-g09d2 From cff90bffe860c38fd07016529eeb0ac2ca41b0a9 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:11:00 +0100 Subject: 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 7bf31d3c50d..ebffffc75af 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 @@ err_free_mem: 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 @@ err_remove_sysfs_group: 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, -- cgit v1.2.3-70-g09d2 From 0158865e2fe9f8a42899d749d49e7d769f799d5e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:24:23 +0100 Subject: 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 ebffffc75af..ec1554c7b05 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]); -- cgit v1.2.3-70-g09d2 From 1f6a0fcbc5db1f022912745002f4ecf4d0a93087 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:27:51 +0100 Subject: 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 ec1554c7b05..6f21f36b771 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 @@ update_count: 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) -- cgit v1.2.3-70-g09d2 From 70d553be3cfa55f955bf06ec99910f9ce5194f55 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:57:31 +0100 Subject: 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 6f21f36b771..d0fae80c685 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); -- cgit v1.2.3-70-g09d2 From 8765349094c5fef3696e1e10fbb6639d727cf1c4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:58:42 +0100 Subject: 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 d0fae80c685..88437f3985b 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; } -- cgit v1.2.3-70-g09d2 From 0dacc744606663d47f191ef1b153f575512105ba Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 23 May 2014 15:59:46 +0100 Subject: 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 88437f3985b..46eaa978dee 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++) { -- cgit v1.2.3-70-g09d2 From 5a4c198d9241025068b2704d7e85ac3c55b9c9e4 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 10:06:04 +0100 Subject: Add documentation for device tree Signed-off-by: Nick Dyer --- .../devicetree/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 00000000000..60d63391a6a --- /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 = ; + }; -- cgit v1.2.3-70-g09d2 From 4eedb0f2c8c5aae878eb9a862c7a83c0238a4177 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 11:48:32 +0100 Subject: 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 46eaa978dee..45eafb650e4 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 */ -- cgit v1.2.3-70-g09d2 From 915c13589f0df45e4e2a85675288dd5a7efb024b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 5 Jun 2014 12:14:31 +0100 Subject: 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 45eafb650e4..05c83398f9f 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 @@ err_free_mem: } 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 @@ retry_bootloader: 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; -- cgit v1.2.3-70-g09d2 From aee662e22e75169aac6a3fd4dfbb622cd8ec69e8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:41:40 +0100 Subject: 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 05c83398f9f..1df6547bf7a 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2400,15 +2400,15 @@ retry_bootloader: 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 @@ retry_bootloader: } 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 */ -- cgit v1.2.3-70-g09d2 From 347dff20141e8516cb048ed002f9e9c4cb68e493 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:29:26 +0100 Subject: 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 1df6547bf7a..be849456f2d 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; } } -- cgit v1.2.3-70-g09d2 From fcfae57a9c49ca5f355dfbc8dd03b00267772bdd Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:30:46 +0100 Subject: 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 be849456f2d..1af0957aa27 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -3131,32 +3131,43 @@ err_free_mem: 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; -- cgit v1.2.3-70-g09d2 From d0c1ce6f8c4c7e838cb666c6bd12550dd267456f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:38:02 +0100 Subject: 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 1af0957aa27..48c288ca4a6 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; -- cgit v1.2.3-70-g09d2 From c70631bf74326ca7c81129480e668431f1f6edf0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:39:28 +0100 Subject: 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 48c288ca4a6..226ad28c492 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2063,54 +2063,6 @@ err_free_mem: 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 @@ fail: 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 @@ err_free_mem: 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) { -- cgit v1.2.3-70-g09d2 From 18596d7290ee9f0d4992369e2111c675c96ccc3f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:39:50 +0100 Subject: 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 226ad28c492..c0ea59b2109 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; } -- cgit v1.2.3-70-g09d2 From c438f1cf18fc31a8db4d5868ee3237f8bcc709a0 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:40:12 +0100 Subject: 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 c0ea59b2109..193a9cc26ff 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; -- cgit v1.2.3-70-g09d2 From 4e8a1ab88d6ada42d8872276ee482e0b5fafe699 Mon Sep 17 00:00:00 2001 From: karl tsou Date: Thu, 19 Jun 2014 00:42:34 +0100 Subject: 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 193a9cc26ff..2834ec14d11 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2583,6 +2583,14 @@ err_free_object_table: 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 }; -- cgit v1.2.3-70-g09d2 From 04280cab6152c082f3023e04771d3a679f6b930e Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:45:33 +0100 Subject: 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 2834ec14d11..1dcf71ed6c4 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 @@ err_free_object: 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; -- cgit v1.2.3-70-g09d2 From 8d5ef3bad3a824fa8fa4e3fd5359b6e809985e9b Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 19 Jun 2014 00:46:06 +0100 Subject: 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 1dcf71ed6c4..dc705abe29f 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, -- cgit v1.2.3-70-g09d2 From 55d857bbff398cef56c56c66c07098e23a50a43c Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 20 Jun 2014 10:30:13 +0100 Subject: 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 dc705abe29f..fc3bc386d97 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, -- cgit v1.2.3-70-g09d2 From 83040d95bb5633799bd7fa2148958ffa6196d4b8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 3 Jul 2014 11:11:20 +0100 Subject: Input: atmel_mxt_ts - leave platform to specify irqflags when using DT The function to check the interrupt configuration must use irq_get_trigger_type() to allow for this case. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index fc3bc386d97..ae8e8ddebfc 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1450,10 +1451,12 @@ static u32 mxt_calculate_crc(u8 *base, off_t start_off, off_t end_off) static int mxt_check_retrigen(struct mxt_data *data) { struct i2c_client *client = data->client; + struct irq_data *irqd; int error; int val; - if (data->pdata->irqflags & IRQF_TRIGGER_LOW) + irqd = irq_get_irq_data(data->irq); + if (irqd_get_trigger_type(irqd) & IRQF_TRIGGER_LOW) return 0; if (data->T18_address) { @@ -3155,9 +3158,6 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) pdata->gpio_reset = of_get_named_gpio_flags(dev->of_node, "atmel,reset-gpio", 0, NULL); - /* Default to this. Properties can be added to configure it later */ - pdata->irqflags = IRQF_TRIGGER_FALLING; - of_property_read_string(dev->of_node, "atmel,cfg_name", &pdata->cfg_name); -- cgit v1.2.3-70-g09d2 From fb52903a15011123344b69ba4c2d299e59189742 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 4 Jul 2014 12:35:57 +0100 Subject: 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 ae8e8ddebfc..501f1aab8ab 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2035,7 +2035,7 @@ static int mxt_read_info_block(struct mxt_data *data) if ((data->info_crc == 0) || (data->info_crc != calculated_crc)) { dev_err(&client->dev, "Info Block CRC error calculated=0x%06X read=0x%06X\n", - data->info_crc, calculated_crc); + calculated_crc, data->info_crc); error = -EIO; goto err_free_mem; } -- cgit v1.2.3-70-g09d2 From 7e2585063bed90ef3239da54098a9004d779626f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Wed, 23 Jul 2014 15:53:33 +0100 Subject: 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 60d63391a6a..6319f07bfbc 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 = ; }; -- cgit v1.2.3-70-g09d2 From cf896abf5d0bc0032538b9b2c787a8d3c5afeff8 Mon Sep 17 00:00:00 2001 From: Janus Cheng Date: Sun, 20 Jul 2014 18:36:14 +0800 Subject: 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 501f1aab8ab..dd5164f48c5 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) -- cgit v1.2.3-70-g09d2 From 49c0c24735bf54b26de2c23a933b99090313bbc4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Jul 2014 14:50:56 -0700 Subject: 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 dd5164f48c5..48d37a742c2 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -569,6 +569,7 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) case 0x5b: bootloader = appmode - 0x26; break; + default: dev_err(&data->client->dev, "Appmode i2c address 0x%02x not found\n", @@ -580,20 +581,20 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry) return 0; } -static int mxt_probe_bootloader(struct mxt_data *data, bool retry) +static int mxt_probe_bootloader(struct mxt_data *data, bool alt_address) { struct device *dev = &data->client->dev; - int ret; + int error; u8 val; bool crc_failure; - ret = mxt_lookup_bootloader_address(data, retry); - if (ret) - return ret; + error = mxt_lookup_bootloader_address(data, alt_address); + if (error) + return error; - ret = mxt_bootloader_read(data, &val, 1); - if (ret) - return ret; + error = mxt_bootloader_read(data, &val, 1); + if (error) + return error; /* Check app crc fail mode */ crc_failure = (val & ~MXT_BOOT_STATUS_MASK) == MXT_APP_CRC_FAIL; @@ -2485,41 +2486,39 @@ static void mxt_config_cb(const struct firmware *cfg, void *ctx) static int mxt_initialize(struct mxt_data *data) { struct i2c_client *client = data->client; + int recovery_attempts = 0; int error; - bool alt_bootloader_addr = false; - bool retry = false; -retry_info: - error = mxt_read_info_block(data); - if (error) { -retry_bootloader: - error = mxt_probe_bootloader(data, alt_bootloader_addr); + while (1) { + error = mxt_read_info_block(data); + if (!error) + break; + + /* Check bootloader state */ + error = mxt_probe_bootloader(data, false); if (error) { - if (alt_bootloader_addr) { + dev_info(&client->dev, "Trying alternate bootloader address\n"); + error = mxt_probe_bootloader(data, true); + if (error) { /* Chip is not in appmode or bootloader mode */ return error; } + } - dev_info(&client->dev, "Trying alternate bootloader address\n"); - alt_bootloader_addr = true; - goto retry_bootloader; - } else { - if (retry) { - dev_err(&client->dev, "Could not recover from bootloader mode\n"); - /* - * We can reflash from this state, so do not - * abort init - */ - data->in_bootloader = true; - return 0; - } - - /* Attempt to exit bootloader into app mode */ - mxt_send_bootloader_cmd(data, false); - msleep(MXT_FW_RESET_TIME); - retry = true; - goto retry_info; + /* OK, we are in bootloader, see if we can recover */ + if (++recovery_attempts > 1) { + dev_err(&client->dev, "Could not recover from bootloader mode\n"); + /* + * We can reflash from this state, so do not + * abort initialization. + */ + data->in_bootloader = true; + return 0; } + + /* Attempt to exit bootloader into app mode */ + mxt_send_bootloader_cmd(data, false); + msleep(MXT_FW_RESET_TIME); } error = mxt_check_retrigen(data); -- cgit v1.2.3-70-g09d2 From 8715dc29130d6c5502dd3044203086bb12afdbd3 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:21:48 +0100 Subject: 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 48d37a742c2..b3985b1e09f 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -225,7 +225,7 @@ struct mxt_data { struct i2c_client *client; struct input_dev *input_dev; char phys[64]; /* device physical location */ - struct mxt_platform_data *pdata; + const struct mxt_platform_data *pdata; struct mxt_object *object_table; struct mxt_info *info; void *raw_info_block; @@ -3146,56 +3146,79 @@ static void mxt_input_close(struct input_dev *dev) static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) { struct mxt_platform_data *pdata; - struct device *dev = &client->dev; - struct property *prop; - unsigned int *keymap; + u32 *keymap; int proplen, ret; - pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!client->dev.of_node) + return ERR_PTR(-ENODEV); + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - return NULL; + return ERR_PTR(-ENOMEM); /* reset gpio */ - pdata->gpio_reset = of_get_named_gpio_flags(dev->of_node, + pdata->gpio_reset = of_get_named_gpio_flags(client->dev.of_node, "atmel,reset-gpio", 0, NULL); - of_property_read_string(dev->of_node, "atmel,cfg_name", + of_property_read_string(client->dev.of_node, "atmel,cfg_name", &pdata->cfg_name); - of_property_read_string(dev->of_node, "atmel,input_name", + of_property_read_string(client->dev.of_node, "atmel,input_name", &pdata->input_name); - prop = of_find_property(dev->of_node, "linux,gpio-keymap", &proplen); - if (prop) { + if (of_find_property(client->dev.of_node, "linux,gpio-keymap", + &proplen)) { pdata->t19_num_keys = proplen / sizeof(u32); - keymap = devm_kzalloc(dev, - pdata->t19_num_keys * sizeof(u32), GFP_KERNEL); + keymap = devm_kzalloc(&client->dev, + pdata->t19_num_keys * sizeof(keymap[0]), + GFP_KERNEL); if (!keymap) - return NULL; - - pdata->t19_keymap = keymap; + return ERR_PTR(-ENOMEM); ret = of_property_read_u32_array(client->dev.of_node, "linux,gpio-keymap", keymap, pdata->t19_num_keys); if (ret) { - dev_err(dev, + dev_err(&client->dev, "Unable to read device tree key codes: %d\n", ret); return NULL; } + + pdata->t19_keymap = keymap; } return pdata; } +#else +static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client) +{ + struct mxt_platform_data *pdata; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + /* Set default parameters */ + pdata->irqflags = IRQF_TRIGGER_FALLING; + + return pdata; +} #endif -static int mxt_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct mxt_data *data; + const struct mxt_platform_data *pdata; int error; + pdata = dev_get_platdata(&client->dev); + if (!pdata) { + pdata = mxt_parse_dt(client); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL); if (!data) { dev_err(&client->dev, "Failed to allocate memory\n"); @@ -3206,28 +3229,10 @@ static int mxt_probe(struct i2c_client *client, client->adapter->nr, client->addr); data->client = client; + data->pdata = pdata; data->irq = client->irq; - data->pdata = dev_get_platdata(&client->dev); i2c_set_clientdata(client, data); -#ifdef CONFIG_OF - if (!data->pdata && client->dev.of_node) - data->pdata = mxt_parse_dt(client); -#endif - - if (!data->pdata) { - data->pdata = devm_kzalloc(&client->dev, sizeof(*data->pdata), - GFP_KERNEL); - if (!data->pdata) { - dev_err(&client->dev, "Failed to allocate pdata\n"); - error = -ENOMEM; - goto err_free_mem; - } - - /* Set default parameters */ - data->pdata->irqflags = IRQF_TRIGGER_FALLING; - } - if (data->pdata->cfg_name) mxt_update_file_name(&data->client->dev, &data->cfg_name, @@ -3240,7 +3245,7 @@ static int mxt_probe(struct i2c_client *client, mutex_init(&data->debug_msg_lock); error = request_threaded_irq(client->irq, NULL, mxt_interrupt, - data->pdata->irqflags | IRQF_ONESHOT, + pdata->irqflags | IRQF_ONESHOT, client->name, data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); -- cgit v1.2.3-70-g09d2 From 97396a078d4a12421860f39be5fa9f6a20bf306f Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:22:25 +0100 Subject: 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 b3985b1e09f..a5570f58833 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2534,9 +2534,14 @@ static int mxt_initialize(struct mxt_data *data) goto err_free_object_table; if (data->cfg_name) { - request_firmware_nowait(THIS_MODULE, true, data->cfg_name, - &data->client->dev, GFP_KERNEL, data, - mxt_config_cb); + error = request_firmware_nowait(THIS_MODULE, true, + data->cfg_name, &data->client->dev, + GFP_KERNEL, data, mxt_config_cb); + if (error) { + dev_err(&client->dev, "Failed to invoke firmware loader: %d\n", + error); + goto err_free_object_table; + } } else { error = mxt_configure_objects(data, NULL); if (error) -- cgit v1.2.3-70-g09d2 From d59050a1db6cfc99f6f8e24e91279b18f9950788 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 23 Jul 2014 16:29:55 -0700 Subject: 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 a5570f58833..115bf7a7ef9 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1478,6 +1478,133 @@ static int mxt_check_retrigen(struct mxt_data *data) return 0; } +static int mxt_prepare_cfg_mem(struct mxt_data *data, + const struct firmware *cfg, + unsigned int data_pos, + unsigned int cfg_start_ofs, + u8 *config_mem, + size_t config_mem_size) +{ + struct device *dev = &data->client->dev; + struct mxt_object *object; + unsigned int type, instance, size, byte_offset; + int offset; + int ret; + int i; + u16 reg; + u8 val; + + while (data_pos < cfg->size) { + /* Read type, instance, length */ + ret = sscanf(cfg->data + data_pos, "%x %x %x%n", + &type, &instance, &size, &offset); + if (ret == 0) { + /* EOF */ + break; + } else if (ret != 3) { + dev_err(dev, "Bad format: failed to parse object\n"); + return -EINVAL; + } + data_pos += offset; + + object = mxt_get_object(data, type); + if (!object) { + /* Skip object */ + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + data_pos += offset; + } + continue; + } + + if (size > mxt_obj_size(object)) { + /* + * Either we are in fallback mode due to wrong + * config or config from a later fw version, + * or the file is corrupt or hand-edited. + */ + dev_warn(dev, "Discarding %zu byte(s) in T%u\n", + size - mxt_obj_size(object), type); + } else if (mxt_obj_size(object) > size) { + /* + * If firmware is upgraded, new bytes may be added to + * end of objects. It is generally forward compatible + * to zero these bytes - previous behaviour will be + * retained. However this does invalidate the CRC and + * will force fallback mode until the configuration is + * updated. We warn here but do nothing else - the + * malloc has zeroed the entire configuration. + */ + dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", + mxt_obj_size(object) - size, type); + } + + if (instance >= mxt_obj_instances(object)) { + dev_err(dev, "Object instances exceeded!\n"); + return -EINVAL; + } + + reg = object->start_address + mxt_obj_size(object) * instance; + + for (i = 0; i < size; i++) { + ret = sscanf(cfg->data + data_pos, "%hhx%n", + &val, + &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d\n", type); + return -EINVAL; + } + data_pos += offset; + + if (i > mxt_obj_size(object)) + continue; + + byte_offset = reg + i - cfg_start_ofs; + + if (byte_offset >= 0 && + byte_offset <= config_mem_size) { + *(config_mem + byte_offset) = val; + } else { + dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", + reg, object->type, byte_offset); + return -EINVAL; + } + } + } + + return 0; +} + +static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start, + u8 *config_mem, size_t config_mem_size) +{ + unsigned int byte_offset = 0; + int error; + + /* Write configuration as blocks */ + while (byte_offset < config_mem_size) { + unsigned int size = config_mem_size - byte_offset; + + if (size > MXT_MAX_BLOCK_WRITE) + size = MXT_MAX_BLOCK_WRITE; + + error = __mxt_write_reg(data->client, + cfg_start + byte_offset, + size, config_mem + byte_offset); + if (error) { + dev_err(&data->client->dev, + "Config write error, ret=%d\n", error); + return error; + } + + byte_offset += size; + } + + return 0; +} + static int mxt_init_t7_power_cfg(struct mxt_data *data); /* @@ -1503,26 +1630,20 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) { struct device *dev = &data->client->dev; struct mxt_info cfg_info; - struct mxt_object *object; int ret; int offset; int data_pos; - int byte_offset; int i; int cfg_start_ofs; u32 info_crc, config_crc, calculated_crc; u8 *config_mem; size_t config_mem_size; - unsigned int type, instance, size; - u8 val; - u16 reg; mxt_update_crc(data, MXT_COMMAND_REPORTALL, 1); if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) { dev_err(dev, "Unrecognised config file\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos = strlen(MXT_CFG_MAGIC); @@ -1534,8 +1655,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) &offset); if (ret != 1) { dev_err(dev, "Bad format\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos += offset; @@ -1543,30 +1663,26 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) if (cfg_info.family_id != data->info->family_id) { dev_err(dev, "Family ID mismatch!\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } if (cfg_info.variant_id != data->info->variant_id) { dev_err(dev, "Variant ID mismatch!\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } /* Read CRCs */ ret = sscanf(cfg->data + data_pos, "%x%n", &info_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Info CRC\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos += offset; ret = sscanf(cfg->data + data_pos, "%x%n", &config_crc, &offset); if (ret != 1) { dev_err(dev, "Bad format: failed to parse Config CRC\n"); - ret = -EINVAL; - goto release; + return -EINVAL; } data_pos += offset; @@ -1582,8 +1698,7 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) } else if (config_crc == data->config_crc) { dev_dbg(dev, "Config CRC 0x%06X: OK\n", data->config_crc); - ret = 0; - goto release; + return 0; } else { dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n", data->config_crc, config_crc); @@ -1602,93 +1717,13 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) config_mem = kzalloc(config_mem_size, GFP_KERNEL); if (!config_mem) { dev_err(dev, "Failed to allocate memory\n"); - ret = -ENOMEM; - goto release; + return -ENOMEM; } - while (data_pos < cfg->size) { - /* Read type, instance, length */ - ret = sscanf(cfg->data + data_pos, "%x %x %x%n", - &type, &instance, &size, &offset); - if (ret == 0) { - /* EOF */ - break; - } else if (ret != 3) { - dev_err(dev, "Bad format: failed to parse object\n"); - ret = -EINVAL; - goto release_mem; - } - data_pos += offset; - - object = mxt_get_object(data, type); - if (!object) { - /* Skip object */ - for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", - &val, - &offset); - data_pos += offset; - } - continue; - } - - if (size > mxt_obj_size(object)) { - /* - * Either we are in fallback mode due to wrong - * config or config from a later fw version, - * or the file is corrupt or hand-edited. - */ - dev_warn(dev, "Discarding %zu byte(s) in T%u\n", - size - mxt_obj_size(object), type); - } else if (mxt_obj_size(object) > size) { - /* - * If firmware is upgraded, new bytes may be added to - * end of objects. It is generally forward compatible - * to zero these bytes - previous behaviour will be - * retained. However this does invalidate the CRC and - * will force fallback mode until the configuration is - * updated. We warn here but do nothing else - the - * malloc has zeroed the entire configuration. - */ - dev_warn(dev, "Zeroing %zu byte(s) in T%d\n", - mxt_obj_size(object) - size, type); - } - - if (instance >= mxt_obj_instances(object)) { - dev_err(dev, "Object instances exceeded!\n"); - ret = -EINVAL; - goto release_mem; - } - - reg = object->start_address + mxt_obj_size(object) * instance; - - for (i = 0; i < size; i++) { - ret = sscanf(cfg->data + data_pos, "%hhx%n", - &val, - &offset); - if (ret != 1) { - dev_err(dev, "Bad format in T%d\n", type); - ret = -EINVAL; - goto release_mem; - } - data_pos += offset; - - if (i > mxt_obj_size(object)) - continue; - - byte_offset = reg + i - cfg_start_ofs; - - if ((byte_offset >= 0) - && (byte_offset <= config_mem_size)) { - *(config_mem + byte_offset) = val; - } else { - dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", - reg, object->type, byte_offset); - ret = -EINVAL; - goto release_mem; - } - } - } + ret = mxt_prepare_cfg_mem(data, cfg, data_pos, cfg_start_ofs, + config_mem, config_mem_size); + if (ret) + goto release_mem; /* Calculate crc of the received configs (not the raw config file) */ if (data->T7_address < cfg_start_ofs) { @@ -1702,28 +1737,14 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) data->T7_address - cfg_start_ofs, config_mem_size); - if (config_crc > 0 && (config_crc != calculated_crc)) + if (config_crc > 0 && config_crc != calculated_crc) dev_warn(dev, "Config CRC error, calculated=%06X, file=%06X\n", calculated_crc, config_crc); - /* Write configuration as blocks */ - byte_offset = 0; - while (byte_offset < config_mem_size) { - size = config_mem_size - byte_offset; - - if (size > MXT_MAX_BLOCK_WRITE) - size = MXT_MAX_BLOCK_WRITE; - - ret = __mxt_write_reg(data->client, - cfg_start_ofs + byte_offset, - size, config_mem + byte_offset); - if (ret != 0) { - dev_err(dev, "Config write error, ret=%d\n", ret); - goto release_mem; - } - - byte_offset += size; - } + ret = mxt_upload_cfg_mem(data, cfg_start_ofs, + config_mem, config_mem_size); + if (ret) + goto release_mem; mxt_update_crc(data, MXT_COMMAND_BACKUPNV, MXT_BACKUP_VALUE); @@ -1742,8 +1763,6 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg) release_mem: kfree(config_mem); -release: - release_firmware(cfg); return ret; } @@ -2481,6 +2500,7 @@ static int mxt_configure_objects(struct mxt_data *data, static void mxt_config_cb(const struct firmware *cfg, void *ctx) { mxt_configure_objects(ctx, cfg); + release_firmware(cfg); } static int mxt_initialize(struct mxt_data *data) @@ -2930,9 +2950,12 @@ static ssize_t mxt_update_cfg_store(struct device *dev, ret = mxt_configure_objects(data, cfg); if (ret) - goto out; + goto release; ret = count; + +release: + release_firmware(cfg); out: data->updating_config = false; return ret; -- cgit v1.2.3-70-g09d2 From 5d954376ff56f53521a74806d2a07184c9779955 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:24:02 +0100 Subject: 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 115bf7a7ef9..f3fec822206 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1144,7 +1144,7 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) mxt_proc_t48_messages(data, message); } else if (!data->input_dev || data->suspended) { /* - * do not report events if input device is not + * Do not report events if input device is not * yet registered or returning from suspend */ mxt_dump_message(data, message); -- cgit v1.2.3-70-g09d2 From 1a811c36e1469ecc22df412b1461741634ddbce7 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Thu, 24 Jul 2014 17:24:33 +0100 Subject: 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 f3fec822206..0d54b7d5b1f 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 { -- cgit v1.2.3-70-g09d2 From c94606f45b412a4194b126b981fdbbdd054ca770 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 10:13:55 +0100 Subject: 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 0d54b7d5b1f..c2620292ea9 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); -- cgit v1.2.3-70-g09d2 From 1b4c1df705a6277dc19587337ba58186a72019fc Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 11:42:55 +0100 Subject: 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 c2620292ea9..22f743d0416 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2980,9 +2980,9 @@ static ssize_t mxt_debug_v2_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mxt_data *data = dev_get_drvdata(dev); - int i; + u8 i; - if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (kstrtou8(buf, 0, &i) == 0 && i < 2) { if (i == 1) mxt_debug_msg_enable(data); else @@ -2999,9 +2999,9 @@ static ssize_t mxt_debug_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mxt_data *data = dev_get_drvdata(dev); - int i; + u8 i; - if (sscanf(buf, "%u", &i) == 1 && i < 2) { + if (kstrtou8(buf, 0, &i) == 0 && i < 2) { data->debug_enabled = (i == 1); dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); -- cgit v1.2.3-70-g09d2 From 01b103d42c1b7ebd7f3251683c7bbe201196faee Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Mon, 28 Jul 2014 16:22:55 +0100 Subject: 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 22f743d0416..5a09f55a7b6 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2616,6 +2616,7 @@ static ssize_t mxt_config_csum_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%06x\n", data->config_crc); } -- cgit v1.2.3-70-g09d2 From e3e0ef50605d2952befdcb064feb2db69fe8b753 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 29 Jul 2014 10:45:53 +0100 Subject: 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 5a09f55a7b6..33cbe2bae71 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -248,12 +248,12 @@ struct mxt_data { u32 config_crc; u32 info_crc; u8 bootloader_addr; - struct t7_config t7_cfg; u8 *msg_buf; u8 t6_status; bool update_input; u8 last_message_count; u8 num_touchids; + struct t7_config t7_cfg; u8 num_stylusids; unsigned long t15_keystatus; bool use_retrigen_workaround; @@ -1765,60 +1765,6 @@ release_mem: return ret; } -static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) -{ - struct device *dev = &data->client->dev; - int error; - struct t7_config *new_config; - struct t7_config deepsleep = { .active = 0, .idle = 0 }; - - if (sleep == MXT_POWER_CFG_DEEPSLEEP) - new_config = &deepsleep; - else - new_config = &data->t7_cfg; - - error = __mxt_write_reg(data->client, data->T7_address, - sizeof(data->t7_cfg), new_config); - if (error) - return error; - - dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", - new_config->active, new_config->idle); - - return 0; -} - -static int mxt_init_t7_power_cfg(struct mxt_data *data) -{ - struct device *dev = &data->client->dev; - int error; - bool retry = false; - -recheck: - error = __mxt_read_reg(data->client, data->T7_address, - sizeof(data->t7_cfg), &data->t7_cfg); - if (error) - return error; - - if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { - if (!retry) { - dev_dbg(dev, "T7 cfg zero, resetting\n"); - mxt_soft_reset(data); - retry = true; - goto recheck; - } else { - dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); - data->t7_cfg.active = 20; - data->t7_cfg.idle = 100; - return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); - } - } - - dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", - data->t7_cfg.active, data->t7_cfg.idle); - return 0; -} - static int mxt_acquire_irq(struct mxt_data *data) { int error; @@ -2574,6 +2520,60 @@ err_free_object_table: return error; } +static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep) +{ + struct device *dev = &data->client->dev; + int error; + struct t7_config *new_config; + struct t7_config deepsleep = { .active = 0, .idle = 0 }; + + if (sleep == MXT_POWER_CFG_DEEPSLEEP) + new_config = &deepsleep; + else + new_config = &data->t7_cfg; + + error = __mxt_write_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), new_config); + if (error) + return error; + + dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n", + new_config->active, new_config->idle); + + return 0; +} + +static int mxt_init_t7_power_cfg(struct mxt_data *data) +{ + struct device *dev = &data->client->dev; + int error; + bool retry = false; + +recheck: + error = __mxt_read_reg(data->client, data->T7_address, + sizeof(data->t7_cfg), &data->t7_cfg); + if (error) + return error; + + if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) { + if (!retry) { + dev_dbg(dev, "T7 cfg zero, resetting\n"); + mxt_soft_reset(data); + retry = true; + goto recheck; + } else { + dev_dbg(dev, "T7 cfg zero after reset, overriding\n"); + data->t7_cfg.active = 20; + data->t7_cfg.idle = 100; + return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN); + } + } + + dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n", + data->t7_cfg.active, data->t7_cfg.idle); + return 0; +} + static int mxt_configure_objects(struct mxt_data *data, const struct firmware *cfg) { @@ -3068,12 +3068,12 @@ static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL); static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL); static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store); static DEVICE_ATTR(update_cfg, S_IWUSR, NULL, mxt_update_cfg_store); +static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); +static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, + mxt_debug_enable_store); static DEVICE_ATTR(debug_v2_enable, S_IWUSR | S_IRUSR, NULL, mxt_debug_v2_enable_store); static DEVICE_ATTR(debug_notify, S_IRUGO, mxt_debug_notify_show, NULL); -static DEVICE_ATTR(debug_enable, S_IWUSR | S_IRUSR, mxt_debug_enable_show, - mxt_debug_enable_store); -static DEVICE_ATTR(config_csum, S_IRUGO, mxt_config_csum_show, NULL); static struct attribute *mxt_attrs[] = { &dev_attr_fw_version.attr, @@ -3081,10 +3081,10 @@ static struct attribute *mxt_attrs[] = { &dev_attr_object.attr, &dev_attr_update_fw.attr, &dev_attr_update_cfg.attr, + &dev_attr_config_csum.attr, &dev_attr_debug_enable.attr, &dev_attr_debug_v2_enable.attr, &dev_attr_debug_notify.attr, - &dev_attr_config_csum.attr, NULL }; -- cgit v1.2.3-70-g09d2 From 88858db093947cdc5bb5dee9f76694dfc79a5c73 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 29 Jul 2014 10:46:35 +0100 Subject: 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 33cbe2bae71..f4b4f351c49 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1856,10 +1856,12 @@ static int mxt_parse_object_table(struct mxt_data *data, switch (object->type) { case MXT_GEN_MESSAGE_T5: - if (data->info->family_id == 0x80) { + if (data->info->family_id == 0x80 && + data->info->version < 0x20) { /* - * On mXT224 read and discard unused CRC byte - * otherwise DMA reads are misaligned + * On mXT224 firmware versions prior to V2.0 + * read and discard unused CRC byte otherwise + * DMA reads are misaligned. */ data->T5_msg_size = mxt_obj_size(object); } else { -- cgit v1.2.3-70-g09d2 From b99f70b657dc10cf828ff020cf3904b35d9620f4 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 11 Aug 2014 11:03:09 -0700 Subject: 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 f4b4f351c49..8eebdfc92f1 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1511,8 +1511,12 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, /* Skip object */ for (i = 0; i < size; i++) { ret = sscanf(cfg->data + data_pos, "%hhx%n", - &val, - &offset); + &val, &offset); + if (ret != 1) { + dev_err(dev, "Bad format in T%d at %d\n", + type, i); + return -EINVAL; + } data_pos += offset; } continue; @@ -1552,7 +1556,8 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, &val, &offset); if (ret != 1) { - dev_err(dev, "Bad format in T%d\n", type); + dev_err(dev, "Bad format in T%d at %d\n", + type, i); return -EINVAL; } data_pos += offset; @@ -1562,8 +1567,7 @@ static int mxt_prepare_cfg_mem(struct mxt_data *data, byte_offset = reg + i - cfg_start_ofs; - if (byte_offset >= 0 && - byte_offset <= config_mem_size) { + if (byte_offset >= 0 && byte_offset < config_mem_size) { *(config_mem + byte_offset) = val; } else { dev_err(dev, "Bad object: reg:%d, T%d, ofs=%d\n", @@ -1869,6 +1873,7 @@ static int mxt_parse_object_table(struct mxt_data *data, data->T5_msg_size = mxt_obj_size(object) - 1; } data->T5_address = object->start_address; + break; case MXT_GEN_COMMAND_T6: data->T6_reportid = min_id; data->T6_address = object->start_address; -- cgit v1.2.3-70-g09d2 From 513bb8cadf695fe88bb3b101f2398ea2607192a8 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 2 Sep 2014 16:37:46 +0100 Subject: 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 8eebdfc92f1..93f1c3bf1c1 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); -- cgit v1.2.3-70-g09d2 From 64c2e0ff9b77ea3fc47489f8d152b472031c32af Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:12:04 +0100 Subject: 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 93f1c3bf1c1..f16e5a845f4 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); -- cgit v1.2.3-70-g09d2 From 29c8ab9dc45ef3bc5cda7fad055121fd421e6a54 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:13:30 +0100 Subject: 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 f16e5a845f4..4df1f0e7d45 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2992,6 +2992,7 @@ static ssize_t mxt_debug_v2_enable_store(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); u8 i; + ssize_t ret; if (kstrtou8(buf, 0, &i) == 0 && i < 2) { if (i == 1) @@ -2999,11 +3000,13 @@ static ssize_t mxt_debug_v2_enable_store(struct device *dev, else mxt_debug_msg_disable(data); - return count; + ret = count; } else { dev_dbg(dev, "debug_enabled write error\n"); - return -EINVAL; + ret = -EINVAL; } + + return ret; } static ssize_t mxt_debug_enable_store(struct device *dev, @@ -3011,16 +3014,19 @@ static ssize_t mxt_debug_enable_store(struct device *dev, { struct mxt_data *data = dev_get_drvdata(dev); u8 i; + ssize_t ret; if (kstrtou8(buf, 0, &i) == 0 && i < 2) { data->debug_enabled = (i == 1); dev_dbg(dev, "%s\n", i ? "debug enabled" : "debug disabled"); - return count; + ret = count; } else { dev_dbg(dev, "debug_enabled write error\n"); - return -EINVAL; + ret = -EINVAL; } + + return ret; } static int mxt_check_mem_access_params(struct mxt_data *data, loff_t off, -- cgit v1.2.3-70-g09d2 From b96fee16f8fcdbc54cdecbac4769bc6d43f3c322 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Tue, 9 Sep 2014 16:14:06 +0100 Subject: 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 4df1f0e7d45..4cb916cf6ed 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -2635,6 +2635,10 @@ static ssize_t mxt_fw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); + + if (!data->object_table) + return -EINVAL; + return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n", data->info->version >> 4, data->info->version & 0xf, data->info->build); @@ -2645,6 +2649,10 @@ static ssize_t mxt_hw_version_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mxt_data *data = dev_get_drvdata(dev); + + if (!data->object_table) + return -EINVAL; + return scnprintf(buf, PAGE_SIZE, "%u.%u\n", data->info->family_id, data->info->variant_id); } @@ -2677,6 +2685,9 @@ static ssize_t mxt_object_show(struct device *dev, int error; u8 *obuf; + if (!data->object_table) + return -EINVAL; + /* Pre-allocate buffer large enough to hold max sized object. */ obuf = kmalloc(256, GFP_KERNEL); if (!obuf) -- cgit v1.2.3-70-g09d2 From 4f1ce8cfd495b6b4ec983c727d1910c0ce26b8ed Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 9 Sep 2014 16:15:24 +0100 Subject: 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 4cb916cf6ed..b948f99a230 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1798,7 +1798,6 @@ static void mxt_free_input_device(struct mxt_data *data) static void mxt_free_object_table(struct mxt_data *data) { mxt_debug_msg_remove(data); - mxt_free_input_device(data); data->object_table = NULL; data->info = NULL; @@ -2788,11 +2787,13 @@ static int mxt_load_fw(struct device *dev) ret = mxt_lookup_bootloader_address(data, 0); if (ret) goto release_firmware; + + mxt_free_input_device(data); + mxt_free_object_table(data); } else { enable_irq(data->irq); } - mxt_free_object_table(data); INIT_COMPLETION(data->bl_completion); ret = mxt_check_bootloader(data, MXT_WAITING_BOOTLOAD_CMD, false); @@ -3311,15 +3312,11 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) disable_irq(data->irq); - error = mxt_initialize(data); - if (error) - goto err_free_irq; - error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group); if (error) { dev_err(&client->dev, "Failure %d creating sysfs group\n", error); - goto err_free_object; + goto err_free_irq; } sysfs_bin_attr_init(&data->mem_access_attr); @@ -3336,12 +3333,17 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id) goto err_remove_sysfs_group; } + error = mxt_initialize(data); + if (error) + goto err_remove_mem_access; + return 0; +err_remove_mem_access: + sysfs_remove_bin_file(&client->dev.kobj, &data->mem_access_attr); + data->mem_access_attr.attr.name = NULL; err_remove_sysfs_group: sysfs_remove_group(&client->dev.kobj, &mxt_attr_group); -err_free_object: - mxt_free_object_table(data); err_free_irq: free_irq(client->irq, data); err_free_mem: @@ -3361,6 +3363,7 @@ static int mxt_remove(struct i2c_client *client) free_irq(data->irq, data); regulator_put(data->reg_avdd); regulator_put(data->reg_vdd); + mxt_free_input_device(data); mxt_free_object_table(data); kfree(data); -- cgit v1.2.3-70-g09d2 From 15fd4c4259d19d0005d9cccb6ab2f85fbd4d6a02 Mon Sep 17 00:00:00 2001 From: Nick Dyer Date: Fri, 24 Oct 2014 10:08:19 +0100 Subject: Adjust MXT_MAX_BLOCK_WRITE Some platforms appear to have an underlying I2C device which starts corrupting data if blocks exceed this length. Signed-off-by: Nick Dyer --- drivers/input/touchscreen/atmel_mxt_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index b948f99a230..c4249f8b5ba 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -40,7 +40,7 @@ #define MXT_OBJECT_START 0x07 #define MXT_OBJECT_SIZE 6 #define MXT_INFO_CHECKSUM_SIZE 3 -#define MXT_MAX_BLOCK_WRITE 256 +#define MXT_MAX_BLOCK_WRITE 255 /* Object types */ #define MXT_DEBUG_DIAGNOSTIC_T37 37 -- cgit v1.2.3-70-g09d2