| .. | .. |
|---|
| 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 | |
|---|