mirror of
https://github.com/torvalds/linux.git
synced 2024-12-17 00:21:32 +00:00
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:
parent
b9b05a8972
commit
9d8dc3e529
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user