diff options
author | Nick Dyer <nick.dyer@itdev.co.uk> | 2014-07-23 15:49:04 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-07-23 17:42:14 -0400 |
commit | 9d8dc3e529a19e427fd379118acd132520935c5d (patch) | |
tree | 30fe040eede1891d31b2274b877ebe77c29d565e | |
parent | b9b05a89721f05e2db227283ec9f7af804830b01 (diff) |
Input: atmel_mxt_ts - implement T44 message handling
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>
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 191 |
1 files changed, 159 insertions, 32 deletions
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index 699de552a6d8..c6dfd0af6365 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c | |||
@@ -244,12 +244,15 @@ struct mxt_data { | |||
244 | unsigned int max_y; | 244 | unsigned int max_y; |
245 | bool in_bootloader; | 245 | bool in_bootloader; |
246 | u16 mem_size; | 246 | u16 mem_size; |
247 | u8 max_reportid; | ||
247 | u32 config_crc; | 248 | u32 config_crc; |
248 | u32 info_crc; | 249 | u32 info_crc; |
249 | u8 bootloader_addr; | 250 | u8 bootloader_addr; |
250 | u8 *msg_buf; | 251 | u8 *msg_buf; |
251 | u8 t6_status; | 252 | u8 t6_status; |
252 | bool update_input; | 253 | bool update_input; |
254 | u8 last_message_count; | ||
255 | u8 num_touchids; | ||
253 | 256 | ||
254 | /* Cached parameters from object table */ | 257 | /* Cached parameters from object table */ |
255 | u16 T5_address; | 258 | u16 T5_address; |
@@ -260,6 +263,7 @@ struct mxt_data { | |||
260 | u8 T9_reportid_min; | 263 | u8 T9_reportid_min; |
261 | u8 T9_reportid_max; | 264 | u8 T9_reportid_max; |
262 | u8 T19_reportid; | 265 | u8 T19_reportid; |
266 | u16 T44_address; | ||
263 | 267 | ||
264 | /* for fw update in bootloader */ | 268 | /* for fw update in bootloader */ |
265 | struct completion bl_completion; | 269 | struct completion bl_completion; |
@@ -795,30 +799,142 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message) | |||
795 | return 1; | 799 | return 1; |
796 | } | 800 | } |
797 | 801 | ||
798 | static int mxt_read_and_process_message(struct mxt_data *data) | 802 | static int mxt_read_and_process_messages(struct mxt_data *data, u8 count) |
799 | { | 803 | { |
800 | struct device *dev = &data->client->dev; | 804 | struct device *dev = &data->client->dev; |
801 | int ret; | 805 | int ret; |
806 | int i; | ||
807 | u8 num_valid = 0; | ||
808 | |||
809 | /* Safety check for msg_buf */ | ||
810 | if (count > data->max_reportid) | ||
811 | return -EINVAL; | ||
802 | 812 | ||
813 | /* Process remaining messages if necessary */ | ||
803 | ret = __mxt_read_reg(data->client, data->T5_address, | 814 | ret = __mxt_read_reg(data->client, data->T5_address, |
804 | data->T5_msg_size, data->msg_buf); | 815 | data->T5_msg_size * count, data->msg_buf); |
805 | if (ret) { | 816 | if (ret) { |
806 | dev_err(dev, "Error %d reading message\n", ret); | 817 | dev_err(dev, "Failed to read %u messages (%d)\n", count, ret); |
807 | return ret; | 818 | return ret; |
808 | } | 819 | } |
809 | 820 | ||
810 | return mxt_proc_message(data, data->msg_buf); | 821 | for (i = 0; i < count; i++) { |
822 | ret = mxt_proc_message(data, | ||
823 | data->msg_buf + data->T5_msg_size * i); | ||
824 | |||
825 | if (ret == 1) | ||
826 | num_valid++; | ||
827 | } | ||
828 | |||
829 | /* return number of messages read */ | ||
830 | return num_valid; | ||
811 | } | 831 | } |
812 | 832 | ||
813 | static irqreturn_t mxt_process_messages_until_invalid(struct mxt_data *data) | 833 | static irqreturn_t mxt_process_messages_t44(struct mxt_data *data) |
814 | { | 834 | { |
835 | struct device *dev = &data->client->dev; | ||
815 | int ret; | 836 | int ret; |
837 | u8 count, num_left; | ||
816 | 838 | ||
817 | do { | 839 | /* Read T44 and T5 together */ |
818 | ret = mxt_read_and_process_message(data); | 840 | ret = __mxt_read_reg(data->client, data->T44_address, |
841 | data->T5_msg_size + 1, data->msg_buf); | ||
842 | if (ret) { | ||
843 | dev_err(dev, "Failed to read T44 and T5 (%d)\n", ret); | ||
844 | return IRQ_NONE; | ||
845 | } | ||
846 | |||
847 | count = data->msg_buf[0]; | ||
848 | |||
849 | if (count == 0) { | ||
850 | dev_warn(dev, "Interrupt triggered but zero messages\n"); | ||
851 | return IRQ_NONE; | ||
852 | } else if (count > data->max_reportid) { | ||
853 | dev_err(dev, "T44 count %d exceeded max report id\n", count); | ||
854 | count = data->max_reportid; | ||
855 | } | ||
856 | |||
857 | /* Process first message */ | ||
858 | ret = mxt_proc_message(data, data->msg_buf + 1); | ||
859 | if (ret < 0) { | ||
860 | dev_warn(dev, "Unexpected invalid message\n"); | ||
861 | return IRQ_NONE; | ||
862 | } | ||
863 | |||
864 | num_left = count - 1; | ||
865 | |||
866 | /* Process remaining messages if necessary */ | ||
867 | if (num_left) { | ||
868 | ret = mxt_read_and_process_messages(data, num_left); | ||
819 | if (ret < 0) | 869 | if (ret < 0) |
870 | goto end; | ||
871 | else if (ret != num_left) | ||
872 | dev_warn(dev, "Unexpected invalid message\n"); | ||
873 | } | ||
874 | |||
875 | end: | ||
876 | if (data->update_input) { | ||
877 | mxt_input_sync(data); | ||
878 | data->update_input = false; | ||
879 | } | ||
880 | |||
881 | return IRQ_HANDLED; | ||
882 | } | ||
883 | |||
884 | static int mxt_process_messages_until_invalid(struct mxt_data *data) | ||
885 | { | ||
886 | struct device *dev = &data->client->dev; | ||
887 | int count, read; | ||
888 | u8 tries = 2; | ||
889 | |||
890 | count = data->max_reportid; | ||
891 | |||
892 | /* Read messages until we force an invalid */ | ||
893 | do { | ||
894 | read = mxt_read_and_process_messages(data, count); | ||
895 | if (read < count) | ||
896 | return 0; | ||
897 | } while (--tries); | ||
898 | |||
899 | if (data->update_input) { | ||
900 | mxt_input_sync(data); | ||
901 | data->update_input = false; | ||
902 | } | ||
903 | |||
904 | dev_err(dev, "CHG pin isn't cleared\n"); | ||
905 | return -EBUSY; | ||
906 | } | ||
907 | |||
908 | static irqreturn_t mxt_process_messages(struct mxt_data *data) | ||
909 | { | ||
910 | int total_handled, num_handled; | ||
911 | u8 count = data->last_message_count; | ||
912 | |||
913 | if (count < 1 || count > data->max_reportid) | ||
914 | count = 1; | ||
915 | |||
916 | /* include final invalid message */ | ||
917 | total_handled = mxt_read_and_process_messages(data, count + 1); | ||
918 | if (total_handled < 0) | ||
919 | return IRQ_NONE; | ||
920 | /* if there were invalid messages, then we are done */ | ||
921 | else if (total_handled <= count) | ||
922 | goto update_count; | ||
923 | |||
924 | /* keep reading two msgs until one is invalid or reportid limit */ | ||
925 | do { | ||
926 | num_handled = mxt_read_and_process_messages(data, 2); | ||
927 | if (num_handled < 0) | ||
820 | return IRQ_NONE; | 928 | return IRQ_NONE; |
821 | } while (ret > 0); | 929 | |
930 | total_handled += num_handled; | ||
931 | |||
932 | if (num_handled < 2) | ||
933 | break; | ||
934 | } while (total_handled < data->num_touchids); | ||
935 | |||
936 | update_count: | ||
937 | data->last_message_count = total_handled; | ||
822 | 938 | ||
823 | if (data->update_input) { | 939 | if (data->update_input) { |
824 | mxt_input_sync(data); | 940 | mxt_input_sync(data); |
@@ -841,7 +957,11 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id) | |||
841 | if (!data->object_table) | 957 | if (!data->object_table) |
842 | return IRQ_HANDLED; | 958 | return IRQ_HANDLED; |
843 | 959 | ||
844 | return mxt_process_messages_until_invalid(data); | 960 | if (data->T44_address) { |
961 | return mxt_process_messages_t44(data); | ||
962 | } else { | ||
963 | return mxt_process_messages(data); | ||
964 | } | ||
845 | } | 965 | } |
846 | 966 | ||
847 | static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, | 967 | static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, |
@@ -1214,32 +1334,13 @@ release: | |||
1214 | return ret; | 1334 | return ret; |
1215 | } | 1335 | } |
1216 | 1336 | ||
1217 | static int mxt_make_highchg(struct mxt_data *data) | ||
1218 | { | ||
1219 | struct device *dev = &data->client->dev; | ||
1220 | int count = 10; | ||
1221 | int ret; | ||
1222 | |||
1223 | /* Read messages until we force an invalid */ | ||
1224 | do { | ||
1225 | ret = mxt_read_and_process_message(data); | ||
1226 | if (ret == 0) | ||
1227 | return 0; | ||
1228 | else if (ret < 0) | ||
1229 | return ret; | ||
1230 | } while (--count); | ||
1231 | |||
1232 | dev_err(dev, "CHG pin isn't cleared\n"); | ||
1233 | return -EBUSY; | ||
1234 | } | ||
1235 | |||
1236 | static int mxt_acquire_irq(struct mxt_data *data) | 1337 | static int mxt_acquire_irq(struct mxt_data *data) |
1237 | { | 1338 | { |
1238 | int error; | 1339 | int error; |
1239 | 1340 | ||
1240 | enable_irq(data->irq); | 1341 | enable_irq(data->irq); |
1241 | 1342 | ||
1242 | error = mxt_make_highchg(data); | 1343 | error = mxt_process_messages_until_invalid(data); |
1243 | if (error) | 1344 | if (error) |
1244 | return error; | 1345 | return error; |
1245 | 1346 | ||
@@ -1276,6 +1377,8 @@ static void mxt_free_object_table(struct mxt_data *data) | |||
1276 | data->T9_reportid_min = 0; | 1377 | data->T9_reportid_min = 0; |
1277 | data->T9_reportid_max = 0; | 1378 | data->T9_reportid_max = 0; |
1278 | data->T19_reportid = 0; | 1379 | data->T19_reportid = 0; |
1380 | data->T44_address = 0; | ||
1381 | data->max_reportid = 0; | ||
1279 | } | 1382 | } |
1280 | 1383 | ||
1281 | static int mxt_get_object_table(struct mxt_data *data) | 1384 | static int mxt_get_object_table(struct mxt_data *data) |
@@ -1329,8 +1432,16 @@ static int mxt_get_object_table(struct mxt_data *data) | |||
1329 | 1432 | ||
1330 | switch (object->type) { | 1433 | switch (object->type) { |
1331 | case MXT_GEN_MESSAGE_T5: | 1434 | case MXT_GEN_MESSAGE_T5: |
1332 | /* CRC not enabled, therefore don't read last byte */ | 1435 | if (data->info.family_id == 0x80) { |
1333 | data->T5_msg_size = mxt_obj_size(object) - 1; | 1436 | /* |
1437 | * On mXT224 read and discard unused CRC byte | ||
1438 | * otherwise DMA reads are misaligned | ||
1439 | */ | ||
1440 | data->T5_msg_size = mxt_obj_size(object); | ||
1441 | } else { | ||
1442 | /* CRC not enabled, so skip last byte */ | ||
1443 | data->T5_msg_size = mxt_obj_size(object) - 1; | ||
1444 | } | ||
1334 | data->T5_address = object->start_address; | 1445 | data->T5_address = object->start_address; |
1335 | case MXT_GEN_COMMAND_T6: | 1446 | case MXT_GEN_COMMAND_T6: |
1336 | data->T6_reportid = min_id; | 1447 | data->T6_reportid = min_id; |
@@ -1342,6 +1453,11 @@ static int mxt_get_object_table(struct mxt_data *data) | |||
1342 | case MXT_TOUCH_MULTI_T9: | 1453 | case MXT_TOUCH_MULTI_T9: |
1343 | data->T9_reportid_min = min_id; | 1454 | data->T9_reportid_min = min_id; |
1344 | data->T9_reportid_max = max_id; | 1455 | data->T9_reportid_max = max_id; |
1456 | data->num_touchids = object->num_report_ids | ||
1457 | * mxt_obj_instances(object); | ||
1458 | break; | ||
1459 | case MXT_SPT_MESSAGECOUNT_T44: | ||
1460 | data->T44_address = object->start_address; | ||
1345 | break; | 1461 | break; |
1346 | case MXT_SPT_GPIOPWM_T19: | 1462 | case MXT_SPT_GPIOPWM_T19: |
1347 | data->T19_reportid = min_id; | 1463 | data->T19_reportid = min_id; |
@@ -1355,7 +1471,18 @@ static int mxt_get_object_table(struct mxt_data *data) | |||
1355 | data->mem_size = end_address + 1; | 1471 | data->mem_size = end_address + 1; |
1356 | } | 1472 | } |
1357 | 1473 | ||
1358 | data->msg_buf = kzalloc(data->T5_msg_size, GFP_KERNEL); | 1474 | /* Store maximum reportid */ |
1475 | data->max_reportid = reportid; | ||
1476 | |||
1477 | /* If T44 exists, T5 position has to be directly after */ | ||
1478 | if (data->T44_address && (data->T5_address != data->T44_address + 1)) { | ||
1479 | dev_err(&client->dev, "Invalid T44 position\n"); | ||
1480 | error = -EINVAL; | ||
1481 | goto free_object_table; | ||
1482 | } | ||
1483 | |||
1484 | data->msg_buf = kcalloc(data->max_reportid, | ||
1485 | data->T5_msg_size, GFP_KERNEL); | ||
1359 | if (!data->msg_buf) { | 1486 | if (!data->msg_buf) { |
1360 | dev_err(&client->dev, "Failed to allocate message buffer\n"); | 1487 | dev_err(&client->dev, "Failed to allocate message buffer\n"); |
1361 | error = -ENOMEM; | 1488 | error = -ENOMEM; |