2013-06-27 12:50:36

by Nick Dyer

[permalink] [raw]
Subject: Atmel updates to atmel_mxt_ts touch controller driver - v6

Some minor updates to these patches to address review comments. I've rebased
them on the latest dtor/next tree.

Input: atmel_mxt_ts - Verify Information Block checksum on probe
- Fix bug which might cause erroneous debug output due to use of krealloc (thanks to
Yufeng Shen for spotting this)

Input: atmel_mxt_ts - Implement T63 Active Stylus support
- Add T63 to #defines since there will be other active stylus objects

Input: atmel_mxt_ts - Implement debug output for messages
Input: atmel_mxt_ts - Add memory access interface via sysfs
- remove from series (move mem_size calculation)

--
Nick Dyer Senior Software Engineer
ITDev Hardware and Software Development Consultancy http://www.itdev.co.uk


2013-06-27 12:50:38

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 03/51] Input: atmel_mxt_ts - Return IRQ_NONE when interrupt handler fails

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 6702175..2a06657 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -591,7 +591,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;
@@ -618,7 +618,6 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
input_sync(data->input_dev);
}

-end:
return IRQ_HANDLED;
}

--
1.7.10.4

2013-06-27 12:51:02

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 50/51] 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 <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 104 ++++++++++++++++++++++++------
include/linux/i2c/atmel_mxt_ts.h | 1 +
2 files changed, 86 insertions(+), 19 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 9aa3a26..9a5ac2c 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -28,7 +28,6 @@
#include <linux/gpio.h>

/* Configuration file */
-#define MXT_CFG_NAME "maxtouch.cfg"
#define MXT_CFG_MAGIC "OBP_RAW V1"

/* Registers */
@@ -246,6 +245,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;
@@ -1372,10 +1372,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;
}

@@ -1673,6 +1678,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);
@@ -1682,10 +1695,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->enable_reporting = false;
data->T5_address = 0;
@@ -2181,6 +2191,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)
{
@@ -2224,16 +2235,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, "Failed to initialize power cfg\n");
- goto err_free_object_table;
+ return error;
}

/* Check register init values */
@@ -2241,28 +2264,23 @@ 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");
}

data->enable_reporting = true;
-
return 0;
-
-err_free_object_table:
- mxt_free_object_table(data);
- return error;
}

/* Firmware Version is returned as Major.Minor.Build */
@@ -2550,16 +2568,57 @@ 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->enable_reporting = false;
+ mxt_free_input_device(data);
+
+ if (data->suspended) {
+ if (data->use_regulator)
+ 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:
+ 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
};

@@ -2644,8 +2703,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 a01f2e8..3422bd0 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 */
--
1.7.10.4

2013-06-27 12:51:00

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 51/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 9a5ac2c..7380a40 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1770,10 +1770,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;
@@ -1796,10 +1797,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;
--
1.7.10.4

2013-06-27 12:50:58

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 49/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 cb6fe8f..9aa3a26 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -27,8 +27,7 @@
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>

-/* Firmware files */
-#define MXT_FW_NAME "maxtouch.fw"
+/* Configuration file */
#define MXT_CFG_NAME "maxtouch.cfg"
#define MXT_CFG_MAGIC "OBP_RAW V1"

@@ -246,6 +245,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;
@@ -2366,7 +2366,7 @@ static int mxt_check_firmware_format(struct device *dev,
return -1;
}

-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;
@@ -2376,9 +2376,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;
}

@@ -2493,6 +2493,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)
@@ -2500,7 +2529,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;
--
1.7.10.4

2013-06-27 12:50:55

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 44/51] Input: atmel_mxt_ts - Handle reports from T47 Stylus object

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ee39683..ceb090a 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)
@@ -696,6 +699,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
int area;
int amplitude;
u8 vector;
+ int tool;

/* do not report events if input device not yet registered */
if (!data->enable_reporting)
@@ -713,6 +717,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
y >>= 2;

area = message[5];
+
amplitude = message[6];
vector = message[7];

@@ -741,8 +746,17 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
mxt_input_sync(input_dev);
}

+ /* A reported size of zero indicates that the reported touch
+ * is a stylus from a linked Stylus T47 object. */
+ 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);
--
1.7.10.4

2013-06-27 12:51:48

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 48/51] 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 <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 299 ++++++++++++++++++++++++++++--
1 file changed, 288 insertions(+), 11 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index b75cf38..cb6fe8f 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 */
@@ -209,6 +227,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;
@@ -244,6 +265,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;
@@ -780,6 +803,79 @@ 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;
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ 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) {
+ /* A reported size of zero indicates that the reported touch
+ * is a stylus from a linked Stylus T47 object. */
+ 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;
@@ -916,6 +1012,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;
@@ -1582,6 +1681,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->enable_reporting = false;
data->T5_address = 0;
data->T5_msg_size = 0;
@@ -1599,6 +1704,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;
}

@@ -1684,6 +1791,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
@@ -1907,6 +2020,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;
@@ -1969,6 +2244,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");
+ }
+
data->enable_reporting = true;

return 0;
@@ -2493,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:
@@ -2529,7 +2807,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);
--
1.7.10.4

2013-06-27 12:52:27

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 47/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 95 +++++++++++++++++++++++++++---
include/linux/i2c/atmel_mxt_ts.h | 1 +
2 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 5ab3861..b75cf38 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -24,6 +24,8 @@
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>

/* Firmware files */
#define MXT_FW_NAME "maxtouch.fw"
@@ -152,6 +154,8 @@ 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_POWERON_DELAY 150 /* msec */

/* Command to unlock bootloader */
#define MXT_UNLOCK_CMD_MSB 0xaa
@@ -218,6 +222,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;
@@ -1840,6 +1847,66 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
return 0;
}

+static void mxt_regulator_enable(struct mxt_data *data)
+{
+ gpio_set_value(data->pdata->gpio_reset, 0);
+
+ regulator_enable(data->reg_vdd);
+ regulator_enable(data->reg_avdd);
+ msleep(MXT_REGULATOR_DELAY);
+
+ INIT_COMPLETION(data->bl_completion);
+ gpio_set_value(data->pdata->gpio_reset, 1);
+ mxt_wait_for_completion(data, &data->bl_completion, MXT_POWERON_DELAY);
+}
+
+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;
@@ -2034,6 +2101,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;
}
@@ -2198,14 +2268,18 @@ 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) {
+ 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);
data->enable_reporting = true;
@@ -2220,7 +2294,10 @@ static void mxt_stop(struct mxt_data *data)
data->enable_reporting = false;
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;
@@ -2408,6 +2485,8 @@ static int mxt_probe(struct i2c_client *client,
goto err_free_pdata;
}

+ mxt_probe_regulators(data);
+
disable_irq(data->irq);

error = mxt_initialize(data);
@@ -2451,6 +2530,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);
if (!dev_get_platdata(&data->client->dev))
kfree(data->pdata);
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index b7d2092..a01f2e8 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 */
--
1.7.10.4

2013-06-27 12:53:00

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 46/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index b4ecd1d..5ab3861 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1226,6 +1226,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
*
@@ -1485,6 +1487,9 @@ static int mxt_check_reg_init(struct mxt_data *data)

dev_info(dev, "Config written\n");

+ /* T7 config may have changed */
+ mxt_init_t7_power_cfg(data);
+
release_mem:
kfree(config_mem);
release:
@@ -1883,17 +1888,17 @@ 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 initialising configuration\n",
- error);
+ dev_err(&client->dev, "Failed to initialize power cfg\n");
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;
}

--
1.7.10.4

2013-06-27 12:50:53

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 39/51] Input: atmel_mxt_ts - Implement T63 Active Stylus support

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 95 +++++++++++++++++++++++++++++-
1 file changed, 94 insertions(+), 1 deletion(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index b63f227..2e6118a 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;
@@ -823,6 +840,63 @@ 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;
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ /* 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 +912,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 if (report_id >= data->T42_reportid_min
&& report_id <= data->T42_reportid_max) {
mxt_proc_t42_messages(data, message);
@@ -1483,6 +1560,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;
}

@@ -1563,6 +1642,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
@@ -2103,7 +2188,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);
@@ -2121,6 +2206,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);
--
1.7.10.4

2013-06-27 12:53:39

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 45/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 46 ++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ceb090a..b4ecd1d 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -249,6 +249,9 @@ struct mxt_data {

/* Enable reporting of input events */
bool enable_reporting;
+
+ /* Indicates whether device is in suspend */
+ bool suspended;
};

static inline size_t mxt_obj_size(const struct mxt_object *obj)
@@ -2025,6 +2028,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;
@@ -2137,6 +2145,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;
@@ -2162,17 +2172,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->enable_reporting = true;
+ data->suspended = false;
}

static void mxt_stop(struct mxt_data *data)
{
+ if (data->suspended || data->in_bootloader)
+ return;
+
+ data->enable_reporting = false;
+ 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)
--
1.7.10.4

2013-06-27 12:53:58

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 43/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 8827bbd..ee39683 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;
@@ -1181,6 +1184,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
*
@@ -1430,6 +1458,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;
@@ -1504,9 +1536,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;
}
@@ -1528,6 +1562,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;
@@ -1598,6 +1633,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;
@@ -1820,6 +1858,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;
--
1.7.10.4

2013-06-27 12:50:52

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 38/51] 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 <[email protected]>
---
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 9188cf7..b63f227 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
@@ -567,6 +568,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;
@@ -583,17 +585,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,
@@ -602,6 +609,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);
@@ -612,14 +620,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);
--
1.7.10.4

2013-06-27 12:54:18

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 42/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 182 ++++++++++++++++++------------
1 file changed, 112 insertions(+), 70 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index c01a457..8827bbd 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 */
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;
@@ -363,12 +364,16 @@ static int mxt_lookup_bootloader_address(struct mxt_data *data, bool retry)
{
u8 appmode = data->client->addr;
u8 bootloader;
+ u8 family_id = 0;
+
+ if (data->info)
+ family_id = data->info->family_id;

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;
}
@@ -608,7 +613,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;
@@ -1245,13 +1250,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;
@@ -1298,7 +1303,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);
@@ -1506,24 +1511,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->enable_reporting = false;
@@ -1545,25 +1538,17 @@ 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 i2c_client *client = data->client;
- size_t table_size;
- 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,
- data->object_table);
- if (error)
- 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 = data->object_table + i;
u8 min_id, max_id;

@@ -1587,7 +1572,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 */
data->T5_msg_size = mxt_obj_size(object);
@@ -1647,22 +1632,103 @@ 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 *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);
+ buf = kzalloc(size, GFP_KERNEL);
+ if (!buf) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ error = __mxt_read_reg(client, 0, size, buf);
+ if (error)
+ goto err_free_mem;
+
+ /* Resize buffer to give space for rest of info block */
+ num_objects = ((struct mxt_info *)buf)->object_num;
+ size += (num_objects * sizeof(struct mxt_object))
+ + MXT_INFO_CHECKSUM_SIZE;
+
+ buf = krealloc(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 */
+ 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);
+ return -EIO;
+ }
+
+ /* Save pointers in device data structure */
+ data->raw_info_block = buf;
+ data->info = (struct mxt_info *)buf;
+ data->object_table = (struct mxt_object *)(buf + MXT_OBJECT_START);
+
+ 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);
+ if (error) {
+ dev_err(&client->dev, "Error %d reading object table\n", error);
+ mxt_free_object_table(data);
+ return error;
}

