Input: atmel_mxt_ts - implement T44 message handling
authorNick Dyer <nick.dyer@itdev.co.uk>
Wed, 23 Jul 2014 19:49:04 +0000 (12:49 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 23 Jul 2014 21:42:14 +0000 (14:42 -0700)
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@itdev.co.uk>
Acked-by: Benson Leung <bleung@chromium.org>
Acked-by: Yufeng Shen <miletus@chromium.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/touchscreen/atmel_mxt_ts.c

index 699de552a6d8abe47c9a9930dad0c75da2c8616e..c6dfd0af6365163cb0035f83b281d23728ce7bf3 100644 (file)
@@ -244,12 +244,15 @@ 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;
        u8 *msg_buf;
        u8 t6_status;
        bool update_input;
+       u8 last_message_count;
+       u8 num_touchids;
 
        /* Cached parameters from object table */
        u16 T5_address;
@@ -260,6 +263,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;
@@ -795,30 +799,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);
@@ -841,7 +957,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,
@@ -1214,32 +1334,13 @@ release:
        return ret;
 }
 
-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;
 
@@ -1276,6 +1377,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)
@@ -1329,8 +1432,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;
@@ -1342,6 +1453,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;
@@ -1355,7 +1471,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;