.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for Semtech SX8654 I2C touchscreen controller. |
---|
3 | 4 | * |
---|
.. | .. |
---|
21 | 22 | * Copyright (C) 2002 MontaVista Software |
---|
22 | 23 | * Copyright (C) 2004 Texas Instruments |
---|
23 | 24 | * Copyright (C) 2005 Dirk Behme |
---|
24 | | - * |
---|
25 | | - * This program is free software; you can redistribute it and/or modify |
---|
26 | | - * it under the terms of the GNU General Public License version 2 as |
---|
27 | | - * published by the Free Software Foundation. |
---|
28 | 25 | */ |
---|
29 | 26 | |
---|
30 | | -#include <linux/input.h> |
---|
31 | | -#include <linux/module.h> |
---|
32 | | -#include <linux/of.h> |
---|
| 27 | +#include <linux/bitops.h> |
---|
| 28 | +#include <linux/delay.h> |
---|
| 29 | +#include <linux/gpio/consumer.h> |
---|
33 | 30 | #include <linux/i2c.h> |
---|
| 31 | +#include <linux/input.h> |
---|
| 32 | +#include <linux/input/touchscreen.h> |
---|
34 | 33 | #include <linux/interrupt.h> |
---|
35 | 34 | #include <linux/irq.h> |
---|
| 35 | +#include <linux/module.h> |
---|
| 36 | +#include <linux/of.h> |
---|
36 | 37 | |
---|
37 | 38 | /* register addresses */ |
---|
38 | 39 | #define I2C_REG_TOUCH0 0x00 |
---|
.. | .. |
---|
42 | 43 | #define I2C_REG_IRQSRC 0x23 |
---|
43 | 44 | #define I2C_REG_SOFTRESET 0x3f |
---|
44 | 45 | |
---|
| 46 | +#define I2C_REG_SX8650_STAT 0x05 |
---|
| 47 | +#define SX8650_STAT_CONVIRQ BIT(7) |
---|
| 48 | + |
---|
45 | 49 | /* commands */ |
---|
46 | 50 | #define CMD_READ_REGISTER 0x40 |
---|
47 | | -#define CMD_MANUAL 0xc0 |
---|
48 | 51 | #define CMD_PENTRG 0xe0 |
---|
49 | 52 | |
---|
50 | 53 | /* value for I2C_REG_SOFTRESET */ |
---|
51 | 54 | #define SOFTRESET_VALUE 0xde |
---|
52 | 55 | |
---|
53 | 56 | /* bits for I2C_REG_IRQSRC */ |
---|
54 | | -#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 |
---|
55 | | -#define IRQ_PENRELEASE 0x04 |
---|
| 57 | +#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(3) |
---|
| 58 | +#define IRQ_PENRELEASE BIT(2) |
---|
56 | 59 | |
---|
57 | 60 | /* bits for RegTouch1 */ |
---|
58 | 61 | #define CONDIRQ 0x20 |
---|
| 62 | +#define RPDNT_100K 0x00 |
---|
59 | 63 | #define FILT_7SA 0x03 |
---|
60 | 64 | |
---|
61 | 65 | /* bits for I2C_REG_CHANMASK */ |
---|
62 | | -#define CONV_X 0x80 |
---|
63 | | -#define CONV_Y 0x40 |
---|
| 66 | +#define CONV_X BIT(7) |
---|
| 67 | +#define CONV_Y BIT(6) |
---|
64 | 68 | |
---|
65 | 69 | /* coordinates rate: higher nibble of CTRL0 register */ |
---|
66 | 70 | #define RATE_MANUAL 0x00 |
---|
.. | .. |
---|
69 | 73 | /* power delay: lower nibble of CTRL0 register */ |
---|
70 | 74 | #define POWDLY_1_1MS 0x0b |
---|
71 | 75 | |
---|
| 76 | +/* for sx8650, as we have no pen release IRQ there: timeout in ns following the |
---|
| 77 | + * last PENIRQ after which we assume the pen is lifted. |
---|
| 78 | + */ |
---|
| 79 | +#define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10) |
---|
| 80 | + |
---|
72 | 81 | #define MAX_12BIT ((1 << 12) - 1) |
---|
| 82 | +#define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */ |
---|
| 83 | + |
---|
| 84 | +/* channel definition */ |
---|
| 85 | +#define CH_X 0x00 |
---|
| 86 | +#define CH_Y 0x01 |
---|
| 87 | + |
---|
| 88 | +struct sx865x_data { |
---|
| 89 | + u8 cmd_manual; |
---|
| 90 | + u8 chan_mask; |
---|
| 91 | + bool has_irq_penrelease; |
---|
| 92 | + bool has_reg_irqmask; |
---|
| 93 | + irq_handler_t irqh; |
---|
| 94 | +}; |
---|
73 | 95 | |
---|
74 | 96 | struct sx8654 { |
---|
75 | 97 | struct input_dev *input; |
---|
76 | 98 | struct i2c_client *client; |
---|
| 99 | + struct gpio_desc *gpio_reset; |
---|
| 100 | + |
---|
| 101 | + spinlock_t lock; /* for input reporting from irq/timer */ |
---|
| 102 | + struct timer_list timer; |
---|
| 103 | + |
---|
| 104 | + struct touchscreen_properties props; |
---|
| 105 | + |
---|
| 106 | + const struct sx865x_data *data; |
---|
77 | 107 | }; |
---|
| 108 | + |
---|
| 109 | +static inline void sx865x_penrelease(struct sx8654 *ts) |
---|
| 110 | +{ |
---|
| 111 | + struct input_dev *input_dev = ts->input; |
---|
| 112 | + |
---|
| 113 | + input_report_key(input_dev, BTN_TOUCH, 0); |
---|
| 114 | + input_sync(input_dev); |
---|
| 115 | +} |
---|
| 116 | + |
---|
| 117 | +static void sx865x_penrelease_timer_handler(struct timer_list *t) |
---|
| 118 | +{ |
---|
| 119 | + struct sx8654 *ts = from_timer(ts, t, timer); |
---|
| 120 | + unsigned long flags; |
---|
| 121 | + |
---|
| 122 | + spin_lock_irqsave(&ts->lock, flags); |
---|
| 123 | + sx865x_penrelease(ts); |
---|
| 124 | + spin_unlock_irqrestore(&ts->lock, flags); |
---|
| 125 | + dev_dbg(&ts->client->dev, "penrelease by timer\n"); |
---|
| 126 | +} |
---|
| 127 | + |
---|
| 128 | +static irqreturn_t sx8650_irq(int irq, void *handle) |
---|
| 129 | +{ |
---|
| 130 | + struct sx8654 *ts = handle; |
---|
| 131 | + struct device *dev = &ts->client->dev; |
---|
| 132 | + int len, i; |
---|
| 133 | + unsigned long flags; |
---|
| 134 | + u8 stat; |
---|
| 135 | + u16 x, y; |
---|
| 136 | + u16 ch; |
---|
| 137 | + u16 chdata; |
---|
| 138 | + __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)]; |
---|
| 139 | + u8 nchan = hweight32(ts->data->chan_mask); |
---|
| 140 | + u8 readlen = nchan * sizeof(*data); |
---|
| 141 | + |
---|
| 142 | + stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER |
---|
| 143 | + | I2C_REG_SX8650_STAT); |
---|
| 144 | + |
---|
| 145 | + if (!(stat & SX8650_STAT_CONVIRQ)) { |
---|
| 146 | + dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat); |
---|
| 147 | + return IRQ_HANDLED; |
---|
| 148 | + } |
---|
| 149 | + |
---|
| 150 | + len = i2c_master_recv(ts->client, (u8 *)data, readlen); |
---|
| 151 | + if (len != readlen) { |
---|
| 152 | + dev_dbg(dev, "ignore short recv (%d)\n", len); |
---|
| 153 | + return IRQ_HANDLED; |
---|
| 154 | + } |
---|
| 155 | + |
---|
| 156 | + spin_lock_irqsave(&ts->lock, flags); |
---|
| 157 | + |
---|
| 158 | + x = 0; |
---|
| 159 | + y = 0; |
---|
| 160 | + for (i = 0; i < nchan; i++) { |
---|
| 161 | + chdata = be16_to_cpu(data[i]); |
---|
| 162 | + |
---|
| 163 | + if (unlikely(chdata == 0xFFFF)) { |
---|
| 164 | + dev_dbg(dev, "invalid qualified data @ %d\n", i); |
---|
| 165 | + continue; |
---|
| 166 | + } else if (unlikely(chdata & 0x8000)) { |
---|
| 167 | + dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata); |
---|
| 168 | + continue; |
---|
| 169 | + } |
---|
| 170 | + |
---|
| 171 | + ch = chdata >> 12; |
---|
| 172 | + if (ch == CH_X) |
---|
| 173 | + x = chdata & MAX_12BIT; |
---|
| 174 | + else if (ch == CH_Y) |
---|
| 175 | + y = chdata & MAX_12BIT; |
---|
| 176 | + else |
---|
| 177 | + dev_warn(dev, "unknown channel %d [0x%04x]\n", ch, |
---|
| 178 | + chdata); |
---|
| 179 | + } |
---|
| 180 | + |
---|
| 181 | + touchscreen_report_pos(ts->input, &ts->props, x, y, false); |
---|
| 182 | + input_report_key(ts->input, BTN_TOUCH, 1); |
---|
| 183 | + input_sync(ts->input); |
---|
| 184 | + dev_dbg(dev, "point(%4d,%4d)\n", x, y); |
---|
| 185 | + |
---|
| 186 | + mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT); |
---|
| 187 | + spin_unlock_irqrestore(&ts->lock, flags); |
---|
| 188 | + |
---|
| 189 | + return IRQ_HANDLED; |
---|
| 190 | +} |
---|
78 | 191 | |
---|
79 | 192 | static irqreturn_t sx8654_irq(int irq, void *handle) |
---|
80 | 193 | { |
---|
.. | .. |
---|
112 | 225 | x = ((data[0] & 0xf) << 8) | (data[1]); |
---|
113 | 226 | y = ((data[2] & 0xf) << 8) | (data[3]); |
---|
114 | 227 | |
---|
115 | | - input_report_abs(sx8654->input, ABS_X, x); |
---|
116 | | - input_report_abs(sx8654->input, ABS_Y, y); |
---|
| 228 | + touchscreen_report_pos(sx8654->input, &sx8654->props, x, y, |
---|
| 229 | + false); |
---|
117 | 230 | input_report_key(sx8654->input, BTN_TOUCH, 1); |
---|
118 | 231 | input_sync(sx8654->input); |
---|
119 | 232 | |
---|
.. | .. |
---|
122 | 235 | |
---|
123 | 236 | out: |
---|
124 | 237 | return IRQ_HANDLED; |
---|
| 238 | +} |
---|
| 239 | + |
---|
| 240 | +static int sx8654_reset(struct sx8654 *ts) |
---|
| 241 | +{ |
---|
| 242 | + int err; |
---|
| 243 | + |
---|
| 244 | + if (ts->gpio_reset) { |
---|
| 245 | + gpiod_set_value_cansleep(ts->gpio_reset, 1); |
---|
| 246 | + udelay(2); /* Tpulse > 1µs */ |
---|
| 247 | + gpiod_set_value_cansleep(ts->gpio_reset, 0); |
---|
| 248 | + } else { |
---|
| 249 | + dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n"); |
---|
| 250 | + err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET, |
---|
| 251 | + SOFTRESET_VALUE); |
---|
| 252 | + if (err) |
---|
| 253 | + return err; |
---|
| 254 | + } |
---|
| 255 | + |
---|
| 256 | + return 0; |
---|
125 | 257 | } |
---|
126 | 258 | |
---|
127 | 259 | static int sx8654_open(struct input_dev *dev) |
---|
.. | .. |
---|
157 | 289 | |
---|
158 | 290 | disable_irq(client->irq); |
---|
159 | 291 | |
---|
| 292 | + if (!sx8654->data->has_irq_penrelease) |
---|
| 293 | + del_timer_sync(&sx8654->timer); |
---|
| 294 | + |
---|
160 | 295 | /* enable manual mode mode */ |
---|
161 | | - error = i2c_smbus_write_byte(client, CMD_MANUAL); |
---|
| 296 | + error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual); |
---|
162 | 297 | if (error) { |
---|
163 | 298 | dev_err(&client->dev, "writing command CMD_MANUAL failed"); |
---|
164 | 299 | return; |
---|
165 | 300 | } |
---|
166 | 301 | |
---|
167 | | - error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); |
---|
| 302 | + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL); |
---|
168 | 303 | if (error) { |
---|
169 | 304 | dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); |
---|
170 | 305 | return; |
---|
.. | .. |
---|
186 | 321 | if (!sx8654) |
---|
187 | 322 | return -ENOMEM; |
---|
188 | 323 | |
---|
| 324 | + sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset", |
---|
| 325 | + GPIOD_OUT_HIGH); |
---|
| 326 | + if (IS_ERR(sx8654->gpio_reset)) { |
---|
| 327 | + error = PTR_ERR(sx8654->gpio_reset); |
---|
| 328 | + if (error != -EPROBE_DEFER) |
---|
| 329 | + dev_err(&client->dev, "unable to get reset-gpio: %d\n", |
---|
| 330 | + error); |
---|
| 331 | + return error; |
---|
| 332 | + } |
---|
| 333 | + dev_dbg(&client->dev, "got GPIO reset pin\n"); |
---|
| 334 | + |
---|
| 335 | + sx8654->data = device_get_match_data(&client->dev); |
---|
| 336 | + if (!sx8654->data) |
---|
| 337 | + sx8654->data = (const struct sx865x_data *)id->driver_data; |
---|
| 338 | + if (!sx8654->data) { |
---|
| 339 | + dev_err(&client->dev, "invalid or missing device data\n"); |
---|
| 340 | + return -EINVAL; |
---|
| 341 | + } |
---|
| 342 | + |
---|
| 343 | + if (!sx8654->data->has_irq_penrelease) { |
---|
| 344 | + dev_dbg(&client->dev, "use timer for penrelease\n"); |
---|
| 345 | + timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0); |
---|
| 346 | + spin_lock_init(&sx8654->lock); |
---|
| 347 | + } |
---|
| 348 | + |
---|
189 | 349 | input = devm_input_allocate_device(&client->dev); |
---|
190 | 350 | if (!input) |
---|
191 | 351 | return -ENOMEM; |
---|
.. | .. |
---|
201 | 361 | input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); |
---|
202 | 362 | input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); |
---|
203 | 363 | |
---|
| 364 | + touchscreen_parse_properties(input, false, &sx8654->props); |
---|
| 365 | + |
---|
204 | 366 | sx8654->client = client; |
---|
205 | 367 | sx8654->input = input; |
---|
206 | 368 | |
---|
207 | 369 | input_set_drvdata(sx8654->input, sx8654); |
---|
208 | 370 | |
---|
209 | | - error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, |
---|
210 | | - SOFTRESET_VALUE); |
---|
| 371 | + error = sx8654_reset(sx8654); |
---|
211 | 372 | if (error) { |
---|
212 | | - dev_err(&client->dev, "writing softreset value failed"); |
---|
| 373 | + dev_err(&client->dev, "reset failed"); |
---|
213 | 374 | return error; |
---|
214 | 375 | } |
---|
215 | 376 | |
---|
216 | 377 | error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, |
---|
217 | | - CONV_X | CONV_Y); |
---|
| 378 | + sx8654->data->chan_mask); |
---|
218 | 379 | if (error) { |
---|
219 | 380 | dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); |
---|
220 | 381 | return error; |
---|
221 | 382 | } |
---|
222 | 383 | |
---|
223 | | - error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, |
---|
224 | | - IRQ_PENTOUCH_TOUCHCONVDONE | |
---|
225 | | - IRQ_PENRELEASE); |
---|
226 | | - if (error) { |
---|
227 | | - dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); |
---|
228 | | - return error; |
---|
| 384 | + if (sx8654->data->has_reg_irqmask) { |
---|
| 385 | + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, |
---|
| 386 | + IRQ_PENTOUCH_TOUCHCONVDONE | |
---|
| 387 | + IRQ_PENRELEASE); |
---|
| 388 | + if (error) { |
---|
| 389 | + dev_err(&client->dev, "writing I2C_REG_IRQMASK failed"); |
---|
| 390 | + return error; |
---|
| 391 | + } |
---|
229 | 392 | } |
---|
230 | 393 | |
---|
231 | 394 | error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, |
---|
232 | | - CONDIRQ | FILT_7SA); |
---|
| 395 | + CONDIRQ | RPDNT_100K | FILT_7SA); |
---|
233 | 396 | if (error) { |
---|
234 | 397 | dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); |
---|
235 | 398 | return error; |
---|
236 | 399 | } |
---|
237 | 400 | |
---|
238 | 401 | error = devm_request_threaded_irq(&client->dev, client->irq, |
---|
239 | | - NULL, sx8654_irq, |
---|
240 | | - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
---|
| 402 | + NULL, sx8654->data->irqh, |
---|
| 403 | + IRQF_ONESHOT, |
---|
241 | 404 | client->name, sx8654); |
---|
242 | 405 | if (error) { |
---|
243 | 406 | dev_err(&client->dev, |
---|
.. | .. |
---|
256 | 419 | return 0; |
---|
257 | 420 | } |
---|
258 | 421 | |
---|
| 422 | +static const struct sx865x_data sx8650_data = { |
---|
| 423 | + .cmd_manual = 0xb0, |
---|
| 424 | + .has_irq_penrelease = false, |
---|
| 425 | + .has_reg_irqmask = false, |
---|
| 426 | + .chan_mask = (CONV_X | CONV_Y), |
---|
| 427 | + .irqh = sx8650_irq, |
---|
| 428 | +}; |
---|
| 429 | + |
---|
| 430 | +static const struct sx865x_data sx8654_data = { |
---|
| 431 | + .cmd_manual = 0xc0, |
---|
| 432 | + .has_irq_penrelease = true, |
---|
| 433 | + .has_reg_irqmask = true, |
---|
| 434 | + .chan_mask = (CONV_X | CONV_Y), |
---|
| 435 | + .irqh = sx8654_irq, |
---|
| 436 | +}; |
---|
| 437 | + |
---|
259 | 438 | #ifdef CONFIG_OF |
---|
260 | 439 | static const struct of_device_id sx8654_of_match[] = { |
---|
261 | | - { .compatible = "semtech,sx8654", }, |
---|
262 | | - { }, |
---|
| 440 | + { |
---|
| 441 | + .compatible = "semtech,sx8650", |
---|
| 442 | + .data = &sx8650_data, |
---|
| 443 | + }, { |
---|
| 444 | + .compatible = "semtech,sx8654", |
---|
| 445 | + .data = &sx8654_data, |
---|
| 446 | + }, { |
---|
| 447 | + .compatible = "semtech,sx8655", |
---|
| 448 | + .data = &sx8654_data, |
---|
| 449 | + }, { |
---|
| 450 | + .compatible = "semtech,sx8656", |
---|
| 451 | + .data = &sx8654_data, |
---|
| 452 | + }, |
---|
| 453 | + { } |
---|
263 | 454 | }; |
---|
264 | 455 | MODULE_DEVICE_TABLE(of, sx8654_of_match); |
---|
265 | 456 | #endif |
---|
266 | 457 | |
---|
267 | 458 | static const struct i2c_device_id sx8654_id_table[] = { |
---|
268 | | - { "semtech_sx8654", 0 }, |
---|
269 | | - { }, |
---|
| 459 | + { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data }, |
---|
| 460 | + { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data }, |
---|
| 461 | + { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data }, |
---|
| 462 | + { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data }, |
---|
| 463 | + { } |
---|
270 | 464 | }; |
---|
271 | 465 | MODULE_DEVICE_TABLE(i2c, sx8654_id_table); |
---|
272 | 466 | |
---|