xref: /openbmc/linux/drivers/input/touchscreen/exc3000.c (revision 3bdd21c6937a07f2877409bcdafc0ae2265fc981)
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>
19*3bdd21c6SSebastian Reichel #include <linux/sizes.h>
207e577a17SAhmet Inan #include <linux/timer.h>
217e577a17SAhmet Inan #include <asm/unaligned.h>
227e577a17SAhmet Inan 
237e577a17SAhmet Inan #define EXC3000_NUM_SLOTS		10
247e577a17SAhmet Inan #define EXC3000_SLOTS_PER_FRAME		5
257e577a17SAhmet Inan #define EXC3000_LEN_FRAME		66
267e577a17SAhmet Inan #define EXC3000_LEN_POINT		10
27*3bdd21c6SSebastian Reichel 
28*3bdd21c6SSebastian Reichel #define EXC3000_MT1_EVENT		0x06
29*3bdd21c6SSebastian Reichel #define EXC3000_MT2_EVENT		0x18
30*3bdd21c6SSebastian Reichel 
317e577a17SAhmet Inan #define EXC3000_TIMEOUT_MS		100
327e577a17SAhmet Inan 
33*3bdd21c6SSebastian Reichel static const struct i2c_device_id exc3000_id[];
34*3bdd21c6SSebastian Reichel 
35*3bdd21c6SSebastian Reichel struct eeti_dev_info {
36*3bdd21c6SSebastian Reichel 	const char *name;
37*3bdd21c6SSebastian Reichel 	int max_xy;
38*3bdd21c6SSebastian Reichel };
39*3bdd21c6SSebastian Reichel 
40*3bdd21c6SSebastian Reichel enum eeti_dev_id {
41*3bdd21c6SSebastian Reichel 	EETI_EXC3000,
42*3bdd21c6SSebastian Reichel 	EETI_EXC80H60,
43*3bdd21c6SSebastian Reichel 	EETI_EXC80H84,
44*3bdd21c6SSebastian Reichel };
45*3bdd21c6SSebastian Reichel 
46*3bdd21c6SSebastian Reichel static struct eeti_dev_info exc3000_info[] = {
47*3bdd21c6SSebastian Reichel 	[EETI_EXC3000] = {
48*3bdd21c6SSebastian Reichel 		.name = "EETI EXC3000 Touch Screen",
49*3bdd21c6SSebastian Reichel 		.max_xy = SZ_4K - 1,
50*3bdd21c6SSebastian Reichel 	},
51*3bdd21c6SSebastian Reichel 	[EETI_EXC80H60] = {
52*3bdd21c6SSebastian Reichel 		.name = "EETI EXC80H60 Touch Screen",
53*3bdd21c6SSebastian Reichel 		.max_xy = SZ_16K - 1,
54*3bdd21c6SSebastian Reichel 	},
55*3bdd21c6SSebastian Reichel 	[EETI_EXC80H84] = {
56*3bdd21c6SSebastian Reichel 		.name = "EETI EXC80H84 Touch Screen",
57*3bdd21c6SSebastian Reichel 		.max_xy = SZ_16K - 1,
58*3bdd21c6SSebastian Reichel 	},
59*3bdd21c6SSebastian Reichel };
60*3bdd21c6SSebastian Reichel 
617e577a17SAhmet Inan struct exc3000_data {
627e577a17SAhmet Inan 	struct i2c_client *client;
63*3bdd21c6SSebastian Reichel 	const struct eeti_dev_info *info;
647e577a17SAhmet Inan 	struct input_dev *input;
657e577a17SAhmet Inan 	struct touchscreen_properties prop;
667e577a17SAhmet Inan 	struct timer_list timer;
677e577a17SAhmet Inan 	u8 buf[2 * EXC3000_LEN_FRAME];
687e577a17SAhmet Inan };
697e577a17SAhmet Inan 
707e577a17SAhmet Inan static void exc3000_report_slots(struct input_dev *input,
717e577a17SAhmet Inan 				 struct touchscreen_properties *prop,
727e577a17SAhmet Inan 				 const u8 *buf, int num)
737e577a17SAhmet Inan {
747e577a17SAhmet Inan 	for (; num--; buf += EXC3000_LEN_POINT) {
757e577a17SAhmet Inan 		if (buf[0] & BIT(0)) {
767e577a17SAhmet Inan 			input_mt_slot(input, buf[1]);
777e577a17SAhmet Inan 			input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
787e577a17SAhmet Inan 			touchscreen_report_pos(input, prop,
797e577a17SAhmet Inan 					       get_unaligned_le16(buf + 2),
807e577a17SAhmet Inan 					       get_unaligned_le16(buf + 4),
817e577a17SAhmet Inan 					       true);
827e577a17SAhmet Inan 		}
837e577a17SAhmet Inan 	}
847e577a17SAhmet Inan }
857e577a17SAhmet Inan 
867e577a17SAhmet Inan static void exc3000_timer(struct timer_list *t)
877e577a17SAhmet Inan {
887e577a17SAhmet Inan 	struct exc3000_data *data = from_timer(data, t, timer);
897e577a17SAhmet Inan 
907e577a17SAhmet Inan 	input_mt_sync_frame(data->input);
917e577a17SAhmet Inan 	input_sync(data->input);
927e577a17SAhmet Inan }
937e577a17SAhmet Inan 
94*3bdd21c6SSebastian Reichel static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
957e577a17SAhmet Inan {
96*3bdd21c6SSebastian Reichel 	struct i2c_client *client = data->client;
97*3bdd21c6SSebastian Reichel 	u8 expected_event = EXC3000_MT1_EVENT;
987e577a17SAhmet Inan 	int ret;
997e577a17SAhmet Inan 
100*3bdd21c6SSebastian Reichel 	if (data->info->max_xy == SZ_16K - 1)
101*3bdd21c6SSebastian Reichel 		expected_event = EXC3000_MT2_EVENT;
102*3bdd21c6SSebastian Reichel 
1037e577a17SAhmet Inan 	ret = i2c_master_send(client, "'", 2);
1047e577a17SAhmet Inan 	if (ret < 0)
1057e577a17SAhmet Inan 		return ret;
1067e577a17SAhmet Inan 
1077e577a17SAhmet Inan 	if (ret != 2)
1087e577a17SAhmet Inan 		return -EIO;
1097e577a17SAhmet Inan 
1107e577a17SAhmet Inan 	ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME);
1117e577a17SAhmet Inan 	if (ret < 0)
1127e577a17SAhmet Inan 		return ret;
1137e577a17SAhmet Inan 
1147e577a17SAhmet Inan 	if (ret != EXC3000_LEN_FRAME)
1157e577a17SAhmet Inan 		return -EIO;
1167e577a17SAhmet Inan 
117*3bdd21c6SSebastian Reichel 	if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
118*3bdd21c6SSebastian Reichel 		return -EINVAL;
119*3bdd21c6SSebastian Reichel 
120*3bdd21c6SSebastian Reichel 	if (buf[2] != expected_event)
1217e577a17SAhmet Inan 		return -EINVAL;
1227e577a17SAhmet Inan 
1237e577a17SAhmet Inan 	return 0;
1247e577a17SAhmet Inan }
1257e577a17SAhmet Inan 
126*3bdd21c6SSebastian Reichel static int exc3000_read_data(struct exc3000_data *data,
1277e577a17SAhmet Inan 			     u8 *buf, int *n_slots)
1287e577a17SAhmet Inan {
1297e577a17SAhmet Inan 	int error;
1307e577a17SAhmet Inan 
131*3bdd21c6SSebastian Reichel 	error = exc3000_read_frame(data, buf);
1327e577a17SAhmet Inan 	if (error)
1337e577a17SAhmet Inan 		return error;
1347e577a17SAhmet Inan 
1357e577a17SAhmet Inan 	*n_slots = buf[3];
1367e577a17SAhmet Inan 	if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS)
1377e577a17SAhmet Inan 		return -EINVAL;
1387e577a17SAhmet Inan 
1397e577a17SAhmet Inan 	if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
1407e577a17SAhmet Inan 		/* Read 2nd frame to get the rest of the contacts. */
141*3bdd21c6SSebastian Reichel 		error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
1427e577a17SAhmet Inan 		if (error)
1437e577a17SAhmet Inan 			return error;
1447e577a17SAhmet Inan 
1457e577a17SAhmet Inan 		/* 2nd chunk must have number of contacts set to 0. */
1467e577a17SAhmet Inan 		if (buf[EXC3000_LEN_FRAME + 3] != 0)
1477e577a17SAhmet Inan 			return -EINVAL;
1487e577a17SAhmet Inan 	}
1497e577a17SAhmet Inan 
1507e577a17SAhmet Inan 	return 0;
1517e577a17SAhmet Inan }
1527e577a17SAhmet Inan 
1537e577a17SAhmet Inan static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
1547e577a17SAhmet Inan {
1557e577a17SAhmet Inan 	struct exc3000_data *data = dev_id;
1567e577a17SAhmet Inan 	struct input_dev *input = data->input;
1577e577a17SAhmet Inan 	u8 *buf = data->buf;
1587e577a17SAhmet Inan 	int slots, total_slots;
1597e577a17SAhmet Inan 	int error;
1607e577a17SAhmet Inan 
161*3bdd21c6SSebastian Reichel 	error = exc3000_read_data(data, buf, &total_slots);
1627e577a17SAhmet Inan 	if (error) {
1637e577a17SAhmet Inan 		/* Schedule a timer to release "stuck" contacts */
1647e577a17SAhmet Inan 		mod_timer(&data->timer,
1657e577a17SAhmet Inan 			  jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
1667e577a17SAhmet Inan 		goto out;
1677e577a17SAhmet Inan 	}
1687e577a17SAhmet Inan 
1697e577a17SAhmet Inan 	/*
1707e577a17SAhmet Inan 	 * We read full state successfully, no contacts will be "stuck".
1717e577a17SAhmet Inan 	 */
1727e577a17SAhmet Inan 	del_timer_sync(&data->timer);
1737e577a17SAhmet Inan 
1747e577a17SAhmet Inan 	while (total_slots > 0) {
1757e577a17SAhmet Inan 		slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
1767e577a17SAhmet Inan 		exc3000_report_slots(input, &data->prop, buf + 4, slots);
1777e577a17SAhmet Inan 		total_slots -= slots;
1787e577a17SAhmet Inan 		buf += EXC3000_LEN_FRAME;
1797e577a17SAhmet Inan 	}
1807e577a17SAhmet Inan 
1817e577a17SAhmet Inan 	input_mt_sync_frame(input);
1827e577a17SAhmet Inan 	input_sync(input);
1837e577a17SAhmet Inan 
1847e577a17SAhmet Inan out:
1857e577a17SAhmet Inan 	return IRQ_HANDLED;
1867e577a17SAhmet Inan }
1877e577a17SAhmet Inan 
188deae5764SSebastian Reichel static int exc3000_probe(struct i2c_client *client)
1897e577a17SAhmet Inan {
1907e577a17SAhmet Inan 	struct exc3000_data *data;
1917e577a17SAhmet Inan 	struct input_dev *input;
192*3bdd21c6SSebastian Reichel 	int error, max_xy;
1937e577a17SAhmet Inan 
1947e577a17SAhmet Inan 	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
1957e577a17SAhmet Inan 	if (!data)
1967e577a17SAhmet Inan 		return -ENOMEM;
1977e577a17SAhmet Inan 
1987e577a17SAhmet Inan 	data->client = client;
199*3bdd21c6SSebastian Reichel 	data->info = device_get_match_data(&client->dev);
200*3bdd21c6SSebastian Reichel 	if (!data->info) {
201*3bdd21c6SSebastian Reichel 		enum eeti_dev_id eeti_dev_id =
202*3bdd21c6SSebastian Reichel 			i2c_match_id(exc3000_id, client)->driver_data;
203*3bdd21c6SSebastian Reichel 		data->info = &exc3000_info[eeti_dev_id];
204*3bdd21c6SSebastian Reichel 	}
2057e577a17SAhmet Inan 	timer_setup(&data->timer, exc3000_timer, 0);
2067e577a17SAhmet Inan 
2077e577a17SAhmet Inan 	input = devm_input_allocate_device(&client->dev);
2087e577a17SAhmet Inan 	if (!input)
2097e577a17SAhmet Inan 		return -ENOMEM;
2107e577a17SAhmet Inan 
2117e577a17SAhmet Inan 	data->input = input;
2127e577a17SAhmet Inan 
213*3bdd21c6SSebastian Reichel 	input->name = data->info->name;
2147e577a17SAhmet Inan 	input->id.bustype = BUS_I2C;
2157e577a17SAhmet Inan 
216*3bdd21c6SSebastian Reichel 	max_xy = data->info->max_xy;
217*3bdd21c6SSebastian Reichel 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0);
218*3bdd21c6SSebastian Reichel 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
219*3bdd21c6SSebastian Reichel 
2207e577a17SAhmet Inan 	touchscreen_parse_properties(input, true, &data->prop);
2217e577a17SAhmet Inan 
2227e577a17SAhmet Inan 	error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
2237e577a17SAhmet Inan 				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
2247e577a17SAhmet Inan 	if (error)
2257e577a17SAhmet Inan 		return error;
2267e577a17SAhmet Inan 
2277e577a17SAhmet Inan 	error = input_register_device(input);
2287e577a17SAhmet Inan 	if (error)
2297e577a17SAhmet Inan 		return error;
2307e577a17SAhmet Inan 
2317e577a17SAhmet Inan 	error = devm_request_threaded_irq(&client->dev, client->irq,
2327e577a17SAhmet Inan 					  NULL, exc3000_interrupt, IRQF_ONESHOT,
2337e577a17SAhmet Inan 					  client->name, data);
2347e577a17SAhmet Inan 	if (error)
2357e577a17SAhmet Inan 		return error;
2367e577a17SAhmet Inan 
2377e577a17SAhmet Inan 	return 0;
2387e577a17SAhmet Inan }
2397e577a17SAhmet Inan 
2407e577a17SAhmet Inan static const struct i2c_device_id exc3000_id[] = {
241*3bdd21c6SSebastian Reichel 	{ "exc3000", EETI_EXC3000 },
242*3bdd21c6SSebastian Reichel 	{ "exc80h60", EETI_EXC80H60 },
243*3bdd21c6SSebastian Reichel 	{ "exc80h84", EETI_EXC80H84 },
2447e577a17SAhmet Inan 	{ }
2457e577a17SAhmet Inan };
2467e577a17SAhmet Inan MODULE_DEVICE_TABLE(i2c, exc3000_id);
2477e577a17SAhmet Inan 
2487e577a17SAhmet Inan #ifdef CONFIG_OF
2497e577a17SAhmet Inan static const struct of_device_id exc3000_of_match[] = {
250*3bdd21c6SSebastian Reichel 	{ .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] },
251*3bdd21c6SSebastian Reichel 	{ .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] },
252*3bdd21c6SSebastian Reichel 	{ .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] },
2537e577a17SAhmet Inan 	{ }
2547e577a17SAhmet Inan };
2557e577a17SAhmet Inan MODULE_DEVICE_TABLE(of, exc3000_of_match);
2567e577a17SAhmet Inan #endif
2577e577a17SAhmet Inan 
2587e577a17SAhmet Inan static struct i2c_driver exc3000_driver = {
2597e577a17SAhmet Inan 	.driver = {
2607e577a17SAhmet Inan 		.name	= "exc3000",
2617e577a17SAhmet Inan 		.of_match_table = of_match_ptr(exc3000_of_match),
2627e577a17SAhmet Inan 	},
2637e577a17SAhmet Inan 	.id_table	= exc3000_id,
264deae5764SSebastian Reichel 	.probe_new	= exc3000_probe,
2657e577a17SAhmet Inan };
2667e577a17SAhmet Inan 
2677e577a17SAhmet Inan module_i2c_driver(exc3000_driver);
2687e577a17SAhmet Inan 
2697e577a17SAhmet Inan MODULE_AUTHOR("Ahmet Inan <inan@distec.de>");
2707e577a17SAhmet Inan MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver");
2717e577a17SAhmet Inan MODULE_LICENSE("GPL v2");
272