[PATCH 15/15] Input: atmel_mxt_ts - implement T44 message handling

From: nick . dyer
Date: Thu Jul 03 2014 - 11:13:26 EST


From: Nick Dyer <nick.dyer@xxxxxxxxxxx>

maXTouch chips allow the reading of multiple messages in a single I2C
transaction, which reduces bus overhead and improves performance/latency. 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>
Acked-by: Benson Leung <bleung@xxxxxxxxxxxx>
Acked-by: Yufeng Shen <miletus@xxxxxxxxxxxx>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 191 +++++++++++++++++++++++++------
1 file changed, 159 insertions(+), 32 deletions(-)

diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index c3eee0f..25cfbba 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -247,6 +247,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;
@@ -254,6 +255,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;
@@ -264,6 +267,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;
@@ -785,30 +789,142 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message)
return 1;
}

-static int mxt_read_and_process_message(struct mxt_data *data)
+static int mxt_read_and_process_messages(struct mxt_data *data, u8 count)
{
struct device *dev = &data->client->dev;
int ret;
+ int i;
+ u8 num_valid = 0;
+
+ /* Safety check for msg_buf */
+ if (count > data->max_reportid)
+ return -EINVAL;

+ /* Process remaining messages if necessary */
ret = __mxt_read_reg(data->client, data->T5_address,
- data->T5_msg_size, data->msg_buf);
+ data->T5_msg_size * count, data->msg_buf);
if (ret) {
- dev_err(dev, "Error %d reading message\n", ret);
+ dev_err(dev, "Failed to read %u messages (%d)\n", count, ret);
return ret;
}

- return mxt_proc_message(data, data->msg_buf);
+ for (i = 0; i < count; i++) {
+ ret = mxt_proc_message(data,
+ data->msg_buf + data->T5_msg_size * i);
+
+ if (ret == 1)
+ num_valid++;
+ }
+
+ /* return number of messages read */
+ return num_valid;
}

-static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data)
+static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
{
+ struct device *dev = &data->client->dev;
int ret;
+ u8 count, num_left;

- do {
- ret = mxt_read_and_process_message(data);
+ /* Read T44 and T5 together */
+ ret = __mxt_read_reg(data->client, data->T44_address,
+ data->T5_msg_size + 1, data->msg_buf);
+ if (ret) {
+ dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret);
+ return IRQ_NONE;
+ }
+
+ count = data->msg_buf[0];
+
+ if (count == 0) {
+ dev_warn(dev, "Interrupt triggered but zero messages\n");
+ return IRQ_NONE;
+ } else if (count > data->max_reportid) {
+ dev_err(dev, "T44 count %d exceeded max report id\n", count);
+ count = data->max_reportid;
+ }
+
+ /* Process first message */
+ ret = mxt_proc_message(data, data->msg_buf + 1);
+ if (ret < 0) {
+ dev_warn(dev, "Unexpected invalid message\n");
+ return IRQ_NONE;
+ }
+
+ num_left = count - 1;
+
+ /* Process remaining messages if necessary */
+ if (num_left) {
+ ret = mxt_read_and_process_messages(data, num_left);
if (ret < 0)
+ goto end;
+ else if (ret != num_left)
+ dev_warn(dev, "Unexpected invalid message\n");
+ }
+
+end:
+ if (data->update_input) {
+ mxt_input_sync(data);
+ 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);
+ data->update_input = false;
+ }
+
+ dev_err(dev, "CHG pin isn't cleared\n");
+ return -EBUSY;
+}
+
+static irqreturn_t mxt_process_messages(struct mxt_data *data)
+{
+ int total_handled, num_handled;
+ u8 count = data->last_message_count;
+
+ if (count < 1 || count > data->max_reportid)
+ count = 1;
+
+ /* include final invalid message */
+ total_handled = mxt_read_and_process_messages(data, count + 1);
+ if (total_handled < 0)
+ return IRQ_NONE;
+ /* if there were invalid messages, then we are done */
+ else if (total_handled <= count)
+ goto update_count;
+
+ /* keep reading two msgs until one is invalid or reportid limit */
+ do {
+ num_handled = mxt_read_and_process_messages(data, 2);
+ if (num_handled < 0)
return IRQ_NONE;
- } while (ret > 0);
+
+ total_handled += num_handled;
+
+ if (num_handled < 2)
+ break;
+ } while (total_handled < data->num_touchids);
+
+update_count:
+ data->last_message_count = total_handled;

if (data->update_input) {
mxt_input_sync(data);
@@ -831,7 +947,11 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
if (!data->object_table)
return IRQ_HANDLED;

- return mxt_process_messages_until_invalid(data);
+ if (data->T44_address) {
+ return mxt_process_messages_t44(data);
+ } else {
+ return mxt_process_messages(data);
+ }
}

static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset,
@@ -1258,32 +1378,13 @@ recheck:
return 0;
}

-static int mxt_make_highchg(struct mxt_data *data)
-{
- struct device *dev = &data->client->dev;
- int count = 10;
- int ret;
-
- /* Read messages until we force an invalid */
- do {
- ret = mxt_read_and_process_message(data);
- if (ret == 0)
- return 0;
- else if (ret < 0)
- return ret;
- } while (--count);
-
- dev_err(dev, "CHG pin isn't cleared\n");
- return -EBUSY;
-}
-
static int mxt_acquire_irq(struct mxt_data *data)
{
int error;

enable_irq(data->irq);

- error = mxt_make_highchg(data);
+ error = mxt_process_messages_until_invalid(data);
if (error)
return error;

@@ -1320,6 +1421,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)
@@ -1373,8 +1476,16 @@ static int mxt_get_object_table(struct mxt_data *data)

switch (object->type) {
case MXT_GEN_MESSAGE_T5:
- /* CRC not enabled, therefore don't read last byte */
- data->T5_msg_size = mxt_obj_size(object) - 1;
+ if (data->info.family_id == 0x80) {
+ /*
+ * On mXT224 read and discard unused CRC byte
+ * otherwise DMA reads are misaligned
+ */
+ data->T5_msg_size = mxt_obj_size(object);
+ } else {
+ /* CRC not enabled, so skip last byte */
+ data->T5_msg_size = mxt_obj_size(object) - 1;
+ }
data->T5_address = object->start_address;
case MXT_GEN_COMMAND_T6:
data->T6_reportid = min_id;
@@ -1386,6 +1497,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;
@@ -1399,7 +1515,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;
--
2.0.1

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