Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9d8dc3e5 authored by Nick Dyer's avatar Nick Dyer Committed by Dmitry Torokhov
Browse files

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: default avatarNick Dyer <nick.dyer@itdev.co.uk>
Acked-by: default avatarBenson Leung <bleung@chromium.org>
Acked-by: default avatarYufeng Shen <miletus@chromium.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent b9b05a89
Loading
Loading
Loading
Loading
+159 −32
Original line number Diff line number Diff line
@@ -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;
	} while (ret > 0);
	/* 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;

		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 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
	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 */
			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;