18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Touch Screen driver for EETI's I2C connected touch screen panels 48c2ecf20Sopenharmony_ci * Copyright (c) 2009,2018 Daniel Mack <daniel@zonque.org> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * See EETI's software guide for the protocol specification: 78c2ecf20Sopenharmony_ci * http://home.eeti.com.tw/documentation.html 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Based on migor_ts.c 108c2ecf20Sopenharmony_ci * Copyright (c) 2008 Magnus Damm 118c2ecf20Sopenharmony_ci * Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/input.h> 178c2ecf20Sopenharmony_ci#include <linux/input/touchscreen.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/i2c.h> 208c2ecf20Sopenharmony_ci#include <linux/timer.h> 218c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistruct eeti_ts { 278c2ecf20Sopenharmony_ci struct i2c_client *client; 288c2ecf20Sopenharmony_ci struct input_dev *input; 298c2ecf20Sopenharmony_ci struct gpio_desc *attn_gpio; 308c2ecf20Sopenharmony_ci struct touchscreen_properties props; 318c2ecf20Sopenharmony_ci struct mutex mutex; 328c2ecf20Sopenharmony_ci bool running; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define EETI_TS_BITDEPTH (11) 368c2ecf20Sopenharmony_ci#define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define REPORT_BIT_PRESSED BIT(0) 398c2ecf20Sopenharmony_ci#define REPORT_BIT_AD0 BIT(1) 408c2ecf20Sopenharmony_ci#define REPORT_BIT_AD1 BIT(2) 418c2ecf20Sopenharmony_ci#define REPORT_BIT_HAS_PRESSURE BIT(6) 428c2ecf20Sopenharmony_ci#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic void eeti_ts_report_event(struct eeti_ts *eeti, u8 *buf) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci unsigned int res; 478c2ecf20Sopenharmony_ci u16 x, y; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1)); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci x = get_unaligned_be16(&buf[1]); 528c2ecf20Sopenharmony_ci y = get_unaligned_be16(&buf[3]); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci /* fix the range to 11 bits */ 558c2ecf20Sopenharmony_ci x >>= res - EETI_TS_BITDEPTH; 568c2ecf20Sopenharmony_ci y >>= res - EETI_TS_BITDEPTH; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (buf[0] & REPORT_BIT_HAS_PRESSURE) 598c2ecf20Sopenharmony_ci input_report_abs(eeti->input, ABS_PRESSURE, buf[5]); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci touchscreen_report_pos(eeti->input, &eeti->props, x, y, false); 628c2ecf20Sopenharmony_ci input_report_key(eeti->input, BTN_TOUCH, buf[0] & REPORT_BIT_PRESSED); 638c2ecf20Sopenharmony_ci input_sync(eeti->input); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int eeti_ts_read(struct eeti_ts *eeti) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci int len, error; 698c2ecf20Sopenharmony_ci char buf[6]; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci len = i2c_master_recv(eeti->client, buf, sizeof(buf)); 728c2ecf20Sopenharmony_ci if (len != sizeof(buf)) { 738c2ecf20Sopenharmony_ci error = len < 0 ? len : -EIO; 748c2ecf20Sopenharmony_ci dev_err(&eeti->client->dev, 758c2ecf20Sopenharmony_ci "failed to read touchscreen data: %d\n", 768c2ecf20Sopenharmony_ci error); 778c2ecf20Sopenharmony_ci return error; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci /* Motion packet */ 818c2ecf20Sopenharmony_ci if (buf[0] & 0x80) 828c2ecf20Sopenharmony_ci eeti_ts_report_event(eeti, buf); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic irqreturn_t eeti_ts_isr(int irq, void *dev_id) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci struct eeti_ts *eeti = dev_id; 908c2ecf20Sopenharmony_ci int error; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci mutex_lock(&eeti->mutex); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci do { 958c2ecf20Sopenharmony_ci /* 968c2ecf20Sopenharmony_ci * If we have attention GPIO, trust it. Otherwise we'll read 978c2ecf20Sopenharmony_ci * once and exit. We assume that in this case we are using 988c2ecf20Sopenharmony_ci * level triggered interrupt and it will get raised again 998c2ecf20Sopenharmony_ci * if/when there is more data. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci if (eeti->attn_gpio && 1028c2ecf20Sopenharmony_ci !gpiod_get_value_cansleep(eeti->attn_gpio)) { 1038c2ecf20Sopenharmony_ci break; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci error = eeti_ts_read(eeti); 1078c2ecf20Sopenharmony_ci if (error) 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci } while (eeti->running && eeti->attn_gpio); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci mutex_unlock(&eeti->mutex); 1138c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void eeti_ts_start(struct eeti_ts *eeti) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci mutex_lock(&eeti->mutex); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci eeti->running = true; 1218c2ecf20Sopenharmony_ci enable_irq(eeti->client->irq); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* 1248c2ecf20Sopenharmony_ci * Kick the controller in case we are using edge interrupt and 1258c2ecf20Sopenharmony_ci * we missed our edge while interrupt was disabled. We expect 1268c2ecf20Sopenharmony_ci * the attention GPIO to be wired in this case. 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ci if (eeti->attn_gpio && gpiod_get_value_cansleep(eeti->attn_gpio)) 1298c2ecf20Sopenharmony_ci eeti_ts_read(eeti); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci mutex_unlock(&eeti->mutex); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void eeti_ts_stop(struct eeti_ts *eeti) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci /* 1378c2ecf20Sopenharmony_ci * Not locking here, just setting a flag and expect that the 1388c2ecf20Sopenharmony_ci * interrupt thread will notice the flag eventually. 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_ci eeti->running = false; 1418c2ecf20Sopenharmony_ci wmb(); 1428c2ecf20Sopenharmony_ci disable_irq(eeti->client->irq); 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int eeti_ts_open(struct input_dev *dev) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct eeti_ts *eeti = input_get_drvdata(dev); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci eeti_ts_start(eeti); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void eeti_ts_close(struct input_dev *dev) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct eeti_ts *eeti = input_get_drvdata(dev); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci eeti_ts_stop(eeti); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int eeti_ts_probe(struct i2c_client *client, 1628c2ecf20Sopenharmony_ci const struct i2c_device_id *idp) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 1658c2ecf20Sopenharmony_ci struct eeti_ts *eeti; 1668c2ecf20Sopenharmony_ci struct input_dev *input; 1678c2ecf20Sopenharmony_ci int error; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * In contrast to what's described in the datasheet, there seems 1718c2ecf20Sopenharmony_ci * to be no way of probing the presence of that device using I2C 1728c2ecf20Sopenharmony_ci * commands. So we need to blindly believe it is there, and wait 1738c2ecf20Sopenharmony_ci * for interrupts to occur. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci eeti = devm_kzalloc(dev, sizeof(*eeti), GFP_KERNEL); 1778c2ecf20Sopenharmony_ci if (!eeti) { 1788c2ecf20Sopenharmony_ci dev_err(dev, "failed to allocate driver data\n"); 1798c2ecf20Sopenharmony_ci return -ENOMEM; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci mutex_init(&eeti->mutex); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 1858c2ecf20Sopenharmony_ci if (!input) { 1868c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate input device.\n"); 1878c2ecf20Sopenharmony_ci return -ENOMEM; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci input_set_capability(input, EV_KEY, BTN_TOUCH); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0); 1938c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0); 1948c2ecf20Sopenharmony_ci input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci touchscreen_parse_properties(input, false, &eeti->props); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci input->name = client->name; 1998c2ecf20Sopenharmony_ci input->id.bustype = BUS_I2C; 2008c2ecf20Sopenharmony_ci input->open = eeti_ts_open; 2018c2ecf20Sopenharmony_ci input->close = eeti_ts_close; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci eeti->client = client; 2048c2ecf20Sopenharmony_ci eeti->input = input; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci eeti->attn_gpio = devm_gpiod_get_optional(dev, "attn", GPIOD_IN); 2078c2ecf20Sopenharmony_ci if (IS_ERR(eeti->attn_gpio)) 2088c2ecf20Sopenharmony_ci return PTR_ERR(eeti->attn_gpio); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci i2c_set_clientdata(client, eeti); 2118c2ecf20Sopenharmony_ci input_set_drvdata(input, eeti); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci error = devm_request_threaded_irq(dev, client->irq, 2148c2ecf20Sopenharmony_ci NULL, eeti_ts_isr, 2158c2ecf20Sopenharmony_ci IRQF_ONESHOT, 2168c2ecf20Sopenharmony_ci client->name, eeti); 2178c2ecf20Sopenharmony_ci if (error) { 2188c2ecf20Sopenharmony_ci dev_err(dev, "Unable to request touchscreen IRQ: %d\n", 2198c2ecf20Sopenharmony_ci error); 2208c2ecf20Sopenharmony_ci return error; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* 2248c2ecf20Sopenharmony_ci * Disable the device for now. It will be enabled once the 2258c2ecf20Sopenharmony_ci * input device is opened. 2268c2ecf20Sopenharmony_ci */ 2278c2ecf20Sopenharmony_ci eeti_ts_stop(eeti); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci error = input_register_device(input); 2308c2ecf20Sopenharmony_ci if (error) 2318c2ecf20Sopenharmony_ci return error; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return 0; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int __maybe_unused eeti_ts_suspend(struct device *dev) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2398c2ecf20Sopenharmony_ci struct eeti_ts *eeti = i2c_get_clientdata(client); 2408c2ecf20Sopenharmony_ci struct input_dev *input_dev = eeti->input; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci mutex_lock(&input_dev->mutex); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (input_dev->users) 2458c2ecf20Sopenharmony_ci eeti_ts_stop(eeti); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci mutex_unlock(&input_dev->mutex); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (device_may_wakeup(&client->dev)) 2508c2ecf20Sopenharmony_ci enable_irq_wake(client->irq); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci return 0; 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic int __maybe_unused eeti_ts_resume(struct device *dev) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 2588c2ecf20Sopenharmony_ci struct eeti_ts *eeti = i2c_get_clientdata(client); 2598c2ecf20Sopenharmony_ci struct input_dev *input_dev = eeti->input; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (device_may_wakeup(&client->dev)) 2628c2ecf20Sopenharmony_ci disable_irq_wake(client->irq); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_lock(&input_dev->mutex); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (input_dev->users) 2678c2ecf20Sopenharmony_ci eeti_ts_start(eeti); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci mutex_unlock(&input_dev->mutex); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci return 0; 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic const struct i2c_device_id eeti_ts_id[] = { 2778c2ecf20Sopenharmony_ci { "eeti_ts", 0 }, 2788c2ecf20Sopenharmony_ci { } 2798c2ecf20Sopenharmony_ci}; 2808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, eeti_ts_id); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2838c2ecf20Sopenharmony_cistatic const struct of_device_id of_eeti_ts_match[] = { 2848c2ecf20Sopenharmony_ci { .compatible = "eeti,exc3000-i2c", }, 2858c2ecf20Sopenharmony_ci { } 2868c2ecf20Sopenharmony_ci}; 2878c2ecf20Sopenharmony_ci#endif 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic struct i2c_driver eeti_ts_driver = { 2908c2ecf20Sopenharmony_ci .driver = { 2918c2ecf20Sopenharmony_ci .name = "eeti_ts", 2928c2ecf20Sopenharmony_ci .pm = &eeti_ts_pm, 2938c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(of_eeti_ts_match), 2948c2ecf20Sopenharmony_ci }, 2958c2ecf20Sopenharmony_ci .probe = eeti_ts_probe, 2968c2ecf20Sopenharmony_ci .id_table = eeti_ts_id, 2978c2ecf20Sopenharmony_ci}; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cimodule_i2c_driver(eeti_ts_driver); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EETI Touchscreen driver"); 3028c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Mack <daniel@zonque.org>"); 3038c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 304