| .. | .. |
|---|
| 1 | | -#include <linux/module.h> |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 2 | +#include <linux/delay.h> |
|---|
| 3 | +#include <linux/gpio/consumer.h> |
|---|
| 2 | 4 | #include <linux/i2c.h> |
|---|
| 3 | | -#include <linux/interrupt.h> |
|---|
| 4 | | -#include <linux/slab.h> |
|---|
| 5 | 5 | #include <linux/input.h> |
|---|
| 6 | 6 | #include <linux/input/mt.h> |
|---|
| 7 | | -#include <linux/delay.h> |
|---|
| 8 | | -#include <linux/workqueue.h> |
|---|
| 9 | | -#include <linux/input/ili210x.h> |
|---|
| 7 | +#include <linux/input/touchscreen.h> |
|---|
| 8 | +#include <linux/interrupt.h> |
|---|
| 9 | +#include <linux/module.h> |
|---|
| 10 | +#include <linux/of_device.h> |
|---|
| 11 | +#include <linux/sizes.h> |
|---|
| 12 | +#include <linux/slab.h> |
|---|
| 13 | +#include <asm/unaligned.h> |
|---|
| 10 | 14 | |
|---|
| 11 | | -#define MAX_TOUCHES 2 |
|---|
| 12 | | -#define DEFAULT_POLL_PERIOD 20 |
|---|
| 15 | +#define ILI2XXX_POLL_PERIOD 20 |
|---|
| 16 | + |
|---|
| 17 | +#define ILI210X_DATA_SIZE 64 |
|---|
| 18 | +#define ILI211X_DATA_SIZE 43 |
|---|
| 19 | +#define ILI251X_DATA_SIZE1 31 |
|---|
| 20 | +#define ILI251X_DATA_SIZE2 20 |
|---|
| 13 | 21 | |
|---|
| 14 | 22 | /* Touchscreen commands */ |
|---|
| 15 | 23 | #define REG_TOUCHDATA 0x10 |
|---|
| 16 | 24 | #define REG_PANEL_INFO 0x20 |
|---|
| 17 | | -#define REG_FIRMWARE_VERSION 0x40 |
|---|
| 18 | 25 | #define REG_CALIBRATE 0xcc |
|---|
| 19 | 26 | |
|---|
| 20 | | -struct finger { |
|---|
| 21 | | - u8 x_low; |
|---|
| 22 | | - u8 x_high; |
|---|
| 23 | | - u8 y_low; |
|---|
| 24 | | - u8 y_high; |
|---|
| 25 | | -} __packed; |
|---|
| 26 | | - |
|---|
| 27 | | -struct touchdata { |
|---|
| 28 | | - u8 status; |
|---|
| 29 | | - struct finger finger[MAX_TOUCHES]; |
|---|
| 30 | | -} __packed; |
|---|
| 31 | | - |
|---|
| 32 | | -struct panel_info { |
|---|
| 33 | | - struct finger finger_max; |
|---|
| 34 | | - u8 xchannel_num; |
|---|
| 35 | | - u8 ychannel_num; |
|---|
| 36 | | -} __packed; |
|---|
| 37 | | - |
|---|
| 38 | | -struct firmware_version { |
|---|
| 39 | | - u8 id; |
|---|
| 40 | | - u8 major; |
|---|
| 41 | | - u8 minor; |
|---|
| 42 | | -} __packed; |
|---|
| 27 | +struct ili2xxx_chip { |
|---|
| 28 | + int (*read_reg)(struct i2c_client *client, u8 reg, |
|---|
| 29 | + void *buf, size_t len); |
|---|
| 30 | + int (*get_touch_data)(struct i2c_client *client, u8 *data); |
|---|
| 31 | + bool (*parse_touch_data)(const u8 *data, unsigned int finger, |
|---|
| 32 | + unsigned int *x, unsigned int *y, |
|---|
| 33 | + unsigned int *z); |
|---|
| 34 | + bool (*continue_polling)(const u8 *data, bool touch); |
|---|
| 35 | + unsigned int max_touches; |
|---|
| 36 | + unsigned int resolution; |
|---|
| 37 | + bool has_calibrate_reg; |
|---|
| 38 | + bool has_pressure_reg; |
|---|
| 39 | +}; |
|---|
| 43 | 40 | |
|---|
| 44 | 41 | struct ili210x { |
|---|
| 45 | 42 | struct i2c_client *client; |
|---|
| 46 | 43 | struct input_dev *input; |
|---|
| 47 | | - bool (*get_pendown_state)(void); |
|---|
| 48 | | - unsigned int poll_period; |
|---|
| 49 | | - struct delayed_work dwork; |
|---|
| 44 | + struct gpio_desc *reset_gpio; |
|---|
| 45 | + struct touchscreen_properties prop; |
|---|
| 46 | + const struct ili2xxx_chip *chip; |
|---|
| 47 | + bool stop; |
|---|
| 50 | 48 | }; |
|---|
| 51 | 49 | |
|---|
| 52 | | -static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, |
|---|
| 53 | | - size_t len) |
|---|
| 50 | +static int ili210x_read_reg(struct i2c_client *client, |
|---|
| 51 | + u8 reg, void *buf, size_t len) |
|---|
| 54 | 52 | { |
|---|
| 55 | | - struct i2c_msg msg[2] = { |
|---|
| 53 | + struct i2c_msg msg[] = { |
|---|
| 56 | 54 | { |
|---|
| 57 | 55 | .addr = client->addr, |
|---|
| 58 | 56 | .flags = 0, |
|---|
| .. | .. |
|---|
| 66 | 64 | .buf = buf, |
|---|
| 67 | 65 | } |
|---|
| 68 | 66 | }; |
|---|
| 67 | + int error, ret; |
|---|
| 69 | 68 | |
|---|
| 70 | | - if (i2c_transfer(client->adapter, msg, 2) != 2) { |
|---|
| 71 | | - dev_err(&client->dev, "i2c transfer failed\n"); |
|---|
| 69 | + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); |
|---|
| 70 | + if (ret != ARRAY_SIZE(msg)) { |
|---|
| 71 | + error = ret < 0 ? ret : -EIO; |
|---|
| 72 | + dev_err(&client->dev, "%s failed: %d\n", __func__, error); |
|---|
| 73 | + return error; |
|---|
| 74 | + } |
|---|
| 75 | + |
|---|
| 76 | + return 0; |
|---|
| 77 | +} |
|---|
| 78 | + |
|---|
| 79 | +static int ili210x_read_touch_data(struct i2c_client *client, u8 *data) |
|---|
| 80 | +{ |
|---|
| 81 | + return ili210x_read_reg(client, REG_TOUCHDATA, |
|---|
| 82 | + data, ILI210X_DATA_SIZE); |
|---|
| 83 | +} |
|---|
| 84 | + |
|---|
| 85 | +static bool ili210x_touchdata_to_coords(const u8 *touchdata, |
|---|
| 86 | + unsigned int finger, |
|---|
| 87 | + unsigned int *x, unsigned int *y, |
|---|
| 88 | + unsigned int *z) |
|---|
| 89 | +{ |
|---|
| 90 | + if (!(touchdata[0] & BIT(finger))) |
|---|
| 91 | + return false; |
|---|
| 92 | + |
|---|
| 93 | + *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); |
|---|
| 94 | + *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); |
|---|
| 95 | + |
|---|
| 96 | + return true; |
|---|
| 97 | +} |
|---|
| 98 | + |
|---|
| 99 | +static bool ili210x_check_continue_polling(const u8 *data, bool touch) |
|---|
| 100 | +{ |
|---|
| 101 | + return data[0] & 0xf3; |
|---|
| 102 | +} |
|---|
| 103 | + |
|---|
| 104 | +static const struct ili2xxx_chip ili210x_chip = { |
|---|
| 105 | + .read_reg = ili210x_read_reg, |
|---|
| 106 | + .get_touch_data = ili210x_read_touch_data, |
|---|
| 107 | + .parse_touch_data = ili210x_touchdata_to_coords, |
|---|
| 108 | + .continue_polling = ili210x_check_continue_polling, |
|---|
| 109 | + .max_touches = 2, |
|---|
| 110 | + .has_calibrate_reg = true, |
|---|
| 111 | +}; |
|---|
| 112 | + |
|---|
| 113 | +static int ili211x_read_touch_data(struct i2c_client *client, u8 *data) |
|---|
| 114 | +{ |
|---|
| 115 | + s16 sum = 0; |
|---|
| 116 | + int error; |
|---|
| 117 | + int ret; |
|---|
| 118 | + int i; |
|---|
| 119 | + |
|---|
| 120 | + ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE); |
|---|
| 121 | + if (ret != ILI211X_DATA_SIZE) { |
|---|
| 122 | + error = ret < 0 ? ret : -EIO; |
|---|
| 123 | + dev_err(&client->dev, "%s failed: %d\n", __func__, error); |
|---|
| 124 | + return error; |
|---|
| 125 | + } |
|---|
| 126 | + |
|---|
| 127 | + /* This chip uses custom checksum at the end of data */ |
|---|
| 128 | + for (i = 0; i < ILI211X_DATA_SIZE - 1; i++) |
|---|
| 129 | + sum = (sum + data[i]) & 0xff; |
|---|
| 130 | + |
|---|
| 131 | + if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) { |
|---|
| 132 | + dev_err(&client->dev, |
|---|
| 133 | + "CRC error (crc=0x%02x expected=0x%02x)\n", |
|---|
| 134 | + sum, data[ILI211X_DATA_SIZE - 1]); |
|---|
| 72 | 135 | return -EIO; |
|---|
| 73 | 136 | } |
|---|
| 74 | 137 | |
|---|
| 75 | 138 | return 0; |
|---|
| 76 | 139 | } |
|---|
| 77 | 140 | |
|---|
| 78 | | -static void ili210x_report_events(struct input_dev *input, |
|---|
| 79 | | - const struct touchdata *touchdata) |
|---|
| 141 | +static bool ili211x_touchdata_to_coords(const u8 *touchdata, |
|---|
| 142 | + unsigned int finger, |
|---|
| 143 | + unsigned int *x, unsigned int *y, |
|---|
| 144 | + unsigned int *z) |
|---|
| 80 | 145 | { |
|---|
| 146 | + u32 data; |
|---|
| 147 | + |
|---|
| 148 | + data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); |
|---|
| 149 | + if (data == 0xffffffff) /* Finger up */ |
|---|
| 150 | + return false; |
|---|
| 151 | + |
|---|
| 152 | + *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) | |
|---|
| 153 | + touchdata[1 + (finger * 4) + 1]; |
|---|
| 154 | + *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) | |
|---|
| 155 | + touchdata[1 + (finger * 4) + 2]; |
|---|
| 156 | + |
|---|
| 157 | + return true; |
|---|
| 158 | +} |
|---|
| 159 | + |
|---|
| 160 | +static bool ili211x_decline_polling(const u8 *data, bool touch) |
|---|
| 161 | +{ |
|---|
| 162 | + return false; |
|---|
| 163 | +} |
|---|
| 164 | + |
|---|
| 165 | +static const struct ili2xxx_chip ili211x_chip = { |
|---|
| 166 | + .read_reg = ili210x_read_reg, |
|---|
| 167 | + .get_touch_data = ili211x_read_touch_data, |
|---|
| 168 | + .parse_touch_data = ili211x_touchdata_to_coords, |
|---|
| 169 | + .continue_polling = ili211x_decline_polling, |
|---|
| 170 | + .max_touches = 10, |
|---|
| 171 | + .resolution = 2048, |
|---|
| 172 | +}; |
|---|
| 173 | + |
|---|
| 174 | +static bool ili212x_touchdata_to_coords(const u8 *touchdata, |
|---|
| 175 | + unsigned int finger, |
|---|
| 176 | + unsigned int *x, unsigned int *y, |
|---|
| 177 | + unsigned int *z) |
|---|
| 178 | +{ |
|---|
| 179 | + u16 val; |
|---|
| 180 | + |
|---|
| 181 | + val = get_unaligned_be16(touchdata + 3 + (finger * 5) + 0); |
|---|
| 182 | + if (!(val & BIT(15))) /* Touch indication */ |
|---|
| 183 | + return false; |
|---|
| 184 | + |
|---|
| 185 | + *x = val & 0x3fff; |
|---|
| 186 | + *y = get_unaligned_be16(touchdata + 3 + (finger * 5) + 2); |
|---|
| 187 | + |
|---|
| 188 | + return true; |
|---|
| 189 | +} |
|---|
| 190 | + |
|---|
| 191 | +static bool ili212x_check_continue_polling(const u8 *data, bool touch) |
|---|
| 192 | +{ |
|---|
| 193 | + return touch; |
|---|
| 194 | +} |
|---|
| 195 | + |
|---|
| 196 | +static const struct ili2xxx_chip ili212x_chip = { |
|---|
| 197 | + .read_reg = ili210x_read_reg, |
|---|
| 198 | + .get_touch_data = ili210x_read_touch_data, |
|---|
| 199 | + .parse_touch_data = ili212x_touchdata_to_coords, |
|---|
| 200 | + .continue_polling = ili212x_check_continue_polling, |
|---|
| 201 | + .max_touches = 10, |
|---|
| 202 | + .has_calibrate_reg = true, |
|---|
| 203 | +}; |
|---|
| 204 | + |
|---|
| 205 | +static int ili251x_read_reg(struct i2c_client *client, |
|---|
| 206 | + u8 reg, void *buf, size_t len) |
|---|
| 207 | +{ |
|---|
| 208 | + int error; |
|---|
| 209 | + int ret; |
|---|
| 210 | + |
|---|
| 211 | + ret = i2c_master_send(client, ®, 1); |
|---|
| 212 | + if (ret == 1) { |
|---|
| 213 | + usleep_range(5000, 5500); |
|---|
| 214 | + |
|---|
| 215 | + ret = i2c_master_recv(client, buf, len); |
|---|
| 216 | + if (ret == len) |
|---|
| 217 | + return 0; |
|---|
| 218 | + } |
|---|
| 219 | + |
|---|
| 220 | + error = ret < 0 ? ret : -EIO; |
|---|
| 221 | + dev_err(&client->dev, "%s failed: %d\n", __func__, error); |
|---|
| 222 | + return ret; |
|---|
| 223 | +} |
|---|
| 224 | + |
|---|
| 225 | +static int ili251x_read_touch_data(struct i2c_client *client, u8 *data) |
|---|
| 226 | +{ |
|---|
| 227 | + int error; |
|---|
| 228 | + |
|---|
| 229 | + error = ili251x_read_reg(client, REG_TOUCHDATA, |
|---|
| 230 | + data, ILI251X_DATA_SIZE1); |
|---|
| 231 | + if (!error && data[0] == 2) { |
|---|
| 232 | + error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1, |
|---|
| 233 | + ILI251X_DATA_SIZE2); |
|---|
| 234 | + if (error >= 0 && error != ILI251X_DATA_SIZE2) |
|---|
| 235 | + error = -EIO; |
|---|
| 236 | + } |
|---|
| 237 | + |
|---|
| 238 | + return error; |
|---|
| 239 | +} |
|---|
| 240 | + |
|---|
| 241 | +static bool ili251x_touchdata_to_coords(const u8 *touchdata, |
|---|
| 242 | + unsigned int finger, |
|---|
| 243 | + unsigned int *x, unsigned int *y, |
|---|
| 244 | + unsigned int *z) |
|---|
| 245 | +{ |
|---|
| 246 | + u16 val; |
|---|
| 247 | + |
|---|
| 248 | + val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); |
|---|
| 249 | + if (!(val & BIT(15))) /* Touch indication */ |
|---|
| 250 | + return false; |
|---|
| 251 | + |
|---|
| 252 | + *x = val & 0x3fff; |
|---|
| 253 | + *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); |
|---|
| 254 | + *z = touchdata[1 + (finger * 5) + 4]; |
|---|
| 255 | + |
|---|
| 256 | + return true; |
|---|
| 257 | +} |
|---|
| 258 | + |
|---|
| 259 | +static bool ili251x_check_continue_polling(const u8 *data, bool touch) |
|---|
| 260 | +{ |
|---|
| 261 | + return touch; |
|---|
| 262 | +} |
|---|
| 263 | + |
|---|
| 264 | +static const struct ili2xxx_chip ili251x_chip = { |
|---|
| 265 | + .read_reg = ili251x_read_reg, |
|---|
| 266 | + .get_touch_data = ili251x_read_touch_data, |
|---|
| 267 | + .parse_touch_data = ili251x_touchdata_to_coords, |
|---|
| 268 | + .continue_polling = ili251x_check_continue_polling, |
|---|
| 269 | + .max_touches = 10, |
|---|
| 270 | + .resolution = 16383, |
|---|
| 271 | + .has_calibrate_reg = true, |
|---|
| 272 | + .has_pressure_reg = true, |
|---|
| 273 | +}; |
|---|
| 274 | + |
|---|
| 275 | +static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) |
|---|
| 276 | +{ |
|---|
| 277 | + struct input_dev *input = priv->input; |
|---|
| 81 | 278 | int i; |
|---|
| 82 | | - bool touch; |
|---|
| 83 | | - unsigned int x, y; |
|---|
| 84 | | - const struct finger *finger; |
|---|
| 279 | + bool contact = false, touch; |
|---|
| 280 | + unsigned int x = 0, y = 0, z = 0; |
|---|
| 85 | 281 | |
|---|
| 86 | | - for (i = 0; i < MAX_TOUCHES; i++) { |
|---|
| 282 | + for (i = 0; i < priv->chip->max_touches; i++) { |
|---|
| 283 | + touch = priv->chip->parse_touch_data(touchdata, i, &x, &y, &z); |
|---|
| 284 | + |
|---|
| 87 | 285 | input_mt_slot(input, i); |
|---|
| 88 | | - |
|---|
| 89 | | - finger = &touchdata->finger[i]; |
|---|
| 90 | | - |
|---|
| 91 | | - touch = touchdata->status & (1 << i); |
|---|
| 92 | | - input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); |
|---|
| 93 | | - if (touch) { |
|---|
| 94 | | - x = finger->x_low | (finger->x_high << 8); |
|---|
| 95 | | - y = finger->y_low | (finger->y_high << 8); |
|---|
| 96 | | - |
|---|
| 97 | | - input_report_abs(input, ABS_MT_POSITION_X, x); |
|---|
| 98 | | - input_report_abs(input, ABS_MT_POSITION_Y, y); |
|---|
| 286 | + if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) { |
|---|
| 287 | + touchscreen_report_pos(input, &priv->prop, x, y, true); |
|---|
| 288 | + if (priv->chip->has_pressure_reg) |
|---|
| 289 | + input_report_abs(input, ABS_MT_PRESSURE, z); |
|---|
| 290 | + contact = true; |
|---|
| 99 | 291 | } |
|---|
| 100 | 292 | } |
|---|
| 101 | 293 | |
|---|
| 102 | 294 | input_mt_report_pointer_emulation(input, false); |
|---|
| 103 | 295 | input_sync(input); |
|---|
| 104 | | -} |
|---|
| 105 | 296 | |
|---|
| 106 | | -static bool get_pendown_state(const struct ili210x *priv) |
|---|
| 107 | | -{ |
|---|
| 108 | | - bool state = false; |
|---|
| 109 | | - |
|---|
| 110 | | - if (priv->get_pendown_state) |
|---|
| 111 | | - state = priv->get_pendown_state(); |
|---|
| 112 | | - |
|---|
| 113 | | - return state; |
|---|
| 114 | | -} |
|---|
| 115 | | - |
|---|
| 116 | | -static void ili210x_work(struct work_struct *work) |
|---|
| 117 | | -{ |
|---|
| 118 | | - struct ili210x *priv = container_of(work, struct ili210x, |
|---|
| 119 | | - dwork.work); |
|---|
| 120 | | - struct i2c_client *client = priv->client; |
|---|
| 121 | | - struct touchdata touchdata; |
|---|
| 122 | | - int error; |
|---|
| 123 | | - |
|---|
| 124 | | - error = ili210x_read_reg(client, REG_TOUCHDATA, |
|---|
| 125 | | - &touchdata, sizeof(touchdata)); |
|---|
| 126 | | - if (error) { |
|---|
| 127 | | - dev_err(&client->dev, |
|---|
| 128 | | - "Unable to get touchdata, err = %d\n", error); |
|---|
| 129 | | - return; |
|---|
| 130 | | - } |
|---|
| 131 | | - |
|---|
| 132 | | - ili210x_report_events(priv->input, &touchdata); |
|---|
| 133 | | - |
|---|
| 134 | | - if ((touchdata.status & 0xf3) || get_pendown_state(priv)) |
|---|
| 135 | | - schedule_delayed_work(&priv->dwork, |
|---|
| 136 | | - msecs_to_jiffies(priv->poll_period)); |
|---|
| 297 | + return contact; |
|---|
| 137 | 298 | } |
|---|
| 138 | 299 | |
|---|
| 139 | 300 | static irqreturn_t ili210x_irq(int irq, void *irq_data) |
|---|
| 140 | 301 | { |
|---|
| 141 | 302 | struct ili210x *priv = irq_data; |
|---|
| 303 | + struct i2c_client *client = priv->client; |
|---|
| 304 | + const struct ili2xxx_chip *chip = priv->chip; |
|---|
| 305 | + u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; |
|---|
| 306 | + bool keep_polling; |
|---|
| 307 | + bool touch; |
|---|
| 308 | + int error; |
|---|
| 142 | 309 | |
|---|
| 143 | | - schedule_delayed_work(&priv->dwork, 0); |
|---|
| 310 | + do { |
|---|
| 311 | + error = chip->get_touch_data(client, touchdata); |
|---|
| 312 | + if (error) { |
|---|
| 313 | + dev_err(&client->dev, |
|---|
| 314 | + "Unable to get touch data: %d\n", error); |
|---|
| 315 | + break; |
|---|
| 316 | + } |
|---|
| 317 | + |
|---|
| 318 | + touch = ili210x_report_events(priv, touchdata); |
|---|
| 319 | + keep_polling = chip->continue_polling(touchdata, touch); |
|---|
| 320 | + if (keep_polling) |
|---|
| 321 | + msleep(ILI2XXX_POLL_PERIOD); |
|---|
| 322 | + } while (!priv->stop && keep_polling); |
|---|
| 144 | 323 | |
|---|
| 145 | 324 | return IRQ_HANDLED; |
|---|
| 146 | 325 | } |
|---|
| .. | .. |
|---|
| 176 | 355 | NULL, |
|---|
| 177 | 356 | }; |
|---|
| 178 | 357 | |
|---|
| 358 | +static umode_t ili210x_calibrate_visible(struct kobject *kobj, |
|---|
| 359 | + struct attribute *attr, int index) |
|---|
| 360 | +{ |
|---|
| 361 | + struct device *dev = kobj_to_dev(kobj); |
|---|
| 362 | + struct i2c_client *client = to_i2c_client(dev); |
|---|
| 363 | + struct ili210x *priv = i2c_get_clientdata(client); |
|---|
| 364 | + |
|---|
| 365 | + return priv->chip->has_calibrate_reg ? attr->mode : 0; |
|---|
| 366 | +} |
|---|
| 367 | + |
|---|
| 179 | 368 | static const struct attribute_group ili210x_attr_group = { |
|---|
| 180 | 369 | .attrs = ili210x_attributes, |
|---|
| 370 | + .is_visible = ili210x_calibrate_visible, |
|---|
| 181 | 371 | }; |
|---|
| 182 | 372 | |
|---|
| 373 | +static void ili210x_power_down(void *data) |
|---|
| 374 | +{ |
|---|
| 375 | + struct gpio_desc *reset_gpio = data; |
|---|
| 376 | + |
|---|
| 377 | + gpiod_set_value_cansleep(reset_gpio, 1); |
|---|
| 378 | +} |
|---|
| 379 | + |
|---|
| 380 | +static void ili210x_stop(void *data) |
|---|
| 381 | +{ |
|---|
| 382 | + struct ili210x *priv = data; |
|---|
| 383 | + |
|---|
| 384 | + /* Tell ISR to quit even if there is a contact. */ |
|---|
| 385 | + priv->stop = true; |
|---|
| 386 | +} |
|---|
| 387 | + |
|---|
| 183 | 388 | static int ili210x_i2c_probe(struct i2c_client *client, |
|---|
| 184 | | - const struct i2c_device_id *id) |
|---|
| 389 | + const struct i2c_device_id *id) |
|---|
| 185 | 390 | { |
|---|
| 186 | 391 | struct device *dev = &client->dev; |
|---|
| 187 | | - const struct ili210x_platform_data *pdata = dev_get_platdata(dev); |
|---|
| 392 | + const struct ili2xxx_chip *chip; |
|---|
| 188 | 393 | struct ili210x *priv; |
|---|
| 394 | + struct gpio_desc *reset_gpio; |
|---|
| 189 | 395 | struct input_dev *input; |
|---|
| 190 | | - struct panel_info panel; |
|---|
| 191 | | - struct firmware_version firmware; |
|---|
| 192 | | - int xmax, ymax; |
|---|
| 193 | 396 | int error; |
|---|
| 397 | + unsigned int max_xy; |
|---|
| 194 | 398 | |
|---|
| 195 | 399 | dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); |
|---|
| 196 | 400 | |
|---|
| 197 | | - if (!pdata) { |
|---|
| 198 | | - dev_err(dev, "No platform data!\n"); |
|---|
| 199 | | - return -EINVAL; |
|---|
| 401 | + chip = device_get_match_data(dev); |
|---|
| 402 | + if (!chip && id) |
|---|
| 403 | + chip = (const struct ili2xxx_chip *)id->driver_data; |
|---|
| 404 | + if (!chip) { |
|---|
| 405 | + dev_err(&client->dev, "unknown device model\n"); |
|---|
| 406 | + return -ENODEV; |
|---|
| 200 | 407 | } |
|---|
| 201 | 408 | |
|---|
| 202 | 409 | if (client->irq <= 0) { |
|---|
| .. | .. |
|---|
| 204 | 411 | return -EINVAL; |
|---|
| 205 | 412 | } |
|---|
| 206 | 413 | |
|---|
| 207 | | - /* Get firmware version */ |
|---|
| 208 | | - error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, |
|---|
| 209 | | - &firmware, sizeof(firmware)); |
|---|
| 210 | | - if (error) { |
|---|
| 211 | | - dev_err(dev, "Failed to get firmware version, err: %d\n", |
|---|
| 212 | | - error); |
|---|
| 213 | | - return error; |
|---|
| 414 | + reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); |
|---|
| 415 | + if (IS_ERR(reset_gpio)) |
|---|
| 416 | + return PTR_ERR(reset_gpio); |
|---|
| 417 | + |
|---|
| 418 | + if (reset_gpio) { |
|---|
| 419 | + error = devm_add_action_or_reset(dev, ili210x_power_down, |
|---|
| 420 | + reset_gpio); |
|---|
| 421 | + if (error) |
|---|
| 422 | + return error; |
|---|
| 423 | + |
|---|
| 424 | + usleep_range(12000, 15000); |
|---|
| 425 | + gpiod_set_value_cansleep(reset_gpio, 0); |
|---|
| 426 | + msleep(160); |
|---|
| 214 | 427 | } |
|---|
| 215 | 428 | |
|---|
| 216 | | - /* get panel info */ |
|---|
| 217 | | - error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); |
|---|
| 218 | | - if (error) { |
|---|
| 219 | | - dev_err(dev, "Failed to get panel information, err: %d\n", |
|---|
| 220 | | - error); |
|---|
| 221 | | - return error; |
|---|
| 222 | | - } |
|---|
| 429 | + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
|---|
| 430 | + if (!priv) |
|---|
| 431 | + return -ENOMEM; |
|---|
| 223 | 432 | |
|---|
| 224 | | - xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); |
|---|
| 225 | | - ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); |
|---|
| 226 | | - |
|---|
| 227 | | - priv = kzalloc(sizeof(*priv), GFP_KERNEL); |
|---|
| 228 | | - input = input_allocate_device(); |
|---|
| 229 | | - if (!priv || !input) { |
|---|
| 230 | | - error = -ENOMEM; |
|---|
| 231 | | - goto err_free_mem; |
|---|
| 232 | | - } |
|---|
| 433 | + input = devm_input_allocate_device(dev); |
|---|
| 434 | + if (!input) |
|---|
| 435 | + return -ENOMEM; |
|---|
| 233 | 436 | |
|---|
| 234 | 437 | priv->client = client; |
|---|
| 235 | 438 | priv->input = input; |
|---|
| 236 | | - priv->get_pendown_state = pdata->get_pendown_state; |
|---|
| 237 | | - priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; |
|---|
| 238 | | - INIT_DELAYED_WORK(&priv->dwork, ili210x_work); |
|---|
| 439 | + priv->reset_gpio = reset_gpio; |
|---|
| 440 | + priv->chip = chip; |
|---|
| 441 | + i2c_set_clientdata(client, priv); |
|---|
| 239 | 442 | |
|---|
| 240 | 443 | /* Setup input device */ |
|---|
| 241 | 444 | input->name = "ILI210x Touchscreen"; |
|---|
| 445 | + input->phys = devm_kasprintf(dev, GFP_KERNEL, "ili210x-%s/input0", dev_name(dev)); |
|---|
| 242 | 446 | input->id.bustype = BUS_I2C; |
|---|
| 243 | | - input->dev.parent = dev; |
|---|
| 244 | | - |
|---|
| 245 | | - __set_bit(EV_SYN, input->evbit); |
|---|
| 246 | | - __set_bit(EV_KEY, input->evbit); |
|---|
| 247 | | - __set_bit(EV_ABS, input->evbit); |
|---|
| 248 | | - __set_bit(BTN_TOUCH, input->keybit); |
|---|
| 249 | | - |
|---|
| 250 | | - /* Single touch */ |
|---|
| 251 | | - input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); |
|---|
| 252 | | - input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); |
|---|
| 253 | 447 | |
|---|
| 254 | 448 | /* Multi touch */ |
|---|
| 255 | | - input_mt_init_slots(input, MAX_TOUCHES, 0); |
|---|
| 256 | | - input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); |
|---|
| 257 | | - input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); |
|---|
| 449 | + max_xy = (chip->resolution ?: SZ_64K) - 1; |
|---|
| 450 | + input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); |
|---|
| 451 | + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); |
|---|
| 452 | + if (priv->chip->has_pressure_reg) |
|---|
| 453 | + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xa, 0, 0); |
|---|
| 454 | + touchscreen_parse_properties(input, true, &priv->prop); |
|---|
| 258 | 455 | |
|---|
| 259 | | - i2c_set_clientdata(client, priv); |
|---|
| 456 | + error = input_mt_init_slots(input, priv->chip->max_touches, |
|---|
| 457 | + INPUT_MT_DIRECT); |
|---|
| 458 | + if (error) { |
|---|
| 459 | + dev_err(dev, "Unable to set up slots, err: %d\n", error); |
|---|
| 460 | + return error; |
|---|
| 461 | + } |
|---|
| 260 | 462 | |
|---|
| 261 | | - error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, |
|---|
| 262 | | - client->name, priv); |
|---|
| 463 | + error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, |
|---|
| 464 | + IRQF_ONESHOT, client->name, priv); |
|---|
| 263 | 465 | if (error) { |
|---|
| 264 | 466 | dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", |
|---|
| 265 | 467 | error); |
|---|
| 266 | | - goto err_free_mem; |
|---|
| 468 | + return error; |
|---|
| 267 | 469 | } |
|---|
| 268 | 470 | |
|---|
| 269 | | - error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); |
|---|
| 471 | + error = devm_add_action_or_reset(dev, ili210x_stop, priv); |
|---|
| 472 | + if (error) |
|---|
| 473 | + return error; |
|---|
| 474 | + |
|---|
| 475 | + error = devm_device_add_group(dev, &ili210x_attr_group); |
|---|
| 270 | 476 | if (error) { |
|---|
| 271 | 477 | dev_err(dev, "Unable to create sysfs attributes, err: %d\n", |
|---|
| 272 | 478 | error); |
|---|
| 273 | | - goto err_free_irq; |
|---|
| 479 | + return error; |
|---|
| 274 | 480 | } |
|---|
| 275 | 481 | |
|---|
| 276 | 482 | error = input_register_device(priv->input); |
|---|
| 277 | 483 | if (error) { |
|---|
| 278 | 484 | dev_err(dev, "Cannot register input device, err: %d\n", error); |
|---|
| 279 | | - goto err_remove_sysfs; |
|---|
| 485 | + return error; |
|---|
| 280 | 486 | } |
|---|
| 281 | 487 | |
|---|
| 282 | | - device_init_wakeup(dev, 1); |
|---|
| 283 | | - |
|---|
| 284 | | - dev_dbg(dev, |
|---|
| 285 | | - "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", |
|---|
| 286 | | - client->irq, firmware.id, firmware.major, firmware.minor); |
|---|
| 287 | | - |
|---|
| 288 | | - return 0; |
|---|
| 289 | | - |
|---|
| 290 | | -err_remove_sysfs: |
|---|
| 291 | | - sysfs_remove_group(&dev->kobj, &ili210x_attr_group); |
|---|
| 292 | | -err_free_irq: |
|---|
| 293 | | - free_irq(client->irq, priv); |
|---|
| 294 | | -err_free_mem: |
|---|
| 295 | | - input_free_device(input); |
|---|
| 296 | | - kfree(priv); |
|---|
| 297 | | - return error; |
|---|
| 298 | | -} |
|---|
| 299 | | - |
|---|
| 300 | | -static int ili210x_i2c_remove(struct i2c_client *client) |
|---|
| 301 | | -{ |
|---|
| 302 | | - struct ili210x *priv = i2c_get_clientdata(client); |
|---|
| 303 | | - |
|---|
| 304 | | - sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); |
|---|
| 305 | | - free_irq(priv->client->irq, priv); |
|---|
| 306 | | - cancel_delayed_work_sync(&priv->dwork); |
|---|
| 307 | | - input_unregister_device(priv->input); |
|---|
| 308 | | - kfree(priv); |
|---|
| 309 | | - |
|---|
| 310 | 488 | return 0; |
|---|
| 311 | 489 | } |
|---|
| 312 | | - |
|---|
| 313 | | -static int __maybe_unused ili210x_i2c_suspend(struct device *dev) |
|---|
| 314 | | -{ |
|---|
| 315 | | - struct i2c_client *client = to_i2c_client(dev); |
|---|
| 316 | | - |
|---|
| 317 | | - if (device_may_wakeup(&client->dev)) |
|---|
| 318 | | - enable_irq_wake(client->irq); |
|---|
| 319 | | - |
|---|
| 320 | | - return 0; |
|---|
| 321 | | -} |
|---|
| 322 | | - |
|---|
| 323 | | -static int __maybe_unused ili210x_i2c_resume(struct device *dev) |
|---|
| 324 | | -{ |
|---|
| 325 | | - struct i2c_client *client = to_i2c_client(dev); |
|---|
| 326 | | - |
|---|
| 327 | | - if (device_may_wakeup(&client->dev)) |
|---|
| 328 | | - disable_irq_wake(client->irq); |
|---|
| 329 | | - |
|---|
| 330 | | - return 0; |
|---|
| 331 | | -} |
|---|
| 332 | | - |
|---|
| 333 | | -static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, |
|---|
| 334 | | - ili210x_i2c_suspend, ili210x_i2c_resume); |
|---|
| 335 | 490 | |
|---|
| 336 | 491 | static const struct i2c_device_id ili210x_i2c_id[] = { |
|---|
| 337 | | - { "ili210x", 0 }, |
|---|
| 492 | + { "ili210x", (long)&ili210x_chip }, |
|---|
| 493 | + { "ili2117", (long)&ili211x_chip }, |
|---|
| 494 | + { "ili2120", (long)&ili212x_chip }, |
|---|
| 495 | + { "ili251x", (long)&ili251x_chip }, |
|---|
| 338 | 496 | { } |
|---|
| 339 | 497 | }; |
|---|
| 340 | 498 | MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); |
|---|
| 341 | 499 | |
|---|
| 500 | +static const struct of_device_id ili210x_dt_ids[] = { |
|---|
| 501 | + { .compatible = "ilitek,ili210x", .data = &ili210x_chip }, |
|---|
| 502 | + { .compatible = "ilitek,ili2117", .data = &ili211x_chip }, |
|---|
| 503 | + { .compatible = "ilitek,ili2120", .data = &ili212x_chip }, |
|---|
| 504 | + { .compatible = "ilitek,ili251x", .data = &ili251x_chip }, |
|---|
| 505 | + { } |
|---|
| 506 | +}; |
|---|
| 507 | +MODULE_DEVICE_TABLE(of, ili210x_dt_ids); |
|---|
| 508 | + |
|---|
| 342 | 509 | static struct i2c_driver ili210x_ts_driver = { |
|---|
| 343 | 510 | .driver = { |
|---|
| 344 | 511 | .name = "ili210x_i2c", |
|---|
| 345 | | - .pm = &ili210x_i2c_pm, |
|---|
| 512 | + .of_match_table = ili210x_dt_ids, |
|---|
| 346 | 513 | }, |
|---|
| 347 | 514 | .id_table = ili210x_i2c_id, |
|---|
| 348 | 515 | .probe = ili210x_i2c_probe, |
|---|
| 349 | | - .remove = ili210x_i2c_remove, |
|---|
| 350 | 516 | }; |
|---|
| 351 | 517 | |
|---|
| 352 | 518 | module_i2c_driver(ili210x_ts_driver); |
|---|