18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/delay.h> 38c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 48c2ecf20Sopenharmony_ci#include <linux/i2c.h> 58c2ecf20Sopenharmony_ci#include <linux/input.h> 68c2ecf20Sopenharmony_ci#include <linux/input/mt.h> 78c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_device.h> 118c2ecf20Sopenharmony_ci#include <linux/sizes.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define ILI2XXX_POLL_PERIOD 20 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define ILI210X_DATA_SIZE 64 188c2ecf20Sopenharmony_ci#define ILI211X_DATA_SIZE 43 198c2ecf20Sopenharmony_ci#define ILI251X_DATA_SIZE1 31 208c2ecf20Sopenharmony_ci#define ILI251X_DATA_SIZE2 20 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Touchscreen commands */ 238c2ecf20Sopenharmony_ci#define REG_TOUCHDATA 0x10 248c2ecf20Sopenharmony_ci#define REG_PANEL_INFO 0x20 258c2ecf20Sopenharmony_ci#define REG_CALIBRATE 0xcc 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct ili2xxx_chip { 288c2ecf20Sopenharmony_ci int (*read_reg)(struct i2c_client *client, u8 reg, 298c2ecf20Sopenharmony_ci void *buf, size_t len); 308c2ecf20Sopenharmony_ci int (*get_touch_data)(struct i2c_client *client, u8 *data); 318c2ecf20Sopenharmony_ci bool (*parse_touch_data)(const u8 *data, unsigned int finger, 328c2ecf20Sopenharmony_ci unsigned int *x, unsigned int *y, 338c2ecf20Sopenharmony_ci unsigned int *z); 348c2ecf20Sopenharmony_ci bool (*continue_polling)(const u8 *data, bool touch); 358c2ecf20Sopenharmony_ci unsigned int max_touches; 368c2ecf20Sopenharmony_ci unsigned int resolution; 378c2ecf20Sopenharmony_ci bool has_calibrate_reg; 388c2ecf20Sopenharmony_ci bool has_pressure_reg; 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistruct ili210x { 428c2ecf20Sopenharmony_ci struct i2c_client *client; 438c2ecf20Sopenharmony_ci struct input_dev *input; 448c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 458c2ecf20Sopenharmony_ci struct touchscreen_properties prop; 468c2ecf20Sopenharmony_ci const struct ili2xxx_chip *chip; 478c2ecf20Sopenharmony_ci bool stop; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int ili210x_read_reg(struct i2c_client *client, 518c2ecf20Sopenharmony_ci u8 reg, void *buf, size_t len) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .addr = client->addr, 568c2ecf20Sopenharmony_ci .flags = 0, 578c2ecf20Sopenharmony_ci .len = 1, 588c2ecf20Sopenharmony_ci .buf = ®, 598c2ecf20Sopenharmony_ci }, 608c2ecf20Sopenharmony_ci { 618c2ecf20Sopenharmony_ci .addr = client->addr, 628c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 638c2ecf20Sopenharmony_ci .len = len, 648c2ecf20Sopenharmony_ci .buf = buf, 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci }; 678c2ecf20Sopenharmony_ci int error, ret; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); 708c2ecf20Sopenharmony_ci if (ret != ARRAY_SIZE(msg)) { 718c2ecf20Sopenharmony_ci error = ret < 0 ? ret : -EIO; 728c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s failed: %d\n", __func__, error); 738c2ecf20Sopenharmony_ci return error; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci return 0; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int ili210x_read_touch_data(struct i2c_client *client, u8 *data) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return ili210x_read_reg(client, REG_TOUCHDATA, 828c2ecf20Sopenharmony_ci data, ILI210X_DATA_SIZE); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic bool ili210x_touchdata_to_coords(const u8 *touchdata, 868c2ecf20Sopenharmony_ci unsigned int finger, 878c2ecf20Sopenharmony_ci unsigned int *x, unsigned int *y, 888c2ecf20Sopenharmony_ci unsigned int *z) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci if (!(touchdata[0] & BIT(finger))) 918c2ecf20Sopenharmony_ci return false; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci *x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0); 948c2ecf20Sopenharmony_ci *y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return true; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic bool ili210x_check_continue_polling(const u8 *data, bool touch) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci return data[0] & 0xf3; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili210x_chip = { 1058c2ecf20Sopenharmony_ci .read_reg = ili210x_read_reg, 1068c2ecf20Sopenharmony_ci .get_touch_data = ili210x_read_touch_data, 1078c2ecf20Sopenharmony_ci .parse_touch_data = ili210x_touchdata_to_coords, 1088c2ecf20Sopenharmony_ci .continue_polling = ili210x_check_continue_polling, 1098c2ecf20Sopenharmony_ci .max_touches = 2, 1108c2ecf20Sopenharmony_ci .has_calibrate_reg = true, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic int ili211x_read_touch_data(struct i2c_client *client, u8 *data) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci s16 sum = 0; 1168c2ecf20Sopenharmony_ci int error; 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci int i; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ret = i2c_master_recv(client, data, ILI211X_DATA_SIZE); 1218c2ecf20Sopenharmony_ci if (ret != ILI211X_DATA_SIZE) { 1228c2ecf20Sopenharmony_ci error = ret < 0 ? ret : -EIO; 1238c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s failed: %d\n", __func__, error); 1248c2ecf20Sopenharmony_ci return error; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* This chip uses custom checksum at the end of data */ 1288c2ecf20Sopenharmony_ci for (i = 0; i < ILI211X_DATA_SIZE - 1; i++) 1298c2ecf20Sopenharmony_ci sum = (sum + data[i]) & 0xff; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if ((-sum & 0xff) != data[ILI211X_DATA_SIZE - 1]) { 1328c2ecf20Sopenharmony_ci dev_err(&client->dev, 1338c2ecf20Sopenharmony_ci "CRC error (crc=0x%02x expected=0x%02x)\n", 1348c2ecf20Sopenharmony_ci sum, data[ILI211X_DATA_SIZE - 1]); 1358c2ecf20Sopenharmony_ci return -EIO; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return 0; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic bool ili211x_touchdata_to_coords(const u8 *touchdata, 1428c2ecf20Sopenharmony_ci unsigned int finger, 1438c2ecf20Sopenharmony_ci unsigned int *x, unsigned int *y, 1448c2ecf20Sopenharmony_ci unsigned int *z) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci u32 data; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci data = get_unaligned_be32(touchdata + 1 + (finger * 4) + 0); 1498c2ecf20Sopenharmony_ci if (data == 0xffffffff) /* Finger up */ 1508c2ecf20Sopenharmony_ci return false; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci *x = ((touchdata[1 + (finger * 4) + 0] & 0xf0) << 4) | 1538c2ecf20Sopenharmony_ci touchdata[1 + (finger * 4) + 1]; 1548c2ecf20Sopenharmony_ci *y = ((touchdata[1 + (finger * 4) + 0] & 0x0f) << 8) | 1558c2ecf20Sopenharmony_ci touchdata[1 + (finger * 4) + 2]; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return true; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic bool ili211x_decline_polling(const u8 *data, bool touch) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci return false; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili211x_chip = { 1668c2ecf20Sopenharmony_ci .read_reg = ili210x_read_reg, 1678c2ecf20Sopenharmony_ci .get_touch_data = ili211x_read_touch_data, 1688c2ecf20Sopenharmony_ci .parse_touch_data = ili211x_touchdata_to_coords, 1698c2ecf20Sopenharmony_ci .continue_polling = ili211x_decline_polling, 1708c2ecf20Sopenharmony_ci .max_touches = 10, 1718c2ecf20Sopenharmony_ci .resolution = 2048, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic bool ili212x_touchdata_to_coords(const u8 *touchdata, 1758c2ecf20Sopenharmony_ci unsigned int finger, 1768c2ecf20Sopenharmony_ci unsigned int *x, unsigned int *y, 1778c2ecf20Sopenharmony_ci unsigned int *z) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci u16 val; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci val = get_unaligned_be16(touchdata + 3 + (finger * 5) + 0); 1828c2ecf20Sopenharmony_ci if (!(val & BIT(15))) /* Touch indication */ 1838c2ecf20Sopenharmony_ci return false; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci *x = val & 0x3fff; 1868c2ecf20Sopenharmony_ci *y = get_unaligned_be16(touchdata + 3 + (finger * 5) + 2); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return true; 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic bool ili212x_check_continue_polling(const u8 *data, bool touch) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci return touch; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili212x_chip = { 1978c2ecf20Sopenharmony_ci .read_reg = ili210x_read_reg, 1988c2ecf20Sopenharmony_ci .get_touch_data = ili210x_read_touch_data, 1998c2ecf20Sopenharmony_ci .parse_touch_data = ili212x_touchdata_to_coords, 2008c2ecf20Sopenharmony_ci .continue_polling = ili212x_check_continue_polling, 2018c2ecf20Sopenharmony_ci .max_touches = 10, 2028c2ecf20Sopenharmony_ci .has_calibrate_reg = true, 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int ili251x_read_reg(struct i2c_client *client, 2068c2ecf20Sopenharmony_ci u8 reg, void *buf, size_t len) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci int error; 2098c2ecf20Sopenharmony_ci int ret; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci ret = i2c_master_send(client, ®, 1); 2128c2ecf20Sopenharmony_ci if (ret == 1) { 2138c2ecf20Sopenharmony_ci usleep_range(5000, 5500); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci ret = i2c_master_recv(client, buf, len); 2168c2ecf20Sopenharmony_ci if (ret == len) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci error = ret < 0 ? ret : -EIO; 2218c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s failed: %d\n", __func__, error); 2228c2ecf20Sopenharmony_ci return ret; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int ili251x_read_touch_data(struct i2c_client *client, u8 *data) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci int error; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci error = ili251x_read_reg(client, REG_TOUCHDATA, 2308c2ecf20Sopenharmony_ci data, ILI251X_DATA_SIZE1); 2318c2ecf20Sopenharmony_ci if (!error && data[0] == 2) { 2328c2ecf20Sopenharmony_ci error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1, 2338c2ecf20Sopenharmony_ci ILI251X_DATA_SIZE2); 2348c2ecf20Sopenharmony_ci if (error >= 0 && error != ILI251X_DATA_SIZE2) 2358c2ecf20Sopenharmony_ci error = -EIO; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return error; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic bool ili251x_touchdata_to_coords(const u8 *touchdata, 2428c2ecf20Sopenharmony_ci unsigned int finger, 2438c2ecf20Sopenharmony_ci unsigned int *x, unsigned int *y, 2448c2ecf20Sopenharmony_ci unsigned int *z) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci u16 val; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci val = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0); 2498c2ecf20Sopenharmony_ci if (!(val & BIT(15))) /* Touch indication */ 2508c2ecf20Sopenharmony_ci return false; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci *x = val & 0x3fff; 2538c2ecf20Sopenharmony_ci *y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2); 2548c2ecf20Sopenharmony_ci *z = touchdata[1 + (finger * 5) + 4]; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return true; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic bool ili251x_check_continue_polling(const u8 *data, bool touch) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci return touch; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic const struct ili2xxx_chip ili251x_chip = { 2658c2ecf20Sopenharmony_ci .read_reg = ili251x_read_reg, 2668c2ecf20Sopenharmony_ci .get_touch_data = ili251x_read_touch_data, 2678c2ecf20Sopenharmony_ci .parse_touch_data = ili251x_touchdata_to_coords, 2688c2ecf20Sopenharmony_ci .continue_polling = ili251x_check_continue_polling, 2698c2ecf20Sopenharmony_ci .max_touches = 10, 2708c2ecf20Sopenharmony_ci .has_calibrate_reg = true, 2718c2ecf20Sopenharmony_ci .has_pressure_reg = true, 2728c2ecf20Sopenharmony_ci}; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic bool ili210x_report_events(struct ili210x *priv, u8 *touchdata) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct input_dev *input = priv->input; 2778c2ecf20Sopenharmony_ci int i; 2788c2ecf20Sopenharmony_ci bool contact = false, touch; 2798c2ecf20Sopenharmony_ci unsigned int x = 0, y = 0, z = 0; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci for (i = 0; i < priv->chip->max_touches; i++) { 2828c2ecf20Sopenharmony_ci touch = priv->chip->parse_touch_data(touchdata, i, &x, &y, &z); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci input_mt_slot(input, i); 2858c2ecf20Sopenharmony_ci if (input_mt_report_slot_state(input, MT_TOOL_FINGER, touch)) { 2868c2ecf20Sopenharmony_ci touchscreen_report_pos(input, &priv->prop, x, y, true); 2878c2ecf20Sopenharmony_ci if (priv->chip->has_pressure_reg) 2888c2ecf20Sopenharmony_ci input_report_abs(input, ABS_MT_PRESSURE, z); 2898c2ecf20Sopenharmony_ci contact = true; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci input_mt_report_pointer_emulation(input, false); 2948c2ecf20Sopenharmony_ci input_sync(input); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return contact; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cistatic irqreturn_t ili210x_irq(int irq, void *irq_data) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct ili210x *priv = irq_data; 3028c2ecf20Sopenharmony_ci struct i2c_client *client = priv->client; 3038c2ecf20Sopenharmony_ci const struct ili2xxx_chip *chip = priv->chip; 3048c2ecf20Sopenharmony_ci u8 touchdata[ILI210X_DATA_SIZE] = { 0 }; 3058c2ecf20Sopenharmony_ci bool keep_polling; 3068c2ecf20Sopenharmony_ci bool touch; 3078c2ecf20Sopenharmony_ci int error; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci do { 3108c2ecf20Sopenharmony_ci error = chip->get_touch_data(client, touchdata); 3118c2ecf20Sopenharmony_ci if (error) { 3128c2ecf20Sopenharmony_ci dev_err(&client->dev, 3138c2ecf20Sopenharmony_ci "Unable to get touch data: %d\n", error); 3148c2ecf20Sopenharmony_ci break; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci touch = ili210x_report_events(priv, touchdata); 3188c2ecf20Sopenharmony_ci keep_polling = chip->continue_polling(touchdata, touch); 3198c2ecf20Sopenharmony_ci if (keep_polling) 3208c2ecf20Sopenharmony_ci msleep(ILI2XXX_POLL_PERIOD); 3218c2ecf20Sopenharmony_ci } while (!priv->stop && keep_polling); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_cistatic ssize_t ili210x_calibrate(struct device *dev, 3278c2ecf20Sopenharmony_ci struct device_attribute *attr, 3288c2ecf20Sopenharmony_ci const char *buf, size_t count) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3318c2ecf20Sopenharmony_ci struct ili210x *priv = i2c_get_clientdata(client); 3328c2ecf20Sopenharmony_ci unsigned long calibrate; 3338c2ecf20Sopenharmony_ci int rc; 3348c2ecf20Sopenharmony_ci u8 cmd = REG_CALIBRATE; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (kstrtoul(buf, 10, &calibrate)) 3378c2ecf20Sopenharmony_ci return -EINVAL; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci if (calibrate > 1) 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (calibrate) { 3438c2ecf20Sopenharmony_ci rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); 3448c2ecf20Sopenharmony_ci if (rc != sizeof(cmd)) 3458c2ecf20Sopenharmony_ci return -EIO; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return count; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_cistatic DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic struct attribute *ili210x_attributes[] = { 3538c2ecf20Sopenharmony_ci &dev_attr_calibrate.attr, 3548c2ecf20Sopenharmony_ci NULL, 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic umode_t ili210x_calibrate_visible(struct kobject *kobj, 3588c2ecf20Sopenharmony_ci struct attribute *attr, int index) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 3618c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3628c2ecf20Sopenharmony_ci struct ili210x *priv = i2c_get_clientdata(client); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci return priv->chip->has_calibrate_reg ? attr->mode : 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic const struct attribute_group ili210x_attr_group = { 3688c2ecf20Sopenharmony_ci .attrs = ili210x_attributes, 3698c2ecf20Sopenharmony_ci .is_visible = ili210x_calibrate_visible, 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic void ili210x_power_down(void *data) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio = data; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(reset_gpio, 1); 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_cistatic void ili210x_stop(void *data) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct ili210x *priv = data; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Tell ISR to quit even if there is a contact. */ 3848c2ecf20Sopenharmony_ci priv->stop = true; 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic int ili210x_i2c_probe(struct i2c_client *client, 3888c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 3918c2ecf20Sopenharmony_ci const struct ili2xxx_chip *chip; 3928c2ecf20Sopenharmony_ci struct ili210x *priv; 3938c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 3948c2ecf20Sopenharmony_ci struct input_dev *input; 3958c2ecf20Sopenharmony_ci int error; 3968c2ecf20Sopenharmony_ci unsigned int max_xy; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci chip = device_get_match_data(dev); 4018c2ecf20Sopenharmony_ci if (!chip && id) 4028c2ecf20Sopenharmony_ci chip = (const struct ili2xxx_chip *)id->driver_data; 4038c2ecf20Sopenharmony_ci if (!chip) { 4048c2ecf20Sopenharmony_ci dev_err(&client->dev, "unknown device model\n"); 4058c2ecf20Sopenharmony_ci return -ENODEV; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (client->irq <= 0) { 4098c2ecf20Sopenharmony_ci dev_err(dev, "No IRQ!\n"); 4108c2ecf20Sopenharmony_ci return -EINVAL; 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); 4148c2ecf20Sopenharmony_ci if (IS_ERR(reset_gpio)) 4158c2ecf20Sopenharmony_ci return PTR_ERR(reset_gpio); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (reset_gpio) { 4188c2ecf20Sopenharmony_ci error = devm_add_action_or_reset(dev, ili210x_power_down, 4198c2ecf20Sopenharmony_ci reset_gpio); 4208c2ecf20Sopenharmony_ci if (error) 4218c2ecf20Sopenharmony_ci return error; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci usleep_range(12000, 15000); 4248c2ecf20Sopenharmony_ci gpiod_set_value_cansleep(reset_gpio, 0); 4258c2ecf20Sopenharmony_ci msleep(160); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 4298c2ecf20Sopenharmony_ci if (!priv) 4308c2ecf20Sopenharmony_ci return -ENOMEM; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 4338c2ecf20Sopenharmony_ci if (!input) 4348c2ecf20Sopenharmony_ci return -ENOMEM; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci priv->client = client; 4378c2ecf20Sopenharmony_ci priv->input = input; 4388c2ecf20Sopenharmony_ci priv->reset_gpio = reset_gpio; 4398c2ecf20Sopenharmony_ci priv->chip = chip; 4408c2ecf20Sopenharmony_ci i2c_set_clientdata(client, priv); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Setup input device */ 4438c2ecf20Sopenharmony_ci input->name = "ILI210x Touchscreen"; 4448c2ecf20Sopenharmony_ci input->id.bustype = BUS_I2C; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Multi touch */ 4478c2ecf20Sopenharmony_ci max_xy = (chip->resolution ?: SZ_64K) - 1; 4488c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0); 4498c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0); 4508c2ecf20Sopenharmony_ci if (priv->chip->has_pressure_reg) 4518c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xa, 0, 0); 4528c2ecf20Sopenharmony_ci touchscreen_parse_properties(input, true, &priv->prop); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci error = input_mt_init_slots(input, priv->chip->max_touches, 4558c2ecf20Sopenharmony_ci INPUT_MT_DIRECT); 4568c2ecf20Sopenharmony_ci if (error) { 4578c2ecf20Sopenharmony_ci dev_err(dev, "Unable to set up slots, err: %d\n", error); 4588c2ecf20Sopenharmony_ci return error; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(dev, client->irq, NULL, ili210x_irq, 4628c2ecf20Sopenharmony_ci IRQF_ONESHOT, client->name, priv); 4638c2ecf20Sopenharmony_ci if (error) { 4648c2ecf20Sopenharmony_ci dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", 4658c2ecf20Sopenharmony_ci error); 4668c2ecf20Sopenharmony_ci return error; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci error = devm_add_action_or_reset(dev, ili210x_stop, priv); 4708c2ecf20Sopenharmony_ci if (error) 4718c2ecf20Sopenharmony_ci return error; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci error = devm_device_add_group(dev, &ili210x_attr_group); 4748c2ecf20Sopenharmony_ci if (error) { 4758c2ecf20Sopenharmony_ci dev_err(dev, "Unable to create sysfs attributes, err: %d\n", 4768c2ecf20Sopenharmony_ci error); 4778c2ecf20Sopenharmony_ci return error; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci error = input_register_device(priv->input); 4818c2ecf20Sopenharmony_ci if (error) { 4828c2ecf20Sopenharmony_ci dev_err(dev, "Cannot register input device, err: %d\n", error); 4838c2ecf20Sopenharmony_ci return error; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return 0; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct i2c_device_id ili210x_i2c_id[] = { 4908c2ecf20Sopenharmony_ci { "ili210x", (long)&ili210x_chip }, 4918c2ecf20Sopenharmony_ci { "ili2117", (long)&ili211x_chip }, 4928c2ecf20Sopenharmony_ci { "ili2120", (long)&ili212x_chip }, 4938c2ecf20Sopenharmony_ci { "ili251x", (long)&ili251x_chip }, 4948c2ecf20Sopenharmony_ci { } 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic const struct of_device_id ili210x_dt_ids[] = { 4998c2ecf20Sopenharmony_ci { .compatible = "ilitek,ili210x", .data = &ili210x_chip }, 5008c2ecf20Sopenharmony_ci { .compatible = "ilitek,ili2117", .data = &ili211x_chip }, 5018c2ecf20Sopenharmony_ci { .compatible = "ilitek,ili2120", .data = &ili212x_chip }, 5028c2ecf20Sopenharmony_ci { .compatible = "ilitek,ili251x", .data = &ili251x_chip }, 5038c2ecf20Sopenharmony_ci { } 5048c2ecf20Sopenharmony_ci}; 5058c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ili210x_dt_ids); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic struct i2c_driver ili210x_ts_driver = { 5088c2ecf20Sopenharmony_ci .driver = { 5098c2ecf20Sopenharmony_ci .name = "ili210x_i2c", 5108c2ecf20Sopenharmony_ci .of_match_table = ili210x_dt_ids, 5118c2ecf20Sopenharmony_ci }, 5128c2ecf20Sopenharmony_ci .id_table = ili210x_i2c_id, 5138c2ecf20Sopenharmony_ci .probe = ili210x_i2c_probe, 5148c2ecf20Sopenharmony_ci}; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cimodule_i2c_driver(ili210x_ts_driver); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); 5198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); 5208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 521