return 0;

-free_object_table:
- mxt_free_object_table(data);
+err_free_mem:
+ kfree(buf);
return error;
}

@@ -1717,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);
@@ -1755,21 +1820,6 @@ retry_bootloader:
}
}

- 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;
- }
-
error = mxt_acquire_irq(data);
if (error)
goto err_free_object_table;
@@ -1788,17 +1838,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);
-
data->enable_reporting = true;

return 0;
@@ -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))
@@ -2127,6 +2165,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");
--
1.7.10.4

2013-06-27 12:54:40

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 41/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 2eaa9d3..c01a457 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -25,27 +25,13 @@
#include <linux/interrupt.h>
#include <linux/slab.h>

-/* 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
@@ -1577,7 +1513,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;

--
1.7.10.4

2013-06-27 12:55:29

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 40/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 57 ++++++++++++++++++++++++++++++
include/linux/i2c/atmel_mxt_ts.h | 2 ++
2 files changed, 59 insertions(+)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2e6118a..2eaa9d3 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;
@@ -809,6 +812,42 @@ 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]);
+
+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
+ 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;
@@ -920,6 +959,9 @@ 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 (report_id >= data->T15_reportid_min
+ && report_id <= data->T15_reportid_max) {
+ mxt_proc_t15_messages(data, message);
} else {
mxt_dump_message(data, message);
}
@@ -1555,6 +1597,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;
@@ -1629,6 +1673,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;
@@ -2214,6 +2262,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 02bf6ea..b7d2092 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 */
--
1.7.10.4

2013-06-27 12:50:50

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 33/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 111 ++++++++++++++++--------------
1 file changed, 58 insertions(+), 53 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 3890ed5..ef68bf1 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;
@@ -668,20 +670,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;
@@ -708,7 +696,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;
@@ -772,44 +760,61 @@ 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 (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 (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 (data->enable_reporting && update_input)
+ if (data->enable_reporting && data->update_input) {
mxt_input_sync(data->input_dev);
+ data->update_input = false;
+ }

return IRQ_HANDLED;
}
@@ -1256,21 +1261,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)
@@ -1307,6 +1310,7 @@ static void mxt_free_object_table(struct mxt_data *data)
kfree(data->msg_buf);
data->msg_buf = NULL;
data->enable_reporting = false;
+ data->T5_address = 0;
data->T5_msg_size = 0;
data->T6_reportid = 0;
data->T7_address = 0;
@@ -1359,6 +1363,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;
--
1.7.10.4

2013-06-27 12:55:47

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 37/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 1c5e640..9188cf7 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -716,6 +716,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
int y;
int area;
int amplitude;
+ u8 vector;

/* do not report events if input device not yet registered */
if (!data->enable_reporting)
@@ -734,9 +735,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' : '.',
@@ -746,7 +748,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);

@@ -766,6 +768,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);
@@ -2100,6 +2103,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);

--
1.7.10.4

2013-06-27 12:56:11

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 36/51] Input: atmel_mxt_ts - Output status from T42 Touch Suppression

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 d7ce38d..1c5e640 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;

@@ -769,6 +774,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;
@@ -804,6 +820,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->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 {
@@ -1442,6 +1461,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;
@@ -1511,6 +1532,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;
--
1.7.10.4

2013-06-27 12:56:42

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 35/51] Input: atmel_mxt_ts - Output status from T48 Noise Supression

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 5d51133..d7ce38d 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;
@@ -768,6 +769,26 @@ 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];
@@ -783,6 +804,8 @@ 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->T48_reportid) {
+ mxt_proc_t48_messages(data, message);
} else {
mxt_dump_message(data, message);
}
@@ -1420,6 +1443,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;
}

@@ -1493,6 +1517,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
--
1.7.10.4

2013-06-27 12:56:58

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 32/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 688eab3..3890ed5 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;
@@ -629,6 +635,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);
+
+ if (crc != data->config_crc) {
+ data->config_crc = crc;
+ dev_dbg(dev, "T6 Config Checksum: 0x%06X\n", crc);
+ complete(&data->crc_completion);
+ }
+
+ /* Detect transition out of reset */
+ if ((data->t6_status & MXT_T6_STATUS_RESET) &&
+ !(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;
@@ -735,11 +774,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];
@@ -749,11 +783,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)) {
@@ -764,19 +796,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 (mxt_is_T9_message(data, message)) {
mxt_input_touchevent(data, message);
update_input = true;
--
1.7.10.4

2013-06-27 12:56:55

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 34/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 190 +++++++++++++++++++++++++-----
1 file changed, 158 insertions(+), 32 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ef68bf1..5d51133 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;
@@ -786,30 +790,143 @@ 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;
+
+ /* read two at a time until an invalid message or else we reach
+ * 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->enable_reporting && data->update_input) {
mxt_input_sync(data->input_dev);
@@ -832,7 +949,11 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
if (!data->object_table)
return IRQ_NONE;

- 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,
@@ -1257,32 +1378,13 @@ recheck:
}
}

-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;

@@ -1317,6 +1419,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)
@@ -1361,8 +1465,14 @@ 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;
@@ -1374,6 +1484,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;
@@ -1387,7 +1502,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;
--
1.7.10.4

2013-06-27 12:50:47

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 23/51] Input: atmel_mxt_ts - Rename pressure to amplitude to match spec

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 76f1c20..8ccc809de 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -637,7 +637,7 @@ static void mxt_input_touchevent(struct mxt_data *data,
int x;
int y;
int area;
- int pressure;
+ int amplitude;

/* do not report events if input device not yet registered */
if (!data->enable_reporting)
@@ -651,7 +651,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",
@@ -664,7 +664,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,
@@ -673,7 +673,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);
}
}
--
1.7.10.4

2013-06-27 12:57:35

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 31/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 b8e2b04..688eab3 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;
@@ -312,11 +312,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,
@@ -630,8 +629,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;
@@ -642,10 +640,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;
@@ -660,7 +658,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);
}
}
@@ -671,12 +669,12 @@ 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;
@@ -686,8 +684,10 @@ static void mxt_input_touchevent(struct mxt_data *data,
if (!data->enable_reporting)
return;

- 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)
@@ -695,8 +695,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",
@@ -740,28 +740,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];
@@ -777,17 +777,16 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)

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);
+ } 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 (data->enable_reporting && update_input)
mxt_input_sync(data->input_dev);
@@ -1236,16 +1235,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");
@@ -1282,6 +1280,21 @@ 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->enable_reporting = false;
+ 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;
@@ -1323,6 +1336,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;
@@ -1346,18 +1362,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);
+ if (!data->msg_buf) {
+ dev_err(&client->dev, "Failed to allocate message buffer\n");
+ error = -ENOMEM;
+ goto free_object_table;
+ }
+
return 0;
-}

-static void mxt_free_object_table(struct mxt_data *data)
-{
- kfree(data->object_table);
- data->object_table = NULL;
- data->enable_reporting = false;
- 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)
@@ -1959,7 +1975,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(data->irq, data);
err_free_pdata:
@@ -1977,7 +1993,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);
if (!dev_get_platdata(&data->client->dev))
kfree(data->pdata);
kfree(data);
--
1.7.10.4

2013-06-27 12:57:53

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 30/51] 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 <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 68 ++++++++++++++++++++++--------
1 file changed, 51 insertions(+), 17 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 780952a..b8e2b04 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -385,7 +385,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;
@@ -394,7 +394,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;
}
@@ -416,14 +416,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;

@@ -524,13 +524,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)
@@ -1408,15 +1413,40 @@ 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");
+ /* this is not an error state, we can reflash
+ * from here */
+ 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;
+ }
}

data->object_table = kcalloc(info->object_num,
@@ -1595,10 +1625,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;
@@ -1609,6 +1635,14 @@ static int mxt_load_fw(struct device *dev, const char *fn)
goto release_firmware;

msleep(MXT_RESET_TIME);
+
+ /* At this stage, 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);
@@ -1625,7 +1659,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;
}
--
1.7.10.4

2013-06-27 12:59:03

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 29/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 4385653..780952a 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -393,6 +393,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:
--
1.7.10.4

2013-06-27 12:50:46

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 21/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 100 ++++++++++++++++++++++--------
1 file changed, 74 insertions(+), 26 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2645d36..ee1e866 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -90,9 +90,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
@@ -104,7 +108,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

@@ -243,6 +246,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;
@@ -604,20 +608,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;
@@ -1133,6 +1123,61 @@ 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_info(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);
+ }
+ } else {
+ dev_info(dev, "Initialised 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;
@@ -1345,6 +1390,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");
@@ -1612,16 +1663,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)
@@ -1834,8 +1884,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)
--
1.7.10.4

2013-06-27 12:59:32

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 28/51] Input: atmel_mxt_ts - Handle bootloader previously unlocked

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 28 +++++++++++++++++++---------
1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 0c0df6c..4385653 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -455,14 +455,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
@@ -1607,15 +1608,24 @@ 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 update
+ * 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;

@@ -1629,7 +1639,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++;

--
1.7.10.4

2013-06-27 12:59:50

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 26/51] Input: atmel_mxt_ts - Move input device init into separate function

Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 129 +++++++++++++++++-------------
1 file changed, 75 insertions(+), 54 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 8632133..030ebc5 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1728,73 +1728,39 @@ static int mxt_handle_pdata(struct mxt_data *data)
return 0;
}

-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)
{
- 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;

- 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->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(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_pdata;
- }
-
- disable_irq(client->irq);
-
- error = mxt_initialize(data);
- if (error)
- goto err_free_irq;
-
__set_bit(EV_ABS, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);

- if (data->pdata->t19_num_keys) {
+ if (pdata->t19_num_keys) {
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);

- for (i = 0; i < data->pdata->t19_num_keys; i++)
- if (data->pdata->t19_keymap[i] != KEY_RESERVED)
+ for (i = 0; i < pdata->t19_num_keys; i++)
+ if (pdata->t19_keymap[i] != KEY_RESERVED)
input_set_capability(input_dev, EV_KEY,
- data->pdata->t19_keymap[i]);
+ pdata->t19_keymap[i]);

mt_flags |= INPUT_MT_POINTER;

@@ -1819,8 +1785,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,
@@ -1834,11 +1803,64 @@ static int mxt_probe(struct i2c_client *client,

error = input_register_device(input_dev);
if (error) {
- dev_err(&client->dev, "Error %d registering input device\n",
- error);
- goto err_free_object;
+ 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;
+ int error;
+
+ 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->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(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_pdata;
+ }
+
+ disable_irq(data->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",
@@ -1849,8 +1871,8 @@ 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:
@@ -1859,7 +1881,6 @@ err_free_pdata:
if (!dev_get_platdata(&data->client->dev))
kfree(data->pdata);
err_free_mem:
- input_free_device(input_dev);
kfree(data);
return error;
}
--
1.7.10.4

2013-06-27 12:59:48

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 27/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 030ebc5..0c0df6c 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -410,6 +410,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;
@@ -469,6 +493,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:
@@ -1378,8 +1403,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;
+ }

data->object_table = kcalloc(info->object_num,
sizeof(struct mxt_object),
@@ -1561,15 +1592,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);
@@ -1652,8 +1687,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;
@@ -1857,9 +1890,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) {
--
1.7.10.4

2013-06-27 12:50:44

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 22/51] Input: atmel_mxt_ts - Add shutdown function

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ee1e866..76f1c20 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1897,6 +1897,13 @@ static int mxt_resume(struct device *dev)

static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);

+static void mxt_shutdown(struct i2c_client *client)
+{
+ struct mxt_data *data = i2c_get_clientdata(client);
+
+ disable_irq(data->irq);
+}
+
static const struct i2c_device_id mxt_id[] = {
{ "qt602240_ts", 0 },
{ "atmel_mxt_ts", 0 },
@@ -1914,6 +1921,7 @@ static struct i2c_driver mxt_driver = {
},
.probe = mxt_probe,
.remove = mxt_remove,
+ .shutdown = mxt_shutdown,
.id_table = mxt_id,
};

--
1.7.10.4

2013-06-27 13:01:31

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 25/51] Input: atmel_mxt_ts - Handle multiple input reports in one message

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 34 +++++++++++++++++++++++-------
1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 5a16383..8632133 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -628,6 +628,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)
{
@@ -645,10 +651,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];
@@ -667,14 +675,26 @@ 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);
}
}

@@ -732,10 +752,8 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)
}
} while (reportid != 0xff);

- if (data->enable_reporting && update_input) {
- input_mt_report_pointer_emulation(data->input_dev, false);
- input_sync(data->input_dev);
- }
+ if (data->enable_reporting && update_input)
+ mxt_input_sync(data->input_dev);

return IRQ_HANDLED;
}
--
1.7.10.4

2013-06-27 13:01:53

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 24/51] Input: atmel_mxt_ts - Rename touchscreen defines to include T9

This avoids confusion with the newer T100 touchscreen object.

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 8ccc809de..5a16383 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -111,13 +111,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
@@ -192,16 +202,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

@@ -656,21 +656,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);
@@ -1339,7 +1339,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 {
--
1.7.10.4

2013-06-27 13:02:32

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 20/51] Input: atmel_mxt_ts - Set default irqflags when there is no pdata

From: Yufeng Shen <[email protected]>

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 <[email protected]>
Signed-off-by: Daniel Kurtz <[email protected]>
Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 54 +++++++++++++++++++++---------
1 file changed, 39 insertions(+), 15 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 1334e5b..2645d36 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -232,7 +232,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;
unsigned int irq;
@@ -1640,10 +1640,29 @@ 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_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
- const struct mxt_platform_data *pdata = client->dev.platform_data;
struct mxt_data *data;
struct input_dev *input_dev;
int error;
@@ -1651,9 +1670,6 @@ static int mxt_probe(struct i2c_client *client,
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) {
@@ -1675,19 +1691,23 @@ static int mxt_probe(struct i2c_client *client,

data->client = client;
data->input_dev = input_dev;
- 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;
}

disable_irq(client->irq);
@@ -1700,13 +1720,13 @@ static int mxt_probe(struct i2c_client *client,
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);

- if (pdata->t19_num_keys) {
+ if (data->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)
+ for (i = 0; i < data->pdata->t19_num_keys; i++)
+ if (data->pdata->t19_keymap[i] != KEY_RESERVED)
input_set_capability(input_dev, EV_KEY,
- pdata->t19_keymap[i]);
+ data->pdata->t19_keymap[i]);

mt_flags |= INPUT_MT_POINTER;

@@ -1743,7 +1763,6 @@ static int mxt_probe(struct i2c_client *client,
0, 255, 0, 0);

input_set_drvdata(input_dev, data);
- i2c_set_clientdata(client, data);

error = input_register_device(input_dev);
if (error) {
@@ -1767,7 +1786,10 @@ err_unregister_device:
err_free_object:
kfree(data->object_table);
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:
input_free_device(input_dev);
kfree(data);
@@ -1782,6 +1804,8 @@ static int mxt_remove(struct i2c_client *client)
free_irq(data->irq, data);
input_unregister_device(data->input_dev);
kfree(data->object_table);
+ if (!dev_get_platdata(&data->client->dev))
+ kfree(data->pdata);
kfree(data);

return 0;
--
1.7.10.4

2013-06-27 12:50:42

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 13/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 224 +++++++++++++++++++++++-------
1 file changed, 171 insertions(+), 53 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2054c64..751f446 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -53,6 +53,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
@@ -263,11 +265,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;
@@ -768,6 +773,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
*
@@ -795,9 +839,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;
@@ -817,11 +865,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) {
@@ -830,7 +878,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) {
@@ -845,123 +893,182 @@ 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;
+
+ /* 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_info(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);
+ }

- if (data->config_crc == config_crc) {
- dev_info(dev, "Config CRC 0x%06X: OK\n", config_crc);
- ret = 0;
+ /* 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;
- } else {
- dev_info(dev, "Config CRC 0x%06X: does not match file 0x%06X\n",
- data->config_crc, config_crc);
}

- 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 %u 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 %u 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 %u 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 %u 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 written\n");

+release_mem:
+ kfree(config_mem);
release:
release_firmware(cfg);
return ret;
@@ -1023,6 +1130,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,
@@ -1032,6 +1140,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;
@@ -1059,6 +1168,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;
@@ -1067,6 +1179,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;
--
1.7.10.4

2013-06-27 13:03:05

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 19/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
arch/arm/mach-exynos/mach-nuri.c | 3 -
arch/arm/mach-exynos/mach-universal_c210.c | 3 -
arch/arm/mach-s5pv210/mach-goni.c | 3 -
drivers/input/touchscreen/atmel_mxt_ts.c | 136 +++++++++++++---------------
drivers/platform/x86/chromeos_laptop.c | 6 --
include/linux/i2c/atmel_mxt_ts.h | 14 ---
6 files changed, 65 insertions(+), 100 deletions(-)

diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c
index 4516d26..726e1b7 100644
--- a/arch/arm/mach-exynos/mach-nuri.c
+++ b/arch/arm/mach-exynos/mach-nuri.c
@@ -333,9 +333,6 @@ static struct i2c_board_info i2c1_devs[] __initdata = {

/* TSP */
static struct mxt_platform_data mxt_platform_data = {
- .x_size = 1024,
- .y_size = 600,
- .orient = MXT_DIAGONAL_COUNTER,
.irqflags = IRQF_TRIGGER_FALLING,
};

diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c
index 2461e60..8bc147f 100644
--- a/arch/arm/mach-exynos/mach-universal_c210.c
+++ b/arch/arm/mach-exynos/mach-universal_c210.c
@@ -604,9 +604,6 @@ static struct i2c_board_info i2c5_devs[] __initdata = {

/* I2C3 (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/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c
index eca73af..39cacf0 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 767050b..1334e5b 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -105,33 +105,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
@@ -216,11 +199,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

@@ -560,11 +538,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)
{
@@ -1287,12 +1260,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_info(&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)
@@ -1325,26 +1345,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);

data->enable_reporting = true;

@@ -1355,20 +1365,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)
@@ -1686,8 +1682,6 @@ static int mxt_probe(struct i2c_client *client,
init_completion(&data->reset_completion);
init_completion(&data->crc_completion);

- mxt_calc_resolution(data);
-
error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
pdata->irqflags | IRQF_ONESHOT,
client->name, data);
diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c
index 4f6f0b8..545a6d5 100644
--- a/drivers/platform/x86/chromeos_laptop.c
+++ b/drivers/platform/x86/chromeos_laptop.c
@@ -80,9 +80,6 @@ static int mxt_t19_keys[] = {
};

static struct mxt_platform_data atmel_224s_tp_platform_data = {
- .x_size = 102*20,
- .y_size = 68*20,
- .orient = MXT_VERTICAL_FLIP,
.irqflags = IRQF_TRIGGER_FALLING,
.t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
.t19_keymap = mxt_t19_keys,
@@ -95,9 +92,6 @@ static struct i2c_board_info __initdata atmel_224s_tp_device = {
};

static struct mxt_platform_data atmel_1664s_platform_data = {
- .x_size = 1700,
- .y_size = 2560,
- .orient = MXT_ROTATED_90_COUNTER,
.irqflags = IRQF_TRIGGER_FALLING,
};

diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index b569bb8..02bf6ea 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -15,22 +15,8 @@

#include <linux/types.h>

-/* 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;
--
1.7.10.4

2013-06-27 13:03:31

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 17/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 3172610..99ebdec 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1456,6 +1456,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);
@@ -1515,9 +1516,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_info(dev, "Sent %d frames, %d/%zd bytes\n",
+ frame, pos, fw->size);
}

/* Wait for flash. */
@@ -1526,6 +1530,8 @@ static int mxt_load_fw(struct device *dev, const char *fn)
if (ret)
goto disable_irq;

+ dev_info(dev, "Sent %d frames, %zd bytes\n", frame, pos);
+
/* Wait for device to reset. Some bootloader versions do not assert
* the CHG line after bootloading has finished, so ignore error */
mxt_wait_for_completion(data, &data->bl_completion,
--
1.7.10.4

2013-06-27 13:03:30

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 18/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 99ebdec..767050b 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1449,6 +1449,28 @@ 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 -1;
+}
+
static int mxt_load_fw(struct device *dev, const char *fn)
{
struct mxt_data *data = dev_get_drvdata(dev);
@@ -1465,6 +1487,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;
--
1.7.10.4

2013-06-27 12:50:40

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 12/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 234 ++++++++++++++++++++++--------
drivers/platform/x86/chromeos_laptop.c | 4 -
include/linux/i2c/atmel_mxt_ts.h | 4 -
3 files changed, 175 insertions(+), 67 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index ad9fc91..2054c64 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -36,8 +36,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
@@ -326,37 +328,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)
{
@@ -550,7 +521,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;
}

@@ -797,58 +768,203 @@ 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:
+ * <TYPE> <INSTANCE> <SIZE> <CONTENTS>
+ *
+ * <TYPE> - 2-byte object type as hex
+ * <INSTANCE> - 2-byte object instance number as hex
+ * <SIZE> - 2-byte object size as hex
+ * <CONTENTS> - array of <SIZE> 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;
- } else {
- dev_info(dev, "Config CRC 0x%06X: does not match 0x%06X\n",
- data->config_crc, pdata->config_crc);
+ if (strncmp(cfg->data, MXT_CFG_MAGIC, strlen(MXT_CFG_MAGIC))) {
+ dev_err(dev, "Unrecognised config file\n");
+ ret = -EINVAL;
+ goto release;
}

- for (i = 0; i < data->info.object_num; i++) {
- object = data->object_table + i;
+ 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;
+ }
+
+ pos += offset;
+ }
+
+ if (cfg_info.family_id != data->info.family_id) {
+ dev_err(dev, "Family ID mismatch!\n");
+ ret = -EINVAL;
+ goto release;
+ }

- if (!mxt_object_writable(object->type))
+ 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 (data->config_crc == config_crc) {
+ dev_info(dev, "Config CRC 0x%06X: OK\n", 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);
+ }
+
+ 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 %u 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 %u 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 written\n");

- return 0;
+release:
+ release_firmware(cfg);
+ return ret;
}

