[PATCH 21/40] Input: atmel_mxt_ts - Implement T44 message handling

From: Nick Dyer
Date: Fri Feb 22 2013 - 13:10:47 EST


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 <nick.dyer@xxxxxxxxxxx>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 188 ++++++++++++++++++++++++------
1 file changed, 155 insertions(+), 33 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 51901b7..238c11c 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -246,12 +246,15 @@ struct mxt_data {
unsigned int max_y;
struct bin_attribute mem_access_attr;
bool debug_enabled;
+ u8 max_reportid;
u32 config_crc;
u32 info_crc;
u8 bootloader_addr;
struct t7_config t7_cfg;
u8 *msg_buf;
bool update_input;
+ u8 last_message_count;
+ u8 num_touchids;

/* Cached parameters from object table */
u16 T5_address;
@@ -261,6 +264,7 @@ struct mxt_data {
u16 T7_address;
u8 T9_reportid_min;
u8 T9_reportid_max;
+ u16 T44_address;
};

/* I2C slave address pairs */
@@ -690,30 +694,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 exceeded max report id\n");
+ 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->update_input) {
mxt_input_sync(data->input_dev);
@@ -727,7 +844,10 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
struct mxt_data *data = dev_id;

- 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, u8 value, bool wait)
@@ -1129,25 +1249,6 @@ 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_get_info(struct mxt_data *data)
{
struct i2c_client *client = data->client;
@@ -1217,8 +1318,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 = OBP_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 = OBP_SIZE(object);
+ } else {
+ /* CRC not enabled, therefore don't read last byte */
+ data->T5_msg_size = OBP_SIZE(object) - 1;
+ }
data->T5_address = object->start_address;
case MXT_GEN_COMMAND_T6:
data->T6_reportid = min_id;
@@ -1230,6 +1337,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 * OBP_INSTANCES(object);
+ break;
+ case MXT_SPT_MESSAGECOUNT_T44:
+ data->T44_address = object->start_address;
break;
}

@@ -1240,7 +1352,17 @@ 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;
@@ -1577,7 +1699,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
if (data->state == APPMODE) {
enable_irq(data->irq);

- error = mxt_make_highchg(data);
+ error = mxt_process_messages_until_invalid(data);
if (error)
return error;
}
@@ -1826,7 +1948,7 @@ static int mxt_probe(struct i2c_client *client,
}

if (data->state == APPMODE) {
- error = mxt_make_highchg(data);
+ error = mxt_process_messages_until_invalid(data);
if (error)
goto err_free_irq;
}
--
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/