xref: /openbmc/linux/drivers/input/touchscreen/exc3000.c (revision deae576455bbeb84a3a86bc359d322d66dfc841c)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27e577a17SAhmet Inan /*
37e577a17SAhmet Inan  * Driver for I2C connected EETI EXC3000 multiple touch controller
47e577a17SAhmet Inan  *
57e577a17SAhmet Inan  * Copyright (C) 2017 Ahmet Inan <inan@distec.de>
67e577a17SAhmet Inan  *
77e577a17SAhmet Inan  * minimal implementation based on egalax_ts.c and egalax_i2c.c
87e577a17SAhmet Inan  */
97e577a17SAhmet Inan 
107e577a17SAhmet Inan #include <linux/bitops.h>
117e577a17SAhmet Inan #include <linux/device.h>
127e577a17SAhmet Inan #include <linux/i2c.h>
137e577a17SAhmet Inan #include <linux/input.h>
147e577a17SAhmet Inan #include <linux/input/mt.h>
157e577a17SAhmet Inan #include <linux/input/touchscreen.h>
167e577a17SAhmet Inan #include <linux/interrupt.h>
177e577a17SAhmet Inan #include <linux/module.h>
187e577a17SAhmet Inan #include <linux/of.h>
197e577a17SAhmet Inan #include <linux/timer.h>
207e577a17SAhmet Inan #include <asm/unaligned.h>
217e577a17SAhmet Inan 
227e577a17SAhmet Inan #define EXC3000_NUM_SLOTS		10
237e577a17SAhmet Inan #define EXC3000_SLOTS_PER_FRAME		5
247e577a17SAhmet Inan #define EXC3000_LEN_FRAME		66
257e577a17SAhmet Inan #define EXC3000_LEN_POINT		10
267e577a17SAhmet Inan #define EXC3000_MT_EVENT		6
277e577a17SAhmet Inan #define EXC3000_TIMEOUT_MS		100
287e577a17SAhmet Inan 
297e577a17SAhmet Inan struct exc3000_data {
307e577a17SAhmet Inan 	struct i2c_client *client;
317e577a17SAhmet Inan 	struct input_dev *input;
327e577a17SAhmet Inan 	struct touchscreen_properties prop;
337e577a17SAhmet Inan 	struct timer_list timer;
347e577a17SAhmet Inan 	u8 buf[2 * EXC3000_LEN_FRAME];
357e577a17SAhmet Inan };
367e577a17SAhmet Inan 
377e577a17SAhmet Inan static void exc3000_report_slots(struct input_dev *input,
387e577a17SAhmet Inan 				 struct touchscreen_properties *prop,
397e577a17SAhmet Inan 				 const u8 *buf, int num)
407e577a17SAhmet Inan {
417e577a17SAhmet Inan 	for (; num--; buf += EXC3000_LEN_POINT) {
427e577a17SAhmet Inan 		if (buf[0] & BIT(0)) {
437e577a17SAhmet Inan 			input_mt_slot(input, buf[1]);
447e577a17SAhmet Inan 			input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
457e577a17SAhmet Inan 			touchscreen_report_pos(input, prop,
467e577a17SAhmet Inan 					       get_unaligned_le16(buf + 2),
477e577a17SAhmet Inan 					       get_unaligned_le16(buf + 4),
487e577a17SAhmet Inan 					       true);
497e577a17SAhmet Inan 		}
507e577a17SAhmet Inan 	}
517e577a17SAhmet Inan }
527e577a17SAhmet Inan 
537e577a17SAhmet Inan static void exc3000_timer(struct timer_list *t)
547e577a17SAhmet Inan {
557e577a17SAhmet Inan 	struct exc3000_data *data = from_timer(data, t, timer);
567e577a17SAhmet Inan 
577e577a17SAhmet Inan 	input_mt_sync_frame(data->input);
587e577a17SAhmet Inan 	input_sync(data->input);
597e577a17SAhmet Inan }
607e577a17SAhmet Inan 
617e577a17SAhmet Inan static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
627e577a17SAhmet Inan {
637e577a17SAhmet Inan 	int ret;
647e577a17SAhmet Inan 
657e577a17SAhmet Inan 	ret = i2c_master_send(client, "'", 2);
667e577a17SAhmet Inan 	if (ret < 0)
677e577a17SAhmet Inan 		return ret;
687e577a17SAhmet Inan 
697e577a17SAhmet Inan 	if (ret != 2)
707e577a17SAhmet Inan 		return -EIO;
717e577a17SAhmet Inan 
727e577a17SAhmet Inan 	ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME);
737e577a17SAhmet Inan 	if (ret < 0)
747e577a17SAhmet Inan 		return ret;
757e577a17SAhmet Inan 
767e577a17SAhmet Inan 	if (ret != EXC3000_LEN_FRAME)
777e577a17SAhmet Inan 		return -EIO;
787e577a17SAhmet Inan 
797e577a17SAhmet Inan 	if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME ||
807e577a17SAhmet Inan 			buf[2] != EXC3000_MT_EVENT)
817e577a17SAhmet Inan 		return -EINVAL;
827e577a17SAhmet Inan 
837e577a17SAhmet Inan 	return 0;
847e577a17SAhmet Inan }
857e577a17SAhmet Inan 
867e577a17SAhmet Inan static int exc3000_read_data(struct i2c_client *client,
877e577a17SAhmet Inan 			     u8 *buf, int *n_slots)
887e577a17SAhmet Inan {
897e577a17SAhmet Inan 	int error;
907e577a17SAhmet Inan 
917e577a17SAhmet Inan 	error = exc3000_read_frame(client, buf);
927e577a17SAhmet Inan 	if (error)
937e577a17SAhmet Inan 		return error;
947e577a17SAhmet Inan 
957e577a17SAhmet Inan 	*n_slots = buf[3];
967e577a17SAhmet Inan 	if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS)
977e577a17SAhmet Inan 		return -EINVAL;
987e577a17SAhmet Inan 
997e577a17SAhmet Inan 	if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
1007e577a17SAhmet Inan 		/* Read 2nd frame to get the rest of the contacts. */
1017e577a17SAhmet Inan 		error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME);
1027e577a17SAhmet Inan 		if (error)
1037e577a17SAhmet Inan 			return error;
1047e577a17SAhmet Inan 
1057e577a17SAhmet Inan 		/* 2nd chunk must have number of contacts set to 0. */
1067e577a17SAhmet Inan 		if (buf[EXC3000_LEN_FRAME + 3] != 0)
1077e577a17SAhmet Inan 			return -EINVAL;
1087e577a17SAhmet Inan 	}
1097e577a17SAhmet Inan 
1107e577a17SAhmet Inan 	return 0;
1117e577a17SAhmet Inan }
1127e577a17SAhmet Inan 
1137e577a17SAhmet Inan static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
1147e577a17SAhmet Inan {
1157e577a17SAhmet Inan 	struct exc3000_data *data = dev_id;
1167e577a17SAhmet Inan 	struct input_dev *input = data->input;
1177e577a17SAhmet Inan 	u8 *buf = data->buf;
1187e577a17SAhmet Inan 	int slots, total_slots;
1197e577a17SAhmet Inan 	int error;
1207e577a17SAhmet Inan 
1217e577a17SAhmet Inan 	error = exc3000_read_data(data->client, buf, &total_slots);
1227e577a17SAhmet Inan 	if (error) {
1237e577a17SAhmet Inan 		/* Schedule a timer to release "stuck" contacts */
1247e577a17SAhmet Inan 		mod_timer(&data->timer,
1257e577a17SAhmet Inan 			  jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
1267e577a17SAhmet Inan 		goto out;
1277e577a17SAhmet Inan 	}
1287e577a17SAhmet Inan 
1297e577a17SAhmet Inan 	/*
1307e577a17SAhmet Inan 	 * We read full state successfully, no contacts will be "stuck".
1317e577a17SAhmet Inan 	 */
1327e577a17SAhmet Inan 	del_timer_sync(&data->timer);
1337e577a17SAhmet Inan 
1347e577a17SAhmet Inan 	while (total_slots > 0) {
1357e577a17SAhmet Inan 		slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
1367e577a17SAhmet Inan 		exc3000_report_slots(input, &data->prop, buf + 4, slots);
1377e577a17SAhmet Inan 		total_slots -= slots;
1387e577a17SAhmet Inan 		buf += EXC3000_LEN_FRAME;
1397e577a17SAhmet Inan 	}
1407e577a17SAhmet Inan 
1417e577a17SAhmet Inan 	input_mt_sync_frame(input);
1427e577a17SAhmet Inan 	input_sync(input);
1437e577a17SAhmet Inan 
1447e577a17SAhmet Inan out:
1457e577a17SAhmet Inan 	return IRQ_HANDLED;
1467e577a17SAhmet Inan }
1477e577a17SAhmet Inan 
148*deae5764SSebastian Reichel static int exc3000_probe(struct i2c_client *client)
1497e577a17SAhmet Inan {
1507e577a17SAhmet Inan 	struct exc3000_data *data;
1517e577a17SAhmet Inan 	struct input_dev *input;
1527e577a17SAhmet Inan 	int error;
1537e577a17SAhmet Inan 
1547e577a17SAhmet Inan 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
1557e577a17SAhmet Inan 	if (!data)
1567e577a17SAhmet Inan 		return -ENOMEM;
1577e577a17SAhmet Inan 
1587e577a17SAhmet Inan 	data->client = client;
1597e577a17SAhmet Inan 	timer_setup(&data->timer, exc3000_timer, 0);
1607e577a17SAhmet Inan 
1617e577a17SAhmet Inan 	input = devm_input_allocate_device(&client->dev);
1627e577a17SAhmet Inan 	if (!input)
1637e577a17SAhmet Inan 		return -ENOMEM;
1647e577a17SAhmet Inan 
1657e577a17SAhmet Inan 	data->input = input;
1667e577a17SAhmet Inan 
1677e577a17SAhmet Inan 	input->name = "EETI EXC3000 Touch Screen";
1687e577a17SAhmet Inan 	input->id.bustype = BUS_I2C;
1697e577a17SAhmet Inan 
1707e577a17SAhmet Inan 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0);
1717e577a17SAhmet Inan 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0);
1727e577a17SAhmet Inan 	touchscreen_parse_properties(input, true, &data->prop);
1737e577a17SAhmet Inan 
1747e577a17SAhmet Inan 	error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
1757e577a17SAhmet Inan 				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
1767e577a17SAhmet Inan 	if (error)
1777e577a17SAhmet Inan 		return error;
1787e577a17SAhmet Inan 
1797e577a17SAhmet Inan 	error = input_register_device(input);
1807e577a17SAhmet Inan 	if (error)
1817e577a17SAhmet Inan 		return error;
1827e577a17SAhmet Inan 
1837e577a17SAhmet Inan 	error = devm_request_threaded_irq(&client->dev, client->irq,
1847e577a17SAhmet Inan 					  NULL, exc3000_interrupt, IRQF_ONESHOT,
1857e577a17SAhmet Inan 					  client->name, data);
1867e577a17SAhmet Inan 	if (error)
1877e577a17SAhmet Inan 		return error;
1887e577a17SAhmet Inan 
1897e577a17SAhmet Inan 	return 0;
1907e577a17SAhmet Inan }
1917e577a17SAhmet Inan 
1927e577a17SAhmet Inan static const struct i2c_device_id exc3000_id[] = {
1937e577a17SAhmet Inan 	{ "exc3000", 0 },
1947e577a17SAhmet Inan 	{ }
1957e577a17SAhmet Inan };
1967e577a17SAhmet Inan MODULE_DEVICE_TABLE(i2c, exc3000_id);
1977e577a17SAhmet Inan 
1987e577a17SAhmet Inan #ifdef CONFIG_OF
1997e577a17SAhmet Inan static const struct of_device_id exc3000_of_match[] = {
2007e577a17SAhmet Inan 	{ .compatible = "eeti,exc3000" },
2017e577a17SAhmet Inan 	{ }
2027e577a17SAhmet Inan };
2037e577a17SAhmet Inan MODULE_DEVICE_TABLE(of, exc3000_of_match);
2047e577a17SAhmet Inan #endif
2057e577a17SAhmet Inan 
2067e577a17SAhmet Inan static struct i2c_driver exc3000_driver = {
2077e577a17SAhmet Inan 	.driver = {
2087e577a17SAhmet Inan 		.name	= "exc3000",
2097e577a17SAhmet Inan 		.of_match_table = of_match_ptr(exc3000_of_match),
2107e577a17SAhmet Inan 	},
2117e577a17SAhmet Inan 	.id_table	= exc3000_id,
212*deae5764SSebastian Reichel 	.probe_new	= exc3000_probe,
2137e577a17SAhmet Inan };
2147e577a17SAhmet Inan 
2157e577a17SAhmet Inan module_i2c_driver(exc3000_driver);
2167e577a17SAhmet Inan 
2177e577a17SAhmet Inan MODULE_AUTHOR("Ahmet Inan <inan@distec.de>");
2187e577a17SAhmet Inan MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver");
2197e577a17SAhmet Inan MODULE_LICENSE("GPL v2");
220