Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1/*
  2 * Driver for I2C connected EETI EXC3000 multiple touch controller
  3 *
  4 * Copyright (C) 2017 Ahmet Inan <inan@distec.de>
  5 *
  6 * minimal implementation based on egalax_ts.c and egalax_i2c.c
  7 *
  8 * This program is free software; you can redistribute it and/or modify
  9 * it under the terms of the GNU General Public License version 2 as
 10 * published by the Free Software Foundation.
 11 */
 12
 13#include <linux/bitops.h>
 14#include <linux/device.h>
 15#include <linux/i2c.h>
 16#include <linux/input.h>
 17#include <linux/input/mt.h>
 18#include <linux/input/touchscreen.h>
 19#include <linux/interrupt.h>
 20#include <linux/module.h>
 21#include <linux/of.h>
 22#include <linux/timer.h>
 23#include <asm/unaligned.h>
 24
 25#define EXC3000_NUM_SLOTS		10
 26#define EXC3000_SLOTS_PER_FRAME		5
 27#define EXC3000_LEN_FRAME		66
 28#define EXC3000_LEN_POINT		10
 29#define EXC3000_MT_EVENT		6
 30#define EXC3000_TIMEOUT_MS		100
 31
 32struct exc3000_data {
 33	struct i2c_client *client;
 34	struct input_dev *input;
 35	struct touchscreen_properties prop;
 36	struct timer_list timer;
 37	u8 buf[2 * EXC3000_LEN_FRAME];
 38};
 39
 40static void exc3000_report_slots(struct input_dev *input,
 41				 struct touchscreen_properties *prop,
 42				 const u8 *buf, int num)
 43{
 44	for (; num--; buf += EXC3000_LEN_POINT) {
 45		if (buf[0] & BIT(0)) {
 46			input_mt_slot(input, buf[1]);
 47			input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
 48			touchscreen_report_pos(input, prop,
 49					       get_unaligned_le16(buf + 2),
 50					       get_unaligned_le16(buf + 4),
 51					       true);
 52		}
 53	}
 54}
 55
 56static void exc3000_timer(struct timer_list *t)
 57{
 58	struct exc3000_data *data = from_timer(data, t, timer);
 59
 60	input_mt_sync_frame(data->input);
 61	input_sync(data->input);
 62}
 63
 64static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
 65{
 66	int ret;
 67
 68	ret = i2c_master_send(client, "'", 2);
 69	if (ret < 0)
 70		return ret;
 71
 72	if (ret != 2)
 73		return -EIO;
 74
 75	ret = i2c_master_recv(client, buf, EXC3000_LEN_FRAME);
 76	if (ret < 0)
 77		return ret;
 78
 79	if (ret != EXC3000_LEN_FRAME)
 80		return -EIO;
 81
 82	if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME ||
 83			buf[2] != EXC3000_MT_EVENT)
 84		return -EINVAL;
 85
 86	return 0;
 87}
 88
 89static int exc3000_read_data(struct i2c_client *client,
 90			     u8 *buf, int *n_slots)
 91{
 92	int error;
 93
 94	error = exc3000_read_frame(client, buf);
 95	if (error)
 96		return error;
 97
 98	*n_slots = buf[3];
 99	if (!*n_slots || *n_slots > EXC3000_NUM_SLOTS)
100		return -EINVAL;
101
102	if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
103		/* Read 2nd frame to get the rest of the contacts. */
104		error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME);
105		if (error)
106			return error;
107
108		/* 2nd chunk must have number of contacts set to 0. */
109		if (buf[EXC3000_LEN_FRAME + 3] != 0)
110			return -EINVAL;
111	}
112
113	return 0;
114}
115
116static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
117{
118	struct exc3000_data *data = dev_id;
119	struct input_dev *input = data->input;
120	u8 *buf = data->buf;
121	int slots, total_slots;
122	int error;
123
124	error = exc3000_read_data(data->client, buf, &total_slots);
125	if (error) {
126		/* Schedule a timer to release "stuck" contacts */
127		mod_timer(&data->timer,
128			  jiffies + msecs_to_jiffies(EXC3000_TIMEOUT_MS));
129		goto out;
130	}
131
132	/*
133	 * We read full state successfully, no contacts will be "stuck".
134	 */
135	del_timer_sync(&data->timer);
136
137	while (total_slots > 0) {
138		slots = min(total_slots, EXC3000_SLOTS_PER_FRAME);
139		exc3000_report_slots(input, &data->prop, buf + 4, slots);
140		total_slots -= slots;
141		buf += EXC3000_LEN_FRAME;
142	}
143
144	input_mt_sync_frame(input);
145	input_sync(input);
146
147out:
148	return IRQ_HANDLED;
149}
150
151static int exc3000_probe(struct i2c_client *client,
152			 const struct i2c_device_id *id)
153{
154	struct exc3000_data *data;
155	struct input_dev *input;
156	int error;
157
158	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
159	if (!data)
160		return -ENOMEM;
161
162	data->client = client;
163	timer_setup(&data->timer, exc3000_timer, 0);
164
165	input = devm_input_allocate_device(&client->dev);
166	if (!input)
167		return -ENOMEM;
168
169	data->input = input;
170
171	input->name = "EETI EXC3000 Touch Screen";
172	input->id.bustype = BUS_I2C;
173
174	input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0);
175	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0);
176	touchscreen_parse_properties(input, true, &data->prop);
177
178	error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
179				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
180	if (error)
181		return error;
182
183	error = input_register_device(input);
184	if (error)
185		return error;
186
187	error = devm_request_threaded_irq(&client->dev, client->irq,
188					  NULL, exc3000_interrupt, IRQF_ONESHOT,
189					  client->name, data);
190	if (error)
191		return error;
192
193	return 0;
194}
195
196static const struct i2c_device_id exc3000_id[] = {
197	{ "exc3000", 0 },
198	{ }
199};
200MODULE_DEVICE_TABLE(i2c, exc3000_id);
201
202#ifdef CONFIG_OF
203static const struct of_device_id exc3000_of_match[] = {
204	{ .compatible = "eeti,exc3000" },
205	{ }
206};
207MODULE_DEVICE_TABLE(of, exc3000_of_match);
208#endif
209
210static struct i2c_driver exc3000_driver = {
211	.driver = {
212		.name	= "exc3000",
213		.of_match_table = of_match_ptr(exc3000_of_match),
214	},
215	.id_table	= exc3000_id,
216	.probe		= exc3000_probe,
217};
218
219module_i2c_driver(exc3000_driver);
220
221MODULE_AUTHOR("Ahmet Inan <inan@distec.de>");
222MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver");
223MODULE_LICENSE("GPL v2");