static int mxt_make_highchg(struct mxt_data *data)
diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c
index 8026554..4f6f0b8 100644
--- a/drivers/platform/x86/chromeos_laptop.c
+++ b/drivers/platform/x86/chromeos_laptop.c
@@ -86,8 +86,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = {
.irqflags = IRQF_TRIGGER_FALLING,
.t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
.t19_keymap = mxt_t19_keys,
- .config = NULL,
- .config_length = 0,
};

static struct i2c_board_info __initdata atmel_224s_tp_device = {
@@ -101,8 +99,6 @@ static struct mxt_platform_data atmel_1664s_platform_data = {
.y_size = 2560,
.orient = MXT_ROTATED_90_COUNTER,
.irqflags = IRQF_TRIGGER_FALLING,
- .config = NULL,
- .config_length = 0,
};

static struct i2c_board_info __initdata atmel_1664s_device = {
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index 9f92135..b569bb8 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;
--
1.7.10.4

2013-06-27 13:04:04

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 16/51] Input: atmel_mxt_ts - Implement bootloader frame retries

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 1ad60b6..3172610 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -490,8 +490,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;
@@ -1451,6 +1455,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);
@@ -1488,9 +1493,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 */
@@ -1499,10 +1502,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);
}
--
1.7.10.4

2013-06-27 13:04:28

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 15/51] Input: atmel_mxt_ts - Read and report bootloader version

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 82327e6..1ad60b6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -203,6 +203,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)
@@ -426,6 +428,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_info(dev, "Bootloader ID:%d Version:%d\n", buf[1], buf[2]);
+
+ return buf[0];
+ } else {
+ dev_info(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;
@@ -458,6 +481,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:
--
1.7.10.4

2013-06-27 13:04:44

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 14/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 751f446..82327e6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -30,12 +30,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"
@@ -268,6 +262,7 @@ struct mxt_data {
u16 mem_size;
u32 config_crc;
u32 info_crc;
+ u8 bootloader_addr;

/* Cached parameters from object table */
u8 T6_reportid;
@@ -358,9 +353,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;

@@ -381,15 +449,14 @@ recheck:
* 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:
@@ -405,7 +472,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;
}
@@ -413,28 +480,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;
}
@@ -1366,7 +1422,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;
@@ -1378,6 +1433,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;

@@ -1387,12 +1446,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);
@@ -1400,7 +1453,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);
@@ -1415,7 +1468,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)
@@ -1443,13 +1498,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;
}

--
1.7.10.4

2013-06-27 13:04:59

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 11/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 55 +++++++++++++++++++++++++-----
include/linux/i2c/atmel_mxt_ts.h | 1 +
2 files changed, 48 insertions(+), 8 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 471bc4d..ad9fc91 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -189,6 +189,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 */

@@ -260,6 +261,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;
@@ -274,6 +276,9 @@ struct mxt_data {
/* for reset handling */
struct completion reset_completion;

+ /* for reset handling */
+ struct completion crc_completion;
+
/* Enable reporting of input events */
bool enable_reporting;
};
@@ -648,7 +653,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);
}
@@ -666,6 +671,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)) {
@@ -677,9 +683,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);
@@ -772,6 +784,19 @@ 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;
@@ -786,6 +811,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;
+ } else {
+ 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;

@@ -805,6 +840,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 written\n");
+
return 0;
}

@@ -962,11 +1005,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)
@@ -1292,6 +1330,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 d26080d..9f92135 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;
--
1.7.10.4

2013-06-27 13:05:30

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 10/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 11ee78a..471bc4d 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -424,7 +424,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;
}

@@ -544,7 +545,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;
}

@@ -889,7 +890,7 @@ 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",
+ "T%u Start:%u Size:%u Instances:%u Report IDs:%u-%u\n",
object->type, object->start_address,
mxt_obj_size(object), mxt_obj_instances(object),
min_id, max_id);
@@ -944,8 +945,10 @@ 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;
+ }

error = mxt_acquire_irq(data);
if (error)
@@ -953,8 +956,11 @@ static int mxt_initialize(struct mxt_data *data)

/* Check register init values */
error = mxt_check_reg_init(data);
- if (error)
+ if (error) {
+ dev_err(&client->dev, "Error %d initialising configuration\n",
+ error);
goto err_free_object_table;
+ }

error = mxt_t6_command(data, MXT_COMMAND_BACKUPNV,
MXT_BACKUP_VALUE, false);
@@ -973,12 +979,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);

@@ -1187,7 +1193,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);

error = mxt_initialize(data);
@@ -1352,12 +1359,18 @@ static int mxt_probe(struct i2c_client *client,
i2c_set_clientdata(client, data);

error = input_register_device(input_dev);
- if (error)
+ if (error) {
+ dev_err(&client->dev, "Error %d registering input device\n",
+ error);
goto err_free_object;
+ }

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;

--
1.7.10.4

2013-06-27 13:06:05

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 08/51] Input: atmel_mxt_ts - Initialise IRQ before probing

This allows the interrupt handler to be used to detect CHG line during config
download. We need to make sure we do not report events if input device not yet
registered.

data->enable_reporting is checked in each of the possible message handling
function paths rather than higher up (such as at mxt_proc_message) because
some objects may be used for completions (T6).

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 73 +++++++++++++++++++++---------
1 file changed, 51 insertions(+), 22 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 55d7667..1e24e54 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-2012 Atmel Corporation
* Copyright (C) 2012 Google, Inc.
*
* Author: Joonyoung Shim <[email protected]>
@@ -261,6 +262,9 @@ struct mxt_data {

/* for fw update in bootloader */
struct completion bl_completion;
+
+ /* Enable reporting of input events */
+ bool enable_reporting;
};

static inline size_t mxt_obj_size(const struct mxt_object *obj)
@@ -568,6 +572,10 @@ static void mxt_input_button(struct mxt_data *data, struct mxt_message *message)
bool button;
int i;

+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
/* Active-low switch */
for (i = 0; i < pdata->t19_num_keys; i++) {
if (pdata->t19_keymap[i] == KEY_RESERVED)
@@ -588,6 +596,10 @@ static void mxt_input_touchevent(struct mxt_data *data,
int area;
int pressure;

+ /* do not report events if input device not yet registered */
+ if (!data->enable_reporting)
+ return;
+
x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
y = (message->message[2] << 4) | ((message->message[3] & 0xf));
if (data->max_x < 1024)
@@ -667,7 +679,7 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)
}
} while (reportid != 0xff);

- if (update_input) {
+ if (data->enable_reporting && update_input) {
input_mt_report_pointer_emulation(data->input_dev, false);
input_sync(data->input_dev);
}
@@ -685,6 +697,9 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}

+ if (!data->object_table)
+ return IRQ_NONE;
+
return mxt_process_messages_until_invalid(data);
}

@@ -746,6 +761,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;
@@ -819,6 +847,7 @@ static void mxt_free_object_table(struct mxt_data *data)
{
kfree(data->object_table);
data->object_table = NULL;
+ data->enable_reporting = false;
data->T6_reportid = 0;
data->T9_reportid_min = 0;
data->T9_reportid_max = 0;
@@ -849,6 +878,10 @@ static int mxt_initialize(struct mxt_data *data)
if (error)
goto err_free_object_table;

+ error = mxt_acquire_irq(data);
+ if (error)
+ goto err_free_object_table;
+
/* Check register init values */
error = mxt_check_reg_init(data);
if (error)
@@ -886,6 +919,8 @@ static int mxt_initialize(struct mxt_data *data)
info->matrix_xsize, info->matrix_ysize,
info->object_num);

+ data->enable_reporting = true;
+
return 0;

err_free_object_table:
@@ -1087,11 +1122,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
dev_dbg(dev, "The firmware update succeeded\n");
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;
}
@@ -1188,9 +1219,19 @@ static int mxt_probe(struct i2c_client *client,

mxt_calc_resolution(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_mem;
+ }
+
+ disable_irq(client->irq);
+
error = mxt_initialize(data);
if (error)
- goto err_free_mem;
+ goto err_free_irq;

__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
@@ -1241,21 +1282,9 @@ 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)
- goto err_free_irq;
+ goto err_free_object;

error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
if (error)
@@ -1266,10 +1295,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);
--
1.7.10.4

2013-06-27 13:06:03

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 09/51] Input: atmel_mxt_ts - Make wait-after-reset period compatible with all chips

From: Iiro Valkonen <[email protected]>

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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
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 1e24e54..11ee78a 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -88,6 +88,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
@@ -179,9 +182,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 */

@@ -256,6 +263,7 @@ 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;
@@ -263,6 +271,9 @@ struct mxt_data {
/* for fw update in bootloader */
struct completion bl_completion;

+ /* for reset handling */
+ struct completion reset_completion;
+
/* Enable reporting of input events */
bool enable_reporting;
};
@@ -348,10 +359,10 @@ 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;

@@ -380,7 +391,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 -EINTR better by terminating fw update
@@ -667,6 +679,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);
@@ -703,6 +718,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;
@@ -829,6 +897,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;
@@ -887,16 +956,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);
@@ -1040,8 +1103,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 */
@@ -1084,18 +1149,21 @@ 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;

/* Wait for device to reset. Some bootloader versions do not assert
* the CHG line after bootloading has finished, so ignore error */
- 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 */
@@ -1216,6 +1284,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);

@@ -1341,11 +1410,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);

--
1.7.10.4

2013-06-27 13:07:31

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 07/51] Input: atmel_mxt_ts - wait for CHG after bootloader resets

From: Benson Leung <[email protected]>

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 <[email protected]>
Signed-off-by: Daniel Kurtz <[email protected]>
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 19 ++++++++++++-------
1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 3f75e8c..55d7667 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
@@ -376,7 +376,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 -EINTR better by terminating fw update
@@ -1048,6 +1048,15 @@ 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 error */
+ mxt_wait_for_chg(data, MXT_FW_RESET_TIME);
+
data->in_bootloader = false;

disable_irq:
@@ -1076,10 +1085,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);
--
1.7.10.4

2013-06-27 13:08:09

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 05/51] Input: atmel_mxt_ts - Select FW_LOADER for firmware code

Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/Kconfig | 1 +
1 file changed, 1 insertion(+)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index f9a5fd8..beb5ad1 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -89,6 +89,7 @@ config TOUCHSCREEN_AD7879_SPI
config TOUCHSCREEN_ATMEL_MXT
tristate "Atmel mXT I2C Touchscreen"
depends on I2C
+ select FW_LOADER
help
Say Y here if you have Atmel mXT series I2C touchscreen,
such as AT42QT602240/ATMXT224, connected to your system.
--
1.7.10.4

2013-06-27 13:08:08

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 06/51] Input: atmel_mxt_ts - wait for CHG assert in mxt_check_bootloader

