diff options
| author | Richard Leitner <richard.leitner@skidata.com> | 2019-01-28 17:58:27 -0500 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2019-01-28 19:30:43 -0500 |
| commit | 43df039c6d92266d6e023f7eb23aeb6511934f20 (patch) | |
| tree | 62882fc73bfaa7324ada56c4bf149f89626bdee9 /drivers/input | |
| parent | 5896756a70b2f1d476d5cf2e174c1675ff0d9e8b (diff) | |
Input: sx8654 - add sx8650 support
The sx8654 and sx8650 are quite similar, therefore add support for the
sx8650 within the sx8654 driver.
Signed-off-by: Richard Leitner <richard.leitner@skidata.com>
Reviewed-by: Rob Herring <robh@kernel.org>
[dtor: use __be16 in sx8650_irq, add missing del_timer_sync]
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input')
| -rw-r--r-- | drivers/input/touchscreen/sx8654.c | 196 |
1 files changed, 176 insertions, 20 deletions
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c index 9e1777ed93a7..5f5af8eaecea 100644 --- a/drivers/input/touchscreen/sx8654.c +++ b/drivers/input/touchscreen/sx8654.c | |||
| @@ -44,9 +44,11 @@ | |||
| 44 | #define I2C_REG_IRQSRC 0x23 | 44 | #define I2C_REG_IRQSRC 0x23 |
| 45 | #define I2C_REG_SOFTRESET 0x3f | 45 | #define I2C_REG_SOFTRESET 0x3f |
| 46 | 46 | ||
| 47 | #define I2C_REG_SX8650_STAT 0x05 | ||
| 48 | #define SX8650_STAT_CONVIRQ 0x80 | ||
| 49 | |||
| 47 | /* commands */ | 50 | /* commands */ |
| 48 | #define CMD_READ_REGISTER 0x40 | 51 | #define CMD_READ_REGISTER 0x40 |
| 49 | #define CMD_MANUAL 0xc0 | ||
| 50 | #define CMD_PENTRG 0xe0 | 52 | #define CMD_PENTRG 0xe0 |
| 51 | 53 | ||
| 52 | /* value for I2C_REG_SOFTRESET */ | 54 | /* value for I2C_REG_SOFTRESET */ |
| @@ -58,6 +60,7 @@ | |||
| 58 | 60 | ||
| 59 | /* bits for RegTouch1 */ | 61 | /* bits for RegTouch1 */ |
| 60 | #define CONDIRQ 0x20 | 62 | #define CONDIRQ 0x20 |
| 63 | #define RPDNT_100K 0x00 | ||
| 61 | #define FILT_7SA 0x03 | 64 | #define FILT_7SA 0x03 |
| 62 | 65 | ||
| 63 | /* bits for I2C_REG_CHANMASK */ | 66 | /* bits for I2C_REG_CHANMASK */ |
| @@ -71,14 +74,121 @@ | |||
| 71 | /* power delay: lower nibble of CTRL0 register */ | 74 | /* power delay: lower nibble of CTRL0 register */ |
| 72 | #define POWDLY_1_1MS 0x0b | 75 | #define POWDLY_1_1MS 0x0b |
| 73 | 76 | ||
| 77 | /* for sx8650, as we have no pen release IRQ there: timeout in ns following the | ||
| 78 | * last PENIRQ after which we assume the pen is lifted. | ||
| 79 | */ | ||
| 80 | #define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10) | ||
| 81 | |||
| 74 | #define MAX_12BIT ((1 << 12) - 1) | 82 | #define MAX_12BIT ((1 << 12) - 1) |
| 83 | #define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */ | ||
| 84 | |||
| 85 | /* channel definition */ | ||
| 86 | #define CH_X 0x00 | ||
| 87 | #define CH_Y 0x01 | ||
| 88 | |||
| 89 | struct sx865x_data { | ||
| 90 | u8 cmd_manual; | ||
| 91 | u8 chan_mask; | ||
| 92 | bool has_irq_penrelease; | ||
| 93 | bool has_reg_irqmask; | ||
| 94 | irq_handler_t irqh; | ||
| 95 | }; | ||
| 75 | 96 | ||
| 76 | struct sx8654 { | 97 | struct sx8654 { |
| 77 | struct input_dev *input; | 98 | struct input_dev *input; |
| 78 | struct i2c_client *client; | 99 | struct i2c_client *client; |
| 79 | struct gpio_desc *gpio_reset; | 100 | struct gpio_desc *gpio_reset; |
| 101 | |||
| 102 | spinlock_t lock; /* for input reporting from irq/timer */ | ||
| 103 | struct timer_list timer; | ||
| 104 | |||
| 105 | const struct sx865x_data *data; | ||
| 80 | }; | 106 | }; |
| 81 | 107 | ||
| 108 | static inline void sx865x_penrelease(struct sx8654 *ts) | ||
| 109 | { | ||
| 110 | struct input_dev *input_dev = ts->input; | ||
| 111 | |||
| 112 | input_report_key(input_dev, BTN_TOUCH, 0); | ||
| 113 | input_sync(input_dev); | ||
| 114 | } | ||
| 115 | |||
| 116 | static void sx865x_penrelease_timer_handler(struct timer_list *t) | ||
| 117 | { | ||
| 118 | struct sx8654 *ts = from_timer(ts, t, timer); | ||
| 119 | unsigned long flags; | ||
| 120 | |||
| 121 | spin_lock_irqsave(&ts->lock, flags); | ||
| 122 | sx865x_penrelease(ts); | ||
| 123 | spin_unlock_irqrestore(&ts->lock, flags); | ||
| 124 | dev_dbg(&ts->client->dev, "penrelease by timer\n"); | ||
| 125 | } | ||
| 126 | |||
| 127 | static irqreturn_t sx8650_irq(int irq, void *handle) | ||
| 128 | { | ||
| 129 | struct sx8654 *ts = handle; | ||
| 130 | struct device *dev = &ts->client->dev; | ||
| 131 | int len, i; | ||
| 132 | unsigned long flags; | ||
| 133 | u8 stat; | ||
| 134 | u16 x, y; | ||
| 135 | u16 ch; | ||
| 136 | u16 chdata; | ||
| 137 | __be16 data[MAX_I2C_READ_LEN / sizeof(__be16)]; | ||
| 138 | u8 nchan = hweight32(ts->data->chan_mask); | ||
| 139 | u8 readlen = nchan * sizeof(*data); | ||
| 140 | |||
| 141 | stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER | ||
| 142 | | I2C_REG_SX8650_STAT); | ||
| 143 | |||
| 144 | if (!(stat & SX8650_STAT_CONVIRQ)) { | ||
| 145 | dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat); | ||
| 146 | return IRQ_HANDLED; | ||
| 147 | } | ||
| 148 | |||
| 149 | len = i2c_master_recv(ts->client, (u8 *)data, readlen); | ||
| 150 | if (len != readlen) { | ||
| 151 | dev_dbg(dev, "ignore short recv (%d)\n", len); | ||
| 152 | return IRQ_HANDLED; | ||
| 153 | } | ||
| 154 | |||
| 155 | spin_lock_irqsave(&ts->lock, flags); | ||
| 156 | |||
| 157 | x = 0; | ||
| 158 | y = 0; | ||
| 159 | for (i = 0; i < nchan; i++) { | ||
| 160 | chdata = be16_to_cpu(data[i]); | ||
| 161 | |||
| 162 | if (unlikely(chdata == 0xFFFF)) { | ||
| 163 | dev_dbg(dev, "invalid qualified data @ %d\n", i); | ||
| 164 | continue; | ||
| 165 | } else if (unlikely(chdata & 0x8000)) { | ||
| 166 | dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata); | ||
| 167 | continue; | ||
| 168 | } | ||
| 169 | |||
| 170 | ch = chdata >> 12; | ||
| 171 | if (ch == CH_X) | ||
| 172 | x = chdata & MAX_12BIT; | ||
| 173 | else if (ch == CH_Y) | ||
| 174 | y = chdata & MAX_12BIT; | ||
| 175 | else | ||
| 176 | dev_warn(dev, "unknown channel %d [0x%04x]\n", ch, | ||
| 177 | chdata); | ||
| 178 | } | ||
| 179 | |||
| 180 | input_report_abs(ts->input, ABS_X, x); | ||
| 181 | input_report_abs(ts->input, ABS_Y, y); | ||
| 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 | } | ||
| 191 | |||
| 82 | static irqreturn_t sx8654_irq(int irq, void *handle) | 192 | static irqreturn_t sx8654_irq(int irq, void *handle) |
| 83 | { | 193 | { |
| 84 | struct sx8654 *sx8654 = handle; | 194 | struct sx8654 *sx8654 = handle; |
| @@ -179,14 +289,17 @@ static void sx8654_close(struct input_dev *dev) | |||
| 179 | 289 | ||
| 180 | disable_irq(client->irq); | 290 | disable_irq(client->irq); |
| 181 | 291 | ||
| 292 | if (!sx8654->data->has_irq_penrelease) | ||
| 293 | del_timer_sync(&sx8654->timer); | ||
| 294 | |||
| 182 | /* enable manual mode mode */ | 295 | /* enable manual mode mode */ |
| 183 | error = i2c_smbus_write_byte(client, CMD_MANUAL); | 296 | error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual); |
| 184 | if (error) { | 297 | if (error) { |
| 185 | dev_err(&client->dev, "writing command CMD_MANUAL failed"); | 298 | dev_err(&client->dev, "writing command CMD_MANUAL failed"); |
| 186 | return; | 299 | return; |
| 187 | } | 300 | } |
| 188 | 301 | ||
| 189 | error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); | 302 | error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL); |
| 190 | if (error) { | 303 | if (error) { |
| 191 | dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); | 304 | dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); |
| 192 | return; | 305 | return; |
| @@ -219,6 +332,20 @@ static int sx8654_probe(struct i2c_client *client, | |||
| 219 | } | 332 | } |
| 220 | dev_dbg(&client->dev, "got GPIO reset pin\n"); | 333 | dev_dbg(&client->dev, "got GPIO reset pin\n"); |
| 221 | 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 | |||
| 222 | input = devm_input_allocate_device(&client->dev); | 349 | input = devm_input_allocate_device(&client->dev); |
| 223 | if (!input) | 350 | if (!input) |
| 224 | return -ENOMEM; | 351 | return -ENOMEM; |
| @@ -246,29 +373,31 @@ static int sx8654_probe(struct i2c_client *client, | |||
| 246 | } | 373 | } |
| 247 | 374 | ||
| 248 | error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, | 375 | error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, |
| 249 | CONV_X | CONV_Y); | 376 | sx8654->data->chan_mask); |
| 250 | if (error) { | 377 | if (error) { |
| 251 | dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); | 378 | dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); |
| 252 | return error; | 379 | return error; |
| 253 | } | 380 | } |
| 254 | 381 | ||
| 255 | error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, | 382 | if (sx8654->data->has_reg_irqmask) { |
| 256 | IRQ_PENTOUCH_TOUCHCONVDONE | | 383 | error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, |
| 257 | IRQ_PENRELEASE); | 384 | IRQ_PENTOUCH_TOUCHCONVDONE | |
| 258 | if (error) { | 385 | IRQ_PENRELEASE); |
| 259 | dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); | 386 | if (error) { |
| 260 | return error; | 387 | dev_err(&client->dev, "writing I2C_REG_IRQMASK failed"); |
| 388 | return error; | ||
| 389 | } | ||
| 261 | } | 390 | } |
| 262 | 391 | ||
| 263 | error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, | 392 | error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, |
| 264 | CONDIRQ | FILT_7SA); | 393 | CONDIRQ | RPDNT_100K | FILT_7SA); |
| 265 | if (error) { | 394 | if (error) { |
| 266 | dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); | 395 | dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); |
| 267 | return error; | 396 | return error; |
| 268 | } | 397 | } |
| 269 | 398 | ||
| 270 | error = devm_request_threaded_irq(&client->dev, client->irq, | 399 | error = devm_request_threaded_irq(&client->dev, client->irq, |
| 271 | NULL, sx8654_irq, | 400 | NULL, sx8654->data->irqh, |
| 272 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, | 401 | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |
| 273 | client->name, sx8654); | 402 | client->name, sx8654); |
| 274 | if (error) { | 403 | if (error) { |
| @@ -288,21 +417,48 @@ static int sx8654_probe(struct i2c_client *client, | |||
| 288 | return 0; | 417 | return 0; |
| 289 | } | 418 | } |
| 290 | 419 | ||
| 420 | static const struct sx865x_data sx8650_data = { | ||
| 421 | .cmd_manual = 0xb0, | ||
| 422 | .has_irq_penrelease = false, | ||
| 423 | .has_reg_irqmask = false, | ||
| 424 | .chan_mask = (CONV_X | CONV_Y), | ||
| 425 | .irqh = sx8650_irq, | ||
| 426 | }; | ||
| 427 | |||
| 428 | static const struct sx865x_data sx8654_data = { | ||
| 429 | .cmd_manual = 0xc0, | ||
| 430 | .has_irq_penrelease = true, | ||
| 431 | .has_reg_irqmask = true, | ||
| 432 | .chan_mask = (CONV_X | CONV_Y), | ||
| 433 | .irqh = sx8654_irq, | ||
| 434 | }; | ||
| 435 | |||
| 291 | #ifdef CONFIG_OF | 436 | #ifdef CONFIG_OF |
| 292 | static const struct of_device_id sx8654_of_match[] = { | 437 | static const struct of_device_id sx8654_of_match[] = { |
| 293 | { .compatible = "semtech,sx8654", }, | 438 | { |
| 294 | { .compatible = "semtech,sx8655", }, | 439 | .compatible = "semtech,sx8650", |
| 295 | { .compatible = "semtech,sx8656", }, | 440 | .data = &sx8650_data, |
| 296 | { }, | 441 | }, { |
| 442 | .compatible = "semtech,sx8654", | ||
| 443 | .data = &sx8654_data, | ||
| 444 | }, { | ||
| 445 | .compatible = "semtech,sx8655", | ||
| 446 | .data = &sx8654_data, | ||
| 447 | }, { | ||
| 448 | .compatible = "semtech,sx8656", | ||
| 449 | .data = &sx8654_data, | ||
| 450 | }, | ||
| 451 | { } | ||
| 297 | }; | 452 | }; |
| 298 | MODULE_DEVICE_TABLE(of, sx8654_of_match); | 453 | MODULE_DEVICE_TABLE(of, sx8654_of_match); |
| 299 | #endif | 454 | #endif |
| 300 | 455 | ||
| 301 | static const struct i2c_device_id sx8654_id_table[] = { | 456 | static const struct i2c_device_id sx8654_id_table[] = { |
| 302 | { "semtech_sx8654", 0 }, | 457 | { .name = "semtech_sx8650", .driver_data = (long)&sx8650_data }, |
| 303 | { "semtech_sx8655", 0 }, | 458 | { .name = "semtech_sx8654", .driver_data = (long)&sx8654_data }, |
| 304 | { "semtech_sx8656", 0 }, | 459 | { .name = "semtech_sx8655", .driver_data = (long)&sx8654_data }, |
| 305 | { }, | 460 | { .name = "semtech_sx8656", .driver_data = (long)&sx8654_data }, |
| 461 | { } | ||
| 306 | }; | 462 | }; |
| 307 | MODULE_DEVICE_TABLE(i2c, sx8654_id_table); | 463 | MODULE_DEVICE_TABLE(i2c, sx8654_id_table); |
| 308 | 464 | ||
