Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
fork

Configure Feed

Select the types of activity you want to include in your feed.

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