From: Benson Leung <[email protected]>

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 <[email protected]>
Signed-off-by: Daniel Kurtz <[email protected]>
Signed-off-by: Nick Dyer <[email protected]>
---
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 7de3b47..3f75e8c 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -15,6 +15,7 @@

#include <linux/module.h>
#include <linux/init.h>
+#include <linux/completion.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
@@ -247,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 inline size_t mxt_obj_size(const struct mxt_object *obj)
@@ -340,12 +344,51 @@ 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) {
+ dev_err(dev, "Wait for completion interrupted.\n");
+ return -EINTR;
+ } 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 -EINTR 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;
@@ -591,9 +634,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;
@@ -633,6 +675,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;
@@ -948,6 +1003,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);
@@ -958,18 +1015,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));

@@ -981,17 +1039,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 */
@@ -1010,8 +1070,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);
@@ -1025,13 +1083,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;
}
@@ -1121,6 +1179,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);
--
1.7.10.4

2013-06-27 13:08:47

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 02/51] 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 <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 44 ++++++++++++------------------
drivers/platform/x86/chromeos_laptop.c | 17 ++++++++----
include/linux/i2c/atmel_mxt_ts.h | 7 ++---
3 files changed, 30 insertions(+), 38 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index e01a9ed..6702175 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -181,12 +181,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
@@ -251,7 +245,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;
@@ -516,15 +509,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);
}
}

@@ -1085,6 +1079,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;
@@ -1097,10 +1093,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);

@@ -1126,20 +1119,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);
@@ -1147,6 +1135,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 */
@@ -1159,7 +1149,7 @@ static int mxt_probe(struct i2c_client *client,

/* For multi touch */
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
- error = input_mt_init_slots(input_dev, num_mt_slots, 0);
+ error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
if (error)
goto err_free_object;
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c
index 78ed277..8026554 100644
--- a/drivers/platform/x86/chromeos_laptop.c
+++ b/drivers/platform/x86/chromeos_laptop.c
@@ -70,16 +70,22 @@ static struct i2c_board_info __initdata tsl2563_als_device = {
I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR),
};

+static int mxt_t19_keys[] = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ BTN_LEFT
+};
+
static struct mxt_platform_data atmel_224s_tp_platform_data = {
.x_size = 102*20,
.y_size = 68*20,
.orient = MXT_VERTICAL_FLIP,
.irqflags = IRQF_TRIGGER_FALLING,
- .is_tp = true,
- .key_map = { KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- BTN_LEFT },
+ .t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
+ .t19_keymap = mxt_t19_keys,
.config = NULL,
.config_length = 0,
};
@@ -95,7 +101,6 @@ static struct mxt_platform_data atmel_1664s_platform_data = {
.y_size = 2560,
.orient = MXT_ROTATED_90_COUNTER,
.irqflags = IRQF_TRIGGER_FALLING,
- .is_tp = false,
.config = NULL,
.config_length = 0,
};
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index eff0cdc..d26080d 100644
--- a/include/linux/i2c/atmel_mxt_ts.h
+++ b/include/linux/i2c/atmel_mxt_ts.h
@@ -15,9 +15,6 @@

#include <linux/types.h>

-/* 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 */
--
1.7.10.4

2013-06-27 13:08:46

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 01/51] 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 <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
arch/arm/mach-exynos/mach-nuri.c | 5 ---
arch/arm/mach-exynos/mach-universal_c210.c | 5 ---
arch/arm/mach-s5pv210/mach-goni.c | 5 ---
drivers/input/touchscreen/atmel_mxt_ts.c | 50 ----------------------------
drivers/platform/x86/chromeos_laptop.c | 10 ------
include/linux/i2c/atmel_mxt_ts.h | 6 +---
6 files changed, 1 insertion(+), 80 deletions(-)

diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c
index 1ea7973..4516d26 100644
--- a/arch/arm/mach-exynos/mach-nuri.c
+++ b/arch/arm/mach-exynos/mach-nuri.c
@@ -333,13 +333,8 @@ static struct i2c_board_info i2c1_devs[] __initdata = {

/* TSP */
static struct mxt_platform_data mxt_platform_data = {
- .x_line = 18,
- .y_line = 11,
.x_size = 1024,
.y_size = 600,
- .blen = 0x1,
- .threshold = 0x28,
- .voltage = 2800000, /* 2.8V */
.orient = MXT_DIAGONAL_COUNTER,
.irqflags = IRQF_TRIGGER_FALLING,
};
diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c
index 497fcb7..2461e60 100644
--- a/arch/arm/mach-exynos/mach-universal_c210.c
+++ b/arch/arm/mach-exynos/mach-universal_c210.c
@@ -604,13 +604,8 @@ static struct i2c_board_info i2c5_devs[] __initdata = {

/* I2C3 (TSP) */
static struct mxt_platform_data qt602240_platform_data = {
- .x_line = 19,
- .y_line = 11,
.x_size = 800,
.y_size = 480,
- .blen = 0x11,
- .threshold = 0x28,
- .voltage = 2800000, /* 2.8V */
.orient = MXT_DIAGONAL,
.irqflags = IRQF_TRIGGER_FALLING,
};
diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c
index 3a38f7b..eca73af 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 59aa240..e01a9ed 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -686,54 +686,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;
@@ -841,8 +793,6 @@ static int mxt_initialize(struct mxt_data *data)
if (error)
goto err_free_object_table;

- mxt_handle_pdata(data);
-
/* Backup to memory */
mxt_write_object(data, MXT_GEN_COMMAND_T6,
MXT_COMMAND_BACKUPNV,
diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c
index 3e5b4497..78ed277 100644
--- a/drivers/platform/x86/chromeos_laptop.c
+++ b/drivers/platform/x86/chromeos_laptop.c
@@ -71,13 +71,8 @@ static struct i2c_board_info __initdata tsl2563_als_device = {
};

static struct mxt_platform_data atmel_224s_tp_platform_data = {
- .x_line = 18,
- .y_line = 12,
.x_size = 102*20,
.y_size = 68*20,
- .blen = 0x80, /* Gain setting is in upper 4 bits */
- .threshold = 0x32,
- .voltage = 0, /* 3.3V */
.orient = MXT_VERTICAL_FLIP,
.irqflags = IRQF_TRIGGER_FALLING,
.is_tp = true,
@@ -96,13 +91,8 @@ static struct i2c_board_info __initdata atmel_224s_tp_device = {
};

static struct mxt_platform_data atmel_1664s_platform_data = {
- .x_line = 32,
- .y_line = 50,
.x_size = 1700,
.y_size = 2560,
- .blen = 0x89, /* Gain setting is in upper 4 bits */
- .threshold = 0x28,
- .voltage = 0, /* 3.3V */
.orient = MXT_ROTATED_90_COUNTER,
.irqflags = IRQF_TRIGGER_FALLING,
.is_tp = false,
diff --git a/include/linux/i2c/atmel_mxt_ts.h b/include/linux/i2c/atmel_mxt_ts.h
index 99e379b..eff0cdc 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];
--
1.7.10.4

2013-06-27 13:08:44

by Nick Dyer

[permalink] [raw]
Subject: [PATCH 04/51] Input: atmel_mxt_ts - define helper functions for size and instances

From: Daniel Kurtz <[email protected]>

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 <[email protected]>
Signed-off-by: Nick Dyer <[email protected]>
Acked-by: Benson Leung <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 35 ++++++++++++++++++++----------
1 file changed, 24 insertions(+), 11 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 2a06657..7de3b47 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 <[email protected]>
*
* This program is free software; you can redistribute it and/or modify it
@@ -227,8 +229,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;

@@ -257,6 +259,16 @@ struct mxt_data {
u8 T19_reportid;
};

+static inline size_t mxt_obj_size(const struct mxt_object *obj)
+{
+ return obj->size_minus_one + 1;
+}
+
+static inline 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) {
@@ -499,7 +511,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;
@@ -641,7 +653,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;
@@ -718,7 +730,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;
@@ -727,8 +739,9 @@ 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);
+ 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:
@@ -865,11 +878,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");
@@ -902,8 +915,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);
--
1.7.10.4

2013-06-27 15:18:01

by Nick Dyer

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

Hi Dimitry-

Nick Dyer wrote:
> Some minor updates to these patches to address review comments. I've rebased
> them on the latest dtor/next tree.

Apologies, I forgot to add Yufeng's Acked-by lines. To save cluttering up
the list, I've pushed a new version with just that change to

[email protected]:ndyer/linux.git

the tag is

for-next-20130627-v7

2013-07-07 05:29:40

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH 22/51] Input: atmel_mxt_ts - Add shutdown function

Hi Nick,

On Thu, Jun 27, 2013 at 01:48:57PM +0100, Nick Dyer wrote:
> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>

Why is this needed?

Thanks.

> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index ee1e866..76f1c20 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -1897,6 +1897,13 @@ static int mxt_resume(struct device *dev)
>
> static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
>
> +static void mxt_shutdown(struct i2c_client *client)
> +{
> + struct mxt_data *data = i2c_get_clientdata(client);
> +
> + disable_irq(data->irq);
> +}
> +
> static const struct i2c_device_id mxt_id[] = {
> { "qt602240_ts", 0 },
> { "atmel_mxt_ts", 0 },
> @@ -1914,6 +1921,7 @@ static struct i2c_driver mxt_driver = {
> },
> .probe = mxt_probe,
> .remove = mxt_remove,
> + .shutdown = mxt_shutdown,
> .id_table = mxt_id,
> };
>
> --
> 1.7.10.4
>

--
Dmitry

2013-07-07 05:34:12

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH 26/51] Input: atmel_mxt_ts - Move input device init into separate function

On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
> Signed-off-by: Nick Dyer <[email protected]>

So before we allocated input device before requesting IRQ, now we fo it
afterwards so there is moment where the interrupt is requested and not
disabled and input device is not allocated yet. Is it possible for
interrupt to happen at that moment?

Thanks.

> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 129 +++++++++++++++++-------------
> 1 file changed, 75 insertions(+), 54 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 8632133..030ebc5 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -1728,73 +1728,39 @@ static int mxt_handle_pdata(struct mxt_data *data)
> return 0;
> }
>
> -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)
> {
> - 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;
>
> - 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->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(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_pdata;
> - }
> -
> - disable_irq(client->irq);
> -
> - error = mxt_initialize(data);
> - if (error)
> - goto err_free_irq;
> -
> __set_bit(EV_ABS, input_dev->evbit);
> - __set_bit(EV_KEY, input_dev->evbit);
> - __set_bit(BTN_TOUCH, input_dev->keybit);
> + input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
>
> - if (data->pdata->t19_num_keys) {
> + if (pdata->t19_num_keys) {
> __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
>
> - for (i = 0; i < data->pdata->t19_num_keys; i++)
> - if (data->pdata->t19_keymap[i] != KEY_RESERVED)
> + for (i = 0; i < pdata->t19_num_keys; i++)
> + if (pdata->t19_keymap[i] != KEY_RESERVED)
> input_set_capability(input_dev, EV_KEY,
> - data->pdata->t19_keymap[i]);
> + pdata->t19_keymap[i]);
>
> mt_flags |= INPUT_MT_POINTER;
>
> @@ -1819,8 +1785,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,
> @@ -1834,11 +1803,64 @@ static int mxt_probe(struct i2c_client *client,
>
> error = input_register_device(input_dev);
> if (error) {
> - dev_err(&client->dev, "Error %d registering input device\n",
> - error);
> - goto err_free_object;
> + 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;
> + int error;
> +
> + 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->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(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_pdata;
> + }
> +
> + disable_irq(data->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",
> @@ -1849,8 +1871,8 @@ 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:
> @@ -1859,7 +1881,6 @@ err_free_pdata:
> if (!dev_get_platdata(&data->client->dev))
> kfree(data->pdata);
> err_free_mem:
> - input_free_device(input_dev);
> kfree(data);
> return error;
> }
> --
> 1.7.10.4
>

--
Dmitry

2013-07-08 09:41:38

by Nick Dyer

[permalink] [raw]
Subject: Re: [PATCH 26/51] Input: atmel_mxt_ts - Move input device init into separate function

Dmitry Torokhov wrote:
> On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
>> Signed-off-by: Nick Dyer <[email protected]>
>
> So before we allocated input device before requesting IRQ, now we fo it
> afterwards so there is moment where the interrupt is requested and not
> disabled and input device is not allocated yet. Is it possible for
> interrupt to happen at that moment?

Yes, and it will be handled correctly, there are guards in the correct
places to ensure the input device will not be used before being registered.

It is registered at this point since there are several paths that might
need the interrupt handler (for example, to handle flash if device is in
failed state, or to upload configuration if necessary).

2013-07-08 09:56:10

by Nick Dyer

[permalink] [raw]
Subject: Re: [PATCH 22/51] Input: atmel_mxt_ts - Add shutdown function

Dmitry Torokhov wrote:
> On Thu, Jun 27, 2013 at 01:48:57PM +0100, Nick Dyer wrote:
>> Signed-off-by: Nick Dyer <[email protected]>
>> Acked-by: Benson Leung <[email protected]>
>
> Why is this needed?

The patch disables the interrupt handler on shutdown.

One of our customers reported a bug caused by input events being generated
during shutdown (for example if the user was touching the device whilst it
was turning off), which was solved by putting in this change.

However, now you've drawn my attention to it again, it seems to me that
probably a better thing for us to be doing would be to power off the
touchscreen controller here, and let the interrupt disable be handled by
core code - do you agree?

2013-07-10 16:53:48

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH 26/51] Input: atmel_mxt_ts - Move input device init into separate function

On Mon, Jul 08, 2013 at 10:41:33AM +0100, Nick Dyer wrote:
> Dmitry Torokhov wrote:
> > On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
> >> Signed-off-by: Nick Dyer <[email protected]>
> >
> > So before we allocated input device before requesting IRQ, now we fo it
> > afterwards so there is moment where the interrupt is requested and not
> > disabled and input device is not allocated yet. Is it possible for
> > interrupt to happen at that moment?
>
> Yes, and it will be handled correctly, there are guards in the correct
> places to ensure the input device will not be used before being registered.
>
> It is registered at this point since there are several paths that might
> need the interrupt handler (for example, to handle flash if device is in
> failed state, or to upload configuration if necessary).

OK, thanks.

--
Dmitry

2013-07-10 16:55:16

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH 22/51] Input: atmel_mxt_ts - Add shutdown function

On Mon, Jul 08, 2013 at 10:56:06AM +0100, Nick Dyer wrote:
> Dmitry Torokhov wrote:
> > On Thu, Jun 27, 2013 at 01:48:57PM +0100, Nick Dyer wrote:
> >> Signed-off-by: Nick Dyer <[email protected]>
> >> Acked-by: Benson Leung <[email protected]>
> >
> > Why is this needed?
>
> The patch disables the interrupt handler on shutdown.
>
> One of our customers reported a bug caused by input events being generated
> during shutdown (for example if the user was touching the device whilst it
> was turning off), which was solved by putting in this change.

What kind of bug? Could you please be more precise?

>
> However, now you've drawn my attention to it again, it seems to me that
> probably a better thing for us to be doing would be to power off the
> touchscreen controller here, and let the interrupt disable be handled by
> core code - do you agree?

We'd be powering off everything in a moment anyway, no? Or is there a
concern that the device will stay powered up even if the system is in off
state?

Thanks.

--
Dmitry

2013-07-10 18:33:00

by Nick Dyer

[permalink] [raw]
Subject: Re: [PATCH 22/51] Input: atmel_mxt_ts - Add shutdown function

Dmitry Torokhov wrote:
>> The patch disables the interrupt handler on shutdown.
>>
>> One of our customers reported a bug caused by input events being generated
>> during shutdown (for example if the user was touching the device whilst it
>> was turning off), which was solved by putting in this change.
>
> What kind of bug? Could you please be more precise?

I did some more digging. Unfortunately the original problem report (some
time around March 2012) was in a customer ticket system that I no longer
have access to. It could well be trying to paper over a problem in their
app layer, to be honest.

>> However, now you've drawn my attention to it again, it seems to me that
>> probably a better thing for us to be doing would be to power off the
>> touchscreen controller here, and let the interrupt disable be handled by
>> core code - do you agree?
>
> We'd be powering off everything in a moment anyway, no? Or is there a
> concern that the device will stay powered up even if the system is in off
> state?

I agree. I think it's best to not apply this patch for the moment, until we
can prove it's actually required.

Let me know if you want me to generate a new pull request.

2013-07-18 17:10:24

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 02/51] Input: atmel_mxt_ts - Improve T19 GPIO keys handling

On Thu, Jun 27, 2013 at 01:48:37PM +0100, Nick Dyer wrote:
>
> * 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 <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 44 ++++++++++++------------------
> drivers/platform/x86/chromeos_laptop.c | 17 ++++++++----
> include/linux/i2c/atmel_mxt_ts.h | 7 ++---
> 3 files changed, 30 insertions(+), 38 deletions(-)

Reviewed-by: Henrik Rydberg <[email protected]>

Thanks,
Henrik

2013-07-18 17:12:38

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 08/51] Input: atmel_mxt_ts - Initialise IRQ before probing

Hi Nick,

> This allows the interrupt handler to be used to detect CHG line during config
> download. We need to make sure we do not report events if input device not yet
> registered.
>
> data->enable_reporting is checked in each of the possible message handling
> function paths rather than higher up (such as at mxt_proc_message) because
> some objects may be used for completions (T6).
>
> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 73 +++++++++++++++++++++---------
> 1 file changed, 51 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 55d7667..1e24e54 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-2012 Atmel Corporation
> * Copyright (C) 2012 Google, Inc.
> *
> * Author: Joonyoung Shim <[email protected]>
> @@ -261,6 +262,9 @@ struct mxt_data {
>
> /* for fw update in bootloader */
> struct completion bl_completion;
> +
> + /* Enable reporting of input events */
> + bool enable_reporting;
> };
>
> static inline size_t mxt_obj_size(const struct mxt_object *obj)
> @@ -568,6 +572,10 @@ static void mxt_input_button(struct mxt_data *data, struct mxt_message *message)
> bool button;
> int i;
>
> + /* do not report events if input device not yet registered */
> + if (!data->enable_reporting)
> + return;

Regardless of the rationale, this test seems to create complex logic
further down the patchset. Any chance it could be done differently?

> +
> /* Active-low switch */
> for (i = 0; i < pdata->t19_num_keys; i++) {
> if (pdata->t19_keymap[i] == KEY_RESERVED)
> @@ -588,6 +596,10 @@ static void mxt_input_touchevent(struct mxt_data *data,
> int area;
> int pressure;
>
> + /* do not report events if input device not yet registered */
> + if (!data->enable_reporting)
> + return;
> +
> x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
> y = (message->message[2] << 4) | ((message->message[3] & 0xf));
> if (data->max_x < 1024)
> @@ -667,7 +679,7 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)
> }
> } while (reportid != 0xff);
>
> - if (update_input) {
> + if (data->enable_reporting && update_input) {
> input_mt_report_pointer_emulation(data->input_dev, false);
> input_sync(data->input_dev);
> }
> @@ -685,6 +697,9 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
> return IRQ_HANDLED;
> }
>
> + if (!data->object_table)
> + return IRQ_NONE;
> +
> return mxt_process_messages_until_invalid(data);
> }
>
> @@ -746,6 +761,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;
> +}
> +

Strange error handling; the name suggests that an error means the irq
has not been enabled, which it clearly does anyways. Also, the error
is not checked everywhere later in the patchset. Opencode instead?

> static int mxt_get_info(struct mxt_data *data)
> {
> struct i2c_client *client = data->client;
> @@ -819,6 +847,7 @@ static void mxt_free_object_table(struct mxt_data *data)
> {
> kfree(data->object_table);
> data->object_table = NULL;
> + data->enable_reporting = false;
> data->T6_reportid = 0;
> data->T9_reportid_min = 0;
> data->T9_reportid_max = 0;
> @@ -849,6 +878,10 @@ static int mxt_initialize(struct mxt_data *data)
> if (error)
> goto err_free_object_table;
>
> + error = mxt_acquire_irq(data);
> + if (error)
> + goto err_free_object_table;
> +
> /* Check register init values */
> error = mxt_check_reg_init(data);
> if (error)
> @@ -886,6 +919,8 @@ static int mxt_initialize(struct mxt_data *data)
> info->matrix_xsize, info->matrix_ysize,
> info->object_num);
>
> + data->enable_reporting = true;
> +
> return 0;
>
> err_free_object_table:
> @@ -1087,11 +1122,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
> dev_dbg(dev, "The firmware update succeeded\n");
> 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;
> }
> @@ -1188,9 +1219,19 @@ static int mxt_probe(struct i2c_client *client,
>
> mxt_calc_resolution(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_mem;
> + }
> +
> + disable_irq(client->irq);

It is unclear what portion of the code should be executed with irqs
off. A better framing of the sensitive code path would be nice.

> +
> error = mxt_initialize(data);
> if (error)
> - goto err_free_mem;
> + goto err_free_irq;
>
> __set_bit(EV_ABS, input_dev->evbit);
> __set_bit(EV_KEY, input_dev->evbit);
> @@ -1241,21 +1282,9 @@ 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)
> - goto err_free_irq;
> + goto err_free_object;
>
> error = sysfs_create_group(&client->dev.kobj, &mxt_attr_group);
> if (error)
> @@ -1266,10 +1295,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);
> --
> 1.7.10.4
>

Thanks,
Henrik

2013-07-18 17:16:37

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 20/51] Input: atmel_mxt_ts - Set default irqflags when there is no pdata

Hi Nick,

> From: Yufeng Shen <[email protected]>
>
> 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 <[email protected]>
> Signed-off-by: Daniel Kurtz <[email protected]>
> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 54 +++++++++++++++++++++---------
> 1 file changed, 39 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 1334e5b..2645d36 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -232,7 +232,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;
> unsigned int irq;
> @@ -1640,10 +1640,29 @@ 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;
> + }

Any chance this could be a static instead?

> +
> + /* Set default parameters */
> + data->pdata->irqflags = IRQF_TRIGGER_FALLING;
> +
> + return 0;
> +}
> +

