root/drivers/input/touchscreen/exc3000.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. exc3000_report_slots
  2. exc3000_timer
  3. exc3000_read_frame
  4. exc3000_read_data
  5. exc3000_interrupt
  6. exc3000_probe

   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 
  29 struct 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 
  37 static 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 
  53 static 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 
  61 static 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 
  86 static 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 
 113 static 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 
 144 out:
 145         return IRQ_HANDLED;
 146 }
 147 
 148 static 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 
 193 static const struct i2c_device_id exc3000_id[] = {
 194         { "exc3000", 0 },
 195         { }
 196 };
 197 MODULE_DEVICE_TABLE(i2c, exc3000_id);
 198 
 199 #ifdef CONFIG_OF
 200 static const struct of_device_id exc3000_of_match[] = {
 201         { .compatible = "eeti,exc3000" },
 202         { }
 203 };
 204 MODULE_DEVICE_TABLE(of, exc3000_of_match);
 205 #endif
 206 
 207 static 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 
 216 module_i2c_driver(exc3000_driver);
 217 
 218 MODULE_AUTHOR("Ahmet Inan <inan@distec.de>");
 219 MODULE_DESCRIPTION("I2C connected EETI EXC3000 multiple touch controller driver");
 220 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */