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>
This commit is contained in:
Nick Dyer 2014-07-23 12:49:04 -07:00 committed by Dmitry Torokhov
parent b9b05a8972
commit 9d8dc3e529
1 changed files with 159 additions and 32 deletions

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