Opencode instead?

> static int mxt_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> - const struct mxt_platform_data *pdata = client->dev.platform_data;

This line keeps reappearing in various versions of this
function. Perhaps it should simply stay as is instead?

> struct mxt_data *data;
> struct input_dev *input_dev;
> int error;
> @@ -1651,9 +1670,6 @@ static int mxt_probe(struct i2c_client *client,
> unsigned int mt_flags = 0;
> int i;
>
> - if (!pdata)

Why not just initialize the default here instead?

> - return -EINVAL;
> -
> data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
> input_dev = input_allocate_device();
> if (!data || !input_dev) {
> @@ -1675,19 +1691,23 @@ static int mxt_probe(struct i2c_client *client,
>
> data->client = client;
> data->input_dev = input_dev;
> - data->pdata = pdata;
> data->irq = client->irq;
> + i2c_set_clientdata(client, data);
> +
> + error = mxt_handle_pdata(data);
> + if (error)
> + goto err_free_mem;

then this would go away

>
> 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);

and this hunk

> if (error) {
> dev_err(&client->dev, "Failed to register interrupt\n");
> - goto err_free_mem;
> + goto err_free_pdata;
> }
>
> disable_irq(client->irq);
> @@ -1700,13 +1720,13 @@ static int mxt_probe(struct i2c_client *client,
> __set_bit(EV_KEY, input_dev->evbit);
> __set_bit(BTN_TOUCH, input_dev->keybit);
>
> - if (pdata->t19_num_keys) {
> + if (data->pdata->t19_num_keys) {

and this hunk

> __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
>
> - for (i = 0; i < pdata->t19_num_keys; i++)
> - if (pdata->t19_keymap[i] != KEY_RESERVED)
> + for (i = 0; i < data->pdata->t19_num_keys; i++)
> + if (data->pdata->t19_keymap[i] != KEY_RESERVED)
> input_set_capability(input_dev, EV_KEY,
> - pdata->t19_keymap[i]);
> + data->pdata->t19_keymap[i]);
>
> mt_flags |= INPUT_MT_POINTER;
>
> @@ -1743,7 +1763,6 @@ static int mxt_probe(struct i2c_client *client,
> 0, 255, 0, 0);
>
> input_set_drvdata(input_dev, data);
> - i2c_set_clientdata(client, data);
>
> error = input_register_device(input_dev);
> if (error) {
> @@ -1767,7 +1786,10 @@ err_unregister_device:
> err_free_object:
> kfree(data->object_table);
> 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:
> input_free_device(input_dev);
> kfree(data);
> @@ -1782,6 +1804,8 @@ static int mxt_remove(struct i2c_client *client)
> free_irq(data->irq, data);
> input_unregister_device(data->input_dev);
> kfree(data->object_table);
> + if (!dev_get_platdata(&data->client->dev))
> + kfree(data->pdata);

Shared ownership should perhaps be signalled in a more robust way?

> kfree(data);
>
> return 0;
> --
> 1.7.10.4
>

Thanks,
Henrik

2013-07-18 17:17:16

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 25/51] Input: atmel_mxt_ts - Handle multiple input reports in one message

Hi Nick,

> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 34 +++++++++++++++++++++++-------
> 1 file changed, 26 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 5a16383..8632133 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -628,6 +628,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);
> +}

Why not handle the enable_reporting and update_input logic here as
well? The logic is inconsistent with the rest of the patchset.

> +
> static void mxt_input_touchevent(struct mxt_data *data,
> struct mxt_message *message, int id)
> {
> @@ -645,10 +651,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;

Unrelated changes.

>
> area = message->message[4];
> amplitude = message->message[5];
> @@ -667,14 +675,26 @@ 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);

What are the guarantees that nobody else expects the frame to not be
cut off here? What is the update_input state after this operation?

> + }
> +
> + /* 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);
> }
> }
>
> @@ -732,10 +752,8 @@ static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)
> }
> } while (reportid != 0xff);
>
> - if (data->enable_reporting && update_input) {
> - input_mt_report_pointer_emulation(data->input_dev, false);
> - input_sync(data->input_dev);
> - }
> + if (data->enable_reporting && update_input)
> + mxt_input_sync(data->input_dev);
>
> return IRQ_HANDLED;
> }
> --
> 1.7.10.4
>

Thanks,
Henrik

2013-07-18 17:18:59

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 26/51] Input: atmel_mxt_ts - Move input device init into separate function

On Thu, Jun 27, 2013 at 01:49:01PM +0100, Nick Dyer wrote:
> Signed-off-by: Nick Dyer <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 129 +++++++++++++++++-------------
> 1 file changed, 75 insertions(+), 54 deletions(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index 8632133..030ebc5 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -1728,73 +1728,39 @@ static int mxt_handle_pdata(struct mxt_data *data)
> return 0;
> }
>
> -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)
> {
> - struct mxt_data *data;
> + struct device *dev = &data->client->dev;
> + const struct mxt_platform_data *pdata = data->pdata;

Similar code was removed from mxt_initialize(), some consistency would be nice.

> struct input_dev *input_dev;
> int error;
> unsigned int num_mt_slots;
> unsigned int mt_flags = 0;
> int i;
>
> - 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->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(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_pdata;
> - }
> -
> - disable_irq(client->irq);
> -
> - error = mxt_initialize(data);
> - if (error)
> - goto err_free_irq;
> -
> __set_bit(EV_ABS, input_dev->evbit);
> - __set_bit(EV_KEY, input_dev->evbit);
> - __set_bit(BTN_TOUCH, input_dev->keybit);
> + input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
>
> - if (data->pdata->t19_num_keys) {
> + if (pdata->t19_num_keys) {
> __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
>
> - for (i = 0; i < data->pdata->t19_num_keys; i++)
> - if (data->pdata->t19_keymap[i] != KEY_RESERVED)
> + for (i = 0; i < pdata->t19_num_keys; i++)
> + if (pdata->t19_keymap[i] != KEY_RESERVED)
> input_set_capability(input_dev, EV_KEY,
> - data->pdata->t19_keymap[i]);
> + pdata->t19_keymap[i]);
>
> mt_flags |= INPUT_MT_POINTER;
>
> @@ -1819,8 +1785,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,
> @@ -1834,11 +1803,64 @@ static int mxt_probe(struct i2c_client *client,
>
> error = input_register_device(input_dev);
> if (error) {
> - dev_err(&client->dev, "Error %d registering input device\n",
> - error);
> - goto err_free_object;
> + 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;
> + int error;
> +
> + 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->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(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_pdata;
> + }
> +
> + disable_irq(data->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",
> @@ -1849,8 +1871,8 @@ 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:
> @@ -1859,7 +1881,6 @@ err_free_pdata:
> if (!dev_get_platdata(&data->client->dev))
> kfree(data->pdata);
> err_free_mem:
> - input_free_device(input_dev);
> kfree(data);
> return error;
> }
> --
> 1.7.10.4
>

In what way does this patch simplify things?

Thanks,
Henrik

2013-07-18 17:19:32

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 37/51] Input: atmel_mxt_ts - Implement vector/orientation support

On Thu, Jun 27, 2013 at 01:49:12PM +0100, Nick Dyer wrote:
> 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.

How does this compare to the input mt documentation?

>
> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> 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 1c5e640..9188cf7 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -716,6 +716,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
> int y;
> int area;
> int amplitude;
> + u8 vector;
>
> /* do not report events if input device not yet registered */
> if (!data->enable_reporting)
> @@ -734,9 +735,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' : '.',
> @@ -746,7 +748,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);
>
> @@ -766,6 +768,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);
> @@ -2100,6 +2103,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);
>
> --
> 1.7.10.4
>

Thanks,
Henrik

2013-07-18 17:20:47

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 39/51] Input: atmel_mxt_ts - Implement T63 Active Stylus support

On Thu, Jun 27, 2013 at 01:49:14PM +0100, Nick Dyer wrote:
> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 95 +++++++++++++++++++++++++++++-
> 1 file changed, 94 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index b63f227..2e6118a 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;
> @@ -823,6 +840,63 @@ 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;
> +
> + /* do not report events if input device not yet registered */
> + if (!data->enable_reporting)
> + return;
> +
> + /* 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 +912,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 if (report_id >= data->T42_reportid_min
> && report_id <= data->T42_reportid_max) {
> mxt_proc_t42_messages(data, message);
> @@ -1483,6 +1560,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;
> }
>
> @@ -1563,6 +1642,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
> @@ -2103,7 +2188,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);
> @@ -2121,6 +2206,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);
> --
> 1.7.10.4
>

Reviewed-by: Henrik Rydberg <[email protected]>

Thanks,
Henrik

2013-07-18 17:22:28

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 44/51] Input: atmel_mxt_ts - Handle reports from T47 Stylus object

On Thu, Jun 27, 2013 at 01:49:19PM +0100, Nick Dyer wrote:
> Signed-off-by: Nick Dyer <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 16 +++++++++++++++-
> 1 file changed, 15 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index ee39683..ceb090a 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
> +

Ok - normally, creating dummy values is not recommended, but for the
shared touch/stylys implementation there is obviously no alternative.

> /* T63 Stylus */
> #define MXT_T63_STYLUS_PRESS (1 << 0)
> #define MXT_T63_STYLUS_RELEASE (1 << 1)
> @@ -696,6 +699,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
> int area;
> int amplitude;
> u8 vector;
> + int tool;
>
> /* do not report events if input device not yet registered */
> if (!data->enable_reporting)
> @@ -713,6 +717,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
> y >>= 2;
>
> area = message[5];
> +
> amplitude = message[6];
> vector = message[7];
>
> @@ -741,8 +746,17 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
> mxt_input_sync(input_dev);
> }
>
> + /* A reported size of zero indicates that the reported touch
> + * is a stylus from a linked Stylus T47 object. */
> + 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);
> --
> 1.7.10.4
>

Reviewed-by: Henrik Rydberg <[email protected]>

Thanks,
Henrik

2013-07-18 17:28:58

by Henrik Rydberg

[permalink] [raw]
Subject: Re: [PATCH 45/51] Input: atmel_mxt_ts - Release touch state during suspend

On Thu, Jun 27, 2013 at 01:49:20PM +0100, Nick Dyer wrote:
> 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.

How long is the window of possible stray interrupts? Could this be
done with a small delay instead of keeping track of the suspend state?

> 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 <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> ---
> drivers/input/touchscreen/atmel_mxt_ts.c | 46 ++++++++++++++++++++++++++++++
> 1 file changed, 46 insertions(+)
>
> diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
> index ceb090a..b4ecd1d 100644
> --- a/drivers/input/touchscreen/atmel_mxt_ts.c
> +++ b/drivers/input/touchscreen/atmel_mxt_ts.c
> @@ -249,6 +249,9 @@ struct mxt_data {
>
> /* Enable reporting of input events */
> bool enable_reporting;
> +
> + /* Indicates whether device is in suspend */
> + bool suspended;
> };
>
> static inline size_t mxt_obj_size(const struct mxt_object *obj)
> @@ -2025,6 +2028,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;
> @@ -2137,6 +2145,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;
> @@ -2162,17 +2172,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->enable_reporting = true;
> + data->suspended = false;
> }
>
> static void mxt_stop(struct mxt_data *data)
> {
> + if (data->suspended || data->in_bootloader)
> + return;
> +
> + data->enable_reporting = false;
> + 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)
> --
> 1.7.10.4
>

Reviewed-by: Henrik Rydberg <[email protected]>

Thanks,
Henrik

2013-07-18 19:46:31

by Henrik Rydberg

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

Hi Nick,

First: thanks for the patches and you work on this driver.

Now, I don't swear much, but I would like to emphasize line 161 of
Documentation/SubmittingPatches:

**Do not send more than 15 patches at once to the vger mailing lists!!!***

One reason that should be obvious by now is that your work will be
attended to much quicker. One may think that it is more efficient to
send the whole backlog at once, but in fact, the time it takes to get
a patchset accepted is inversely proportional to the length of the
patchset. So please, keep it small and simple next time,

Thanks,
Henrik

2013-08-15 15:52:48

by Nick Dyer

[permalink] [raw]
Subject: Re: [PATCH 45/51] Input: atmel_mxt_ts - Release touch state during suspend

[email protected] wrote:
> On Thu, Jun 27, 2013 at 01:49:20PM +0100, Nick Dyer wrote:
>> 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.
>
> How long is the window of possible stray interrupts? Could this be
> done with a small delay instead of keeping track of the suspend state?

The touch controller has its own acquisition scheduling which switches
between active/idle modes. Which mode it is in isn't explicitly
communicated to the driver.

If we could tell it was in active mode (which would involve some hairier
code than this patch involves), we might have to wait ~20ms on a modern
device. But the worst case is that we would have to wait for the idle scan
interval plus some margin, so a couple of hundred ms. I think that is too
long, right?

2013-08-15 15:56:00

by Nick Dyer

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

[email protected] wrote:
> First: thanks for the patches and you work on this driver.

Thank you for your time in looking at these changes.

> Now, I don't swear much, but I would like to emphasize line 161 of
> Documentation/SubmittingPatches:
>
> **Do not send more than 15 patches at once to the vger mailing lists!!!***
>
> One reason that should be obvious by now is that your work will be
> attended to much quicker. One may think that it is more efficient to
> send the whole backlog at once, but in fact, the time it takes to get
> a patchset accepted is inversely proportional to the length of the
> patchset. So please, keep it small and simple next time,

Apologies. I will split this lot up into several smaller series of patches.

2013-08-15 16:18:56

by Nick Dyer

[permalink] [raw]
Subject: Re: [PATCH 37/51] Input: atmel_mxt_ts - Implement vector/orientation support

[email protected] wrote:
> On Thu, Jun 27, 2013 at 01:49:12PM +0100, Nick Dyer wrote:
>> 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

Except they've changed the URL, should be:
https://source.android.com/devices/tech/input/touch-devices.html#touchorientationcalibration

The Atmel format is two 4-bit signed values packed into 1 byte, you use
inverse tan to work out the angle, and pythagoras theorem to work out the
magnitude of the vector (giving a confidence level)

>> 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.
>
> How does this compare to the input mt documentation?

http://lxr.free-electrons.com/source/Documentation/input/multi-touch-protocol.txt#L263

So yes, we don't meet the documented format. Options:
1. Leave out this patch entirely and support out of tree
2. Update multi-touch-protocol.txt to include the Atmel format
3. Convert in driver to match documented protocol. Presumably via a LUT of
the 256 possible values. Although this loses the confidence level that is
implied.
4. Getting the firmware changed is not a valid option I'm afraid (too many
devices already out there).

Which do you prefer (I suspect 3) ?

2013-08-15 21:23:13

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

On Thu, Aug 15, 2013 at 04:55:57PM +0100, Nick Dyer wrote:
> [email protected] wrote:
> > First: thanks for the patches and you work on this driver.
>
> Thank you for your time in looking at these changes.
>
> > Now, I don't swear much, but I would like to emphasize line 161 of
> > Documentation/SubmittingPatches:
> >
> > **Do not send more than 15 patches at once to the vger mailing lists!!!***
> >
> > One reason that should be obvious by now is that your work will be
> > attended to much quicker. One may think that it is more efficient to
> > send the whole backlog at once, but in fact, the time it takes to get
> > a patchset accepted is inversely proportional to the length of the
> > patchset. So please, keep it small and simple next time,
>
> Apologies. I will split this lot up into several smaller series of patches.

Wait, I am in the process of applying it actually...

--
Dmitry

2013-08-19 14:25:05

by Nick Dyer

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

Dmitry Torokhov wrote:
> On Thu, Aug 15, 2013 at 04:55:57PM +0100, Nick Dyer wrote:
>> [email protected] wrote:
>>> **Do not send more than 15 patches at once to the vger mailing lists!!!***
>>>
>>> One reason that should be obvious by now is that your work will be
>>> attended to much quicker. One may think that it is more efficient to
>>> send the whole backlog at once, but in fact, the time it takes to get
>>> a patchset accepted is inversely proportional to the length of the
>>> patchset. So please, keep it small and simple next time,
>>
>> Apologies. I will split this lot up into several smaller series of patches.
>
> Wait, I am in the process of applying it actually...

OK, will hang on for the moment. Let me know if you need anything from me.

2013-09-10 13:58:45

by Nick Dyer

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

Dmitry Torokhov wrote:
> On Thu, Aug 15, 2013 at 04:55:57PM +0100, Nick Dyer wrote:
>> [email protected] wrote:
>>> First: thanks for the patches and you work on this driver.
>>
>> Thank you for your time in looking at these changes.
>>
>>> Now, I don't swear much, but I would like to emphasize line 161 of
>>> Documentation/SubmittingPatches:
>>>
>>> **Do not send more than 15 patches at once to the vger mailing lists!!!***
>>>
>>> One reason that should be obvious by now is that your work will be
>>> attended to much quicker. One may think that it is more efficient to
>>> send the whole backlog at once, but in fact, the time it takes to get
>>> a patchset accepted is inversely proportional to the length of the
>>> patchset. So please, keep it small and simple next time,
>>
>> Apologies. I will split this lot up into several smaller series of patches.
>
> Wait, I am in the process of applying it actually...

I wonder, have you made any progress with this? Is there any way I can assist?

Thanks

--
Nick Dyer

2013-09-16 06:33:10

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH 20/51] Input: atmel_mxt_ts - Set default irqflags when there is no pdata

On Thu, Jul 18, 2013 at 07:17:44PM +0200, [email protected] wrote:
> Hi Nick,
>
> > From: Yufeng Shen <[email protected]>
> >
> > 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.

I think if there is no platform data we should use 0 as IRQ falgs and
assume that IRQ line is properly configured by the board code or via
device tree.

Thanks.

--
Dmitry

2013-09-18 17:08:26

by Martin Fuzzey

[permalink] [raw]
Subject: Re: [12/51] Input: atmel_mxt_ts - Download device config using firmware loader

On 27/06/13 14:48, Nick Dyer wrote:
> 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.

I am using the mxt-app tool (v1.13) from
git://github.com/atmel-maxtouch/obp-utils.git
However when an existing config is dumped using that tool's --save
option the
config CRC is always zero.

That means that with this patch the config is loaded every time unless the
dumped file is manually tweaked to set the correct CRC.

>
> ---
> - dev_dbg(dev, "No cfg data defined, skipping reg init\n");
> + ret = request_firmware(&cfg, MXT_CFG_NAME, dev);
>
When building the driver into the kernel this just hangs for 60 seconds
then fails.
Wouldn't it be better to use request_firmware_nowait() ?

2013-09-18 17:29:36

by Martin Fuzzey

[permalink] [raw]
Subject: Re: [11/51] Input: atmel_mxt_ts - Implement CRC check for configuration data

On 27/06/13 14:48, Nick Dyer wrote:
> 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 <[email protected]>
> Acked-by: Benson Leung <[email protected]>
> -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);
> }

Shouldn't this be u32?
It's losing data causing the checksums not to match for me.

2014-01-15 10:51:24

by Nick Dyer

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

Yufeng Shen wrote:
> On Tue, Sep 10, 2013 at 9:58 AM, Nick Dyer <[email protected]
> <mailto:[email protected]>> wrote:
>> Dmitry Torokhov wrote:
>> > On Thu, Aug 15, 2013 at 04:55:57PM +0100, Nick Dyer wrote:
>> >> [email protected] <mailto:[email protected]> wrote:
>> >>> First: thanks for the patches and you work on this driver.
>> >>
>> >> Thank you for your time in looking at these changes.
>> >>
>> >>> Now, I don't swear much, but I would like to emphasize line 161 of
>> >>> Documentation/SubmittingPatches:
>> >>>
>> >>> **Do not send more than 15 patches at once to the vger mailing
> lists!!!***
>> >>>
>> >>> One reason that should be obvious by now is that your work will be
>> >>> attended to much quicker. One may think that it is more efficient to
>> >>> send the whole backlog at once, but in fact, the time it takes to get
>> >>> a patchset accepted is inversely proportional to the length of the
>> >>> patchset. So please, keep it small and simple next time,
>> >>
>> >> Apologies. I will split this lot up into several smaller series of
> patches.
>> >
>> > Wait, I am in the process of applying it actually...
>>
>> I wonder, have you made any progress with this? Is there any way I can
>> assist?
>
> Hey Dimitry,
>
> What's the status on this ?
>
> I noticed that at /pub/scm/linux/kernel/git/dtor/input, branch
> atmel-mxt-ts has part of Nick's patches applied
> but not all. The upstream tree and Nick's tree
> https://github.com/ndyer/linux/commits/for-next are still
> significantly diverged.
>
> I am wondering any plan on your side to apply more patches that sent out by
> Nick ?
>
> We (Chromium authors) are planning on bringing our local tree of atmel
> driver to be as close as possible to
> upstream and trying to figure out what's the best tree to rebase against.

I was considering picking up the patches that Dimitry has already reviewed
and rebasing them against the current mainline to try and get things moving
again - would that be useful?

2014-01-17 20:02:04

by Nick Dyer

[permalink] [raw]
Subject: Re: Atmel updates to atmel_mxt_ts touch controller driver - v6

Yufeng Shen wrote:
> On Wed, Jan 15, 2014 at 5:44 AM, Nick Dyer <[email protected]
> <mailto:[email protected]>> wrote:
> > We (Chromium authors) are planning on bringing our local tree of atmel
> > driver to be as close as possible to
> > upstream and trying to figure out what's the best tree to rebase against.
>
> I was considering picking up the patches that Dimitry has already reviewed
> and rebasing them against the current mainline to try and get things moving
> again - would that be useful?
>
> That would certainly be useful. Please let me know the branch/tag name once
> you finish that.

I've rebased Dimitry's atmel-mxt-ts branch against his dtor/next tree. You
can find it at

https://github.com/ndyer/linux/tree/for-next-20140117-dtor-rebase

I needed to update the INIT_COMPLETION api. So far I have verified it
builds, will do some testing on it next week.

I will also need to port my previous patchset on top of this branch.