13eb66d9fSLinus Walleij // SPDX-License-Identifier: GPL-2.0-only 23eb66d9fSLinus Walleij /* 33eb66d9fSLinus Walleij * Driver for Cypress CY8CTMA140 (TMA140) touchscreen 43eb66d9fSLinus Walleij * (C) 2020 Linus Walleij <linus.walleij@linaro.org> 53eb66d9fSLinus Walleij * (C) 2007 Cypress 63eb66d9fSLinus Walleij * (C) 2007 Google, Inc. 73eb66d9fSLinus Walleij * 83eb66d9fSLinus Walleij * Inspired by the tma140_skomer.c driver in the Samsung GT-S7710 code 93eb66d9fSLinus Walleij * drop. The GT-S7710 is codenamed "Skomer", the code also indicates 103eb66d9fSLinus Walleij * that the same touchscreen was used in a product called "Lucas". 113eb66d9fSLinus Walleij * 123eb66d9fSLinus Walleij * The code drop for GT-S7710 also contains a firmware downloader and 133eb66d9fSLinus Walleij * 15 (!) versions of the firmware drop from Cypress. But here we assume 143eb66d9fSLinus Walleij * the firmware got downloaded to the touchscreen flash successfully and 153eb66d9fSLinus Walleij * just use it to read the fingers. The shipped vendor driver does the 163eb66d9fSLinus Walleij * same. 173eb66d9fSLinus Walleij */ 183eb66d9fSLinus Walleij 193eb66d9fSLinus Walleij #include <asm/unaligned.h> 203eb66d9fSLinus Walleij #include <linux/module.h> 213eb66d9fSLinus Walleij #include <linux/kernel.h> 223eb66d9fSLinus Walleij #include <linux/input.h> 233eb66d9fSLinus Walleij #include <linux/input/touchscreen.h> 243eb66d9fSLinus Walleij #include <linux/input/mt.h> 253eb66d9fSLinus Walleij #include <linux/slab.h> 263eb66d9fSLinus Walleij #include <linux/interrupt.h> 273eb66d9fSLinus Walleij #include <linux/io.h> 283eb66d9fSLinus Walleij #include <linux/i2c.h> 293eb66d9fSLinus Walleij #include <linux/regulator/consumer.h> 303eb66d9fSLinus Walleij #include <linux/delay.h> 313eb66d9fSLinus Walleij 323eb66d9fSLinus Walleij #define CY8CTMA140_NAME "cy8ctma140" 333eb66d9fSLinus Walleij 343eb66d9fSLinus Walleij #define CY8CTMA140_MAX_FINGERS 4 353eb66d9fSLinus Walleij 363eb66d9fSLinus Walleij #define CY8CTMA140_GET_FINGERS 0x00 373eb66d9fSLinus Walleij #define CY8CTMA140_GET_FW_INFO 0x19 383eb66d9fSLinus Walleij 393eb66d9fSLinus Walleij /* This message also fits some bytes for touchkeys, if used */ 403eb66d9fSLinus Walleij #define CY8CTMA140_PACKET_SIZE 31 413eb66d9fSLinus Walleij 423eb66d9fSLinus Walleij #define CY8CTMA140_INVALID_BUFFER_BIT 5 433eb66d9fSLinus Walleij 443eb66d9fSLinus Walleij struct cy8ctma140 { 453eb66d9fSLinus Walleij struct input_dev *input; 463eb66d9fSLinus Walleij struct touchscreen_properties props; 473eb66d9fSLinus Walleij struct device *dev; 483eb66d9fSLinus Walleij struct i2c_client *client; 493eb66d9fSLinus Walleij struct regulator_bulk_data regulators[2]; 503eb66d9fSLinus Walleij u8 prev_fingers; 513eb66d9fSLinus Walleij u8 prev_f1id; 523eb66d9fSLinus Walleij u8 prev_f2id; 533eb66d9fSLinus Walleij }; 543eb66d9fSLinus Walleij 553eb66d9fSLinus Walleij static void cy8ctma140_report(struct cy8ctma140 *ts, u8 *data, int n_fingers) 563eb66d9fSLinus Walleij { 573eb66d9fSLinus Walleij static const u8 contact_offsets[] = { 0x03, 0x09, 0x10, 0x16 }; 583eb66d9fSLinus Walleij u8 *buf; 593eb66d9fSLinus Walleij u16 x, y; 603eb66d9fSLinus Walleij u8 w; 613eb66d9fSLinus Walleij u8 id; 623eb66d9fSLinus Walleij int slot; 633eb66d9fSLinus Walleij int i; 643eb66d9fSLinus Walleij 653eb66d9fSLinus Walleij for (i = 0; i < n_fingers; i++) { 663eb66d9fSLinus Walleij buf = &data[contact_offsets[i]]; 673eb66d9fSLinus Walleij 683eb66d9fSLinus Walleij /* 693eb66d9fSLinus Walleij * Odd contacts have contact ID in the lower nibble of 703eb66d9fSLinus Walleij * the preceding byte, whereas even contacts have it in 713eb66d9fSLinus Walleij * the upper nibble of the following byte. 723eb66d9fSLinus Walleij */ 733eb66d9fSLinus Walleij id = i % 2 ? buf[-1] & 0x0f : buf[5] >> 4; 743eb66d9fSLinus Walleij slot = input_mt_get_slot_by_key(ts->input, id); 753eb66d9fSLinus Walleij if (slot < 0) 763eb66d9fSLinus Walleij continue; 773eb66d9fSLinus Walleij 783eb66d9fSLinus Walleij x = get_unaligned_be16(buf); 793eb66d9fSLinus Walleij y = get_unaligned_be16(buf + 2); 803eb66d9fSLinus Walleij w = buf[4]; 813eb66d9fSLinus Walleij 823eb66d9fSLinus Walleij dev_dbg(ts->dev, "finger %d: ID %02x (%d, %d) w: %d\n", 833eb66d9fSLinus Walleij slot, id, x, y, w); 843eb66d9fSLinus Walleij 853eb66d9fSLinus Walleij input_mt_slot(ts->input, slot); 863eb66d9fSLinus Walleij input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); 873eb66d9fSLinus Walleij touchscreen_report_pos(ts->input, &ts->props, x, y, true); 883eb66d9fSLinus Walleij input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, w); 893eb66d9fSLinus Walleij } 903eb66d9fSLinus Walleij 913eb66d9fSLinus Walleij input_mt_sync_frame(ts->input); 923eb66d9fSLinus Walleij input_sync(ts->input); 933eb66d9fSLinus Walleij } 943eb66d9fSLinus Walleij 953eb66d9fSLinus Walleij static irqreturn_t cy8ctma140_irq_thread(int irq, void *d) 963eb66d9fSLinus Walleij { 973eb66d9fSLinus Walleij struct cy8ctma140 *ts = d; 983eb66d9fSLinus Walleij u8 cmdbuf[] = { CY8CTMA140_GET_FINGERS }; 993eb66d9fSLinus Walleij u8 buf[CY8CTMA140_PACKET_SIZE]; 1003eb66d9fSLinus Walleij struct i2c_msg msg[] = { 1013eb66d9fSLinus Walleij { 1023eb66d9fSLinus Walleij .addr = ts->client->addr, 1033eb66d9fSLinus Walleij .flags = 0, 1043eb66d9fSLinus Walleij .len = sizeof(cmdbuf), 1053eb66d9fSLinus Walleij .buf = cmdbuf, 1063eb66d9fSLinus Walleij }, { 1073eb66d9fSLinus Walleij .addr = ts->client->addr, 1083eb66d9fSLinus Walleij .flags = I2C_M_RD, 1093eb66d9fSLinus Walleij .len = sizeof(buf), 1103eb66d9fSLinus Walleij .buf = buf, 1113eb66d9fSLinus Walleij }, 1123eb66d9fSLinus Walleij }; 1133eb66d9fSLinus Walleij u8 n_fingers; 1143eb66d9fSLinus Walleij int ret; 1153eb66d9fSLinus Walleij 1163eb66d9fSLinus Walleij ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); 1173eb66d9fSLinus Walleij if (ret != ARRAY_SIZE(msg)) { 1183eb66d9fSLinus Walleij if (ret < 0) 1193eb66d9fSLinus Walleij dev_err(ts->dev, "error reading message: %d\n", ret); 1203eb66d9fSLinus Walleij else 1213eb66d9fSLinus Walleij dev_err(ts->dev, "wrong number of messages\n"); 1223eb66d9fSLinus Walleij goto out; 1233eb66d9fSLinus Walleij } 1243eb66d9fSLinus Walleij 1253eb66d9fSLinus Walleij if (buf[1] & BIT(CY8CTMA140_INVALID_BUFFER_BIT)) { 1263eb66d9fSLinus Walleij dev_dbg(ts->dev, "invalid event\n"); 1273eb66d9fSLinus Walleij goto out; 1283eb66d9fSLinus Walleij } 1293eb66d9fSLinus Walleij 1303eb66d9fSLinus Walleij n_fingers = buf[2] & 0x0f; 1313eb66d9fSLinus Walleij if (n_fingers > CY8CTMA140_MAX_FINGERS) { 1323eb66d9fSLinus Walleij dev_err(ts->dev, "unexpected number of fingers: %d\n", 1333eb66d9fSLinus Walleij n_fingers); 1343eb66d9fSLinus Walleij goto out; 1353eb66d9fSLinus Walleij } 1363eb66d9fSLinus Walleij 1373eb66d9fSLinus Walleij cy8ctma140_report(ts, buf, n_fingers); 1383eb66d9fSLinus Walleij 1393eb66d9fSLinus Walleij out: 1403eb66d9fSLinus Walleij return IRQ_HANDLED; 1413eb66d9fSLinus Walleij } 1423eb66d9fSLinus Walleij 1433eb66d9fSLinus Walleij static int cy8ctma140_init(struct cy8ctma140 *ts) 1443eb66d9fSLinus Walleij { 1453eb66d9fSLinus Walleij u8 addr[1]; 1463eb66d9fSLinus Walleij u8 buf[5]; 1473eb66d9fSLinus Walleij int ret; 1483eb66d9fSLinus Walleij 1493eb66d9fSLinus Walleij addr[0] = CY8CTMA140_GET_FW_INFO; 1503eb66d9fSLinus Walleij ret = i2c_master_send(ts->client, addr, 1); 1513eb66d9fSLinus Walleij if (ret < 0) { 1523eb66d9fSLinus Walleij dev_err(ts->dev, "error sending FW info message\n"); 1533eb66d9fSLinus Walleij return ret; 1543eb66d9fSLinus Walleij } 1553eb66d9fSLinus Walleij ret = i2c_master_recv(ts->client, buf, 5); 1563eb66d9fSLinus Walleij if (ret < 0) { 1573eb66d9fSLinus Walleij dev_err(ts->dev, "error receiving FW info message\n"); 1583eb66d9fSLinus Walleij return ret; 1593eb66d9fSLinus Walleij } 1603eb66d9fSLinus Walleij if (ret != 5) { 1613eb66d9fSLinus Walleij dev_err(ts->dev, "got only %d bytes\n", ret); 1623eb66d9fSLinus Walleij return -EIO; 1633eb66d9fSLinus Walleij } 1643eb66d9fSLinus Walleij 1653eb66d9fSLinus Walleij dev_dbg(ts->dev, "vendor %c%c, HW ID %.2d, FW ver %.4d\n", 1663eb66d9fSLinus Walleij buf[0], buf[1], buf[3], buf[4]); 1673eb66d9fSLinus Walleij 1683eb66d9fSLinus Walleij return 0; 1693eb66d9fSLinus Walleij } 1703eb66d9fSLinus Walleij 1713eb66d9fSLinus Walleij static int cy8ctma140_power_up(struct cy8ctma140 *ts) 1723eb66d9fSLinus Walleij { 1733eb66d9fSLinus Walleij int error; 1743eb66d9fSLinus Walleij 1753eb66d9fSLinus Walleij error = regulator_bulk_enable(ARRAY_SIZE(ts->regulators), 1763eb66d9fSLinus Walleij ts->regulators); 1773eb66d9fSLinus Walleij if (error) { 1783eb66d9fSLinus Walleij dev_err(ts->dev, "failed to enable regulators\n"); 1793eb66d9fSLinus Walleij return error; 1803eb66d9fSLinus Walleij } 1813eb66d9fSLinus Walleij 1823eb66d9fSLinus Walleij msleep(250); 1833eb66d9fSLinus Walleij 1843eb66d9fSLinus Walleij return 0; 1853eb66d9fSLinus Walleij } 1863eb66d9fSLinus Walleij 1873eb66d9fSLinus Walleij static void cy8ctma140_power_down(struct cy8ctma140 *ts) 1883eb66d9fSLinus Walleij { 1893eb66d9fSLinus Walleij regulator_bulk_disable(ARRAY_SIZE(ts->regulators), 1903eb66d9fSLinus Walleij ts->regulators); 1913eb66d9fSLinus Walleij } 1923eb66d9fSLinus Walleij 1933eb66d9fSLinus Walleij /* Called from the registered devm action */ 1943eb66d9fSLinus Walleij static void cy8ctma140_power_off_action(void *d) 1953eb66d9fSLinus Walleij { 1963eb66d9fSLinus Walleij struct cy8ctma140 *ts = d; 1973eb66d9fSLinus Walleij 1983eb66d9fSLinus Walleij cy8ctma140_power_down(ts); 1993eb66d9fSLinus Walleij } 2003eb66d9fSLinus Walleij 2014baa3011SUwe Kleine-König static int cy8ctma140_probe(struct i2c_client *client) 2023eb66d9fSLinus Walleij { 2033eb66d9fSLinus Walleij struct cy8ctma140 *ts; 2043eb66d9fSLinus Walleij struct input_dev *input; 2053eb66d9fSLinus Walleij struct device *dev = &client->dev; 2063eb66d9fSLinus Walleij int error; 2073eb66d9fSLinus Walleij 2083eb66d9fSLinus Walleij ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); 2093eb66d9fSLinus Walleij if (!ts) 2103eb66d9fSLinus Walleij return -ENOMEM; 2113eb66d9fSLinus Walleij 2123eb66d9fSLinus Walleij input = devm_input_allocate_device(dev); 2133eb66d9fSLinus Walleij if (!input) 2143eb66d9fSLinus Walleij return -ENOMEM; 2153eb66d9fSLinus Walleij 2163eb66d9fSLinus Walleij ts->dev = dev; 2173eb66d9fSLinus Walleij ts->client = client; 2183eb66d9fSLinus Walleij ts->input = input; 2193eb66d9fSLinus Walleij 2203eb66d9fSLinus Walleij input_set_capability(input, EV_ABS, ABS_MT_POSITION_X); 2213eb66d9fSLinus Walleij input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y); 2223eb66d9fSLinus Walleij /* One byte for width 0..255 so this is the limit */ 2233eb66d9fSLinus Walleij input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); 2243eb66d9fSLinus Walleij /* 2253eb66d9fSLinus Walleij * This sets up event max/min capabilities and fuzz. 2263eb66d9fSLinus Walleij * Some DT properties are compulsory so we do not need 2273eb66d9fSLinus Walleij * to provide defaults for X/Y max or pressure max. 2283eb66d9fSLinus Walleij * 2293eb66d9fSLinus Walleij * We just initialize a very simple MT touchscreen here, 2303eb66d9fSLinus Walleij * some devices use the capability of this touchscreen to 2313eb66d9fSLinus Walleij * provide touchkeys, and in that case this needs to be 2323eb66d9fSLinus Walleij * extended to handle touchkey input. 2333eb66d9fSLinus Walleij * 2343eb66d9fSLinus Walleij * The firmware takes care of finger tracking and dropping 2353eb66d9fSLinus Walleij * invalid ranges. 2363eb66d9fSLinus Walleij */ 2373eb66d9fSLinus Walleij touchscreen_parse_properties(input, true, &ts->props); 2383eb66d9fSLinus Walleij input_abs_set_fuzz(input, ABS_MT_POSITION_X, 0); 2393eb66d9fSLinus Walleij input_abs_set_fuzz(input, ABS_MT_POSITION_Y, 0); 2403eb66d9fSLinus Walleij 2413eb66d9fSLinus Walleij error = input_mt_init_slots(input, CY8CTMA140_MAX_FINGERS, 2423eb66d9fSLinus Walleij INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); 2433eb66d9fSLinus Walleij if (error) 2443eb66d9fSLinus Walleij return error; 2453eb66d9fSLinus Walleij 2463eb66d9fSLinus Walleij input->name = CY8CTMA140_NAME; 2473eb66d9fSLinus Walleij input->id.bustype = BUS_I2C; 2483eb66d9fSLinus Walleij input_set_drvdata(input, ts); 2493eb66d9fSLinus Walleij 2503eb66d9fSLinus Walleij /* 2513eb66d9fSLinus Walleij * VCPIN is the analog voltage supply 2523eb66d9fSLinus Walleij * VDD is the digital voltage supply 2533eb66d9fSLinus Walleij * since the voltage range of VDD overlaps that of VCPIN, 2543eb66d9fSLinus Walleij * many designs to just supply both with a single voltage 2553eb66d9fSLinus Walleij * source of ~3.3 V. 2563eb66d9fSLinus Walleij */ 2573eb66d9fSLinus Walleij ts->regulators[0].supply = "vcpin"; 2583eb66d9fSLinus Walleij ts->regulators[1].supply = "vdd"; 2593eb66d9fSLinus Walleij error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators), 2603eb66d9fSLinus Walleij ts->regulators); 2613eb66d9fSLinus Walleij if (error) { 2623eb66d9fSLinus Walleij if (error != -EPROBE_DEFER) 2633eb66d9fSLinus Walleij dev_err(dev, "Failed to get regulators %d\n", 2643eb66d9fSLinus Walleij error); 2653eb66d9fSLinus Walleij return error; 2663eb66d9fSLinus Walleij } 2673eb66d9fSLinus Walleij 2683eb66d9fSLinus Walleij error = cy8ctma140_power_up(ts); 2693eb66d9fSLinus Walleij if (error) 2703eb66d9fSLinus Walleij return error; 2713eb66d9fSLinus Walleij 2723eb66d9fSLinus Walleij error = devm_add_action_or_reset(dev, cy8ctma140_power_off_action, ts); 2733eb66d9fSLinus Walleij if (error) { 2743eb66d9fSLinus Walleij dev_err(dev, "failed to install power off handler\n"); 2753eb66d9fSLinus Walleij return error; 2763eb66d9fSLinus Walleij } 2773eb66d9fSLinus Walleij 2783eb66d9fSLinus Walleij error = devm_request_threaded_irq(dev, client->irq, 2793eb66d9fSLinus Walleij NULL, cy8ctma140_irq_thread, 2803eb66d9fSLinus Walleij IRQF_ONESHOT, CY8CTMA140_NAME, ts); 2813eb66d9fSLinus Walleij if (error) { 2823eb66d9fSLinus Walleij dev_err(dev, "irq %d busy? error %d\n", client->irq, error); 2833eb66d9fSLinus Walleij return error; 2843eb66d9fSLinus Walleij } 2853eb66d9fSLinus Walleij 2863eb66d9fSLinus Walleij error = cy8ctma140_init(ts); 2873eb66d9fSLinus Walleij if (error) 2883eb66d9fSLinus Walleij return error; 2893eb66d9fSLinus Walleij 2903eb66d9fSLinus Walleij error = input_register_device(input); 2913eb66d9fSLinus Walleij if (error) 2923eb66d9fSLinus Walleij return error; 2933eb66d9fSLinus Walleij 2943eb66d9fSLinus Walleij i2c_set_clientdata(client, ts); 2953eb66d9fSLinus Walleij 2963eb66d9fSLinus Walleij return 0; 2973eb66d9fSLinus Walleij } 2983eb66d9fSLinus Walleij 29902998590SJonathan Cameron static int cy8ctma140_suspend(struct device *dev) 3003eb66d9fSLinus Walleij { 3013eb66d9fSLinus Walleij struct i2c_client *client = to_i2c_client(dev); 3023eb66d9fSLinus Walleij struct cy8ctma140 *ts = i2c_get_clientdata(client); 3033eb66d9fSLinus Walleij 3043eb66d9fSLinus Walleij if (!device_may_wakeup(&client->dev)) 3053eb66d9fSLinus Walleij cy8ctma140_power_down(ts); 3063eb66d9fSLinus Walleij 3073eb66d9fSLinus Walleij return 0; 3083eb66d9fSLinus Walleij } 3093eb66d9fSLinus Walleij 31002998590SJonathan Cameron static int cy8ctma140_resume(struct device *dev) 3113eb66d9fSLinus Walleij { 3123eb66d9fSLinus Walleij struct i2c_client *client = to_i2c_client(dev); 3133eb66d9fSLinus Walleij struct cy8ctma140 *ts = i2c_get_clientdata(client); 3143eb66d9fSLinus Walleij int error; 3153eb66d9fSLinus Walleij 3163eb66d9fSLinus Walleij if (!device_may_wakeup(&client->dev)) { 3173eb66d9fSLinus Walleij error = cy8ctma140_power_up(ts); 3183eb66d9fSLinus Walleij if (error) 3193eb66d9fSLinus Walleij return error; 3203eb66d9fSLinus Walleij } 3213eb66d9fSLinus Walleij 3223eb66d9fSLinus Walleij return 0; 3233eb66d9fSLinus Walleij } 3243eb66d9fSLinus Walleij 32502998590SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(cy8ctma140_pm, 32602998590SJonathan Cameron cy8ctma140_suspend, cy8ctma140_resume); 3273eb66d9fSLinus Walleij 3283eb66d9fSLinus Walleij static const struct i2c_device_id cy8ctma140_idtable[] = { 3293eb66d9fSLinus Walleij { CY8CTMA140_NAME, 0 }, 3303eb66d9fSLinus Walleij { /* sentinel */ } 3313eb66d9fSLinus Walleij }; 3323eb66d9fSLinus Walleij MODULE_DEVICE_TABLE(i2c, cy8ctma140_idtable); 3333eb66d9fSLinus Walleij 3343eb66d9fSLinus Walleij static const struct of_device_id cy8ctma140_of_match[] = { 3353eb66d9fSLinus Walleij { .compatible = "cypress,cy8ctma140", }, 3363eb66d9fSLinus Walleij { /* sentinel */ } 3373eb66d9fSLinus Walleij }; 3383eb66d9fSLinus Walleij MODULE_DEVICE_TABLE(of, cy8ctma140_of_match); 3393eb66d9fSLinus Walleij 3403eb66d9fSLinus Walleij static struct i2c_driver cy8ctma140_driver = { 3413eb66d9fSLinus Walleij .driver = { 3423eb66d9fSLinus Walleij .name = CY8CTMA140_NAME, 34302998590SJonathan Cameron .pm = pm_sleep_ptr(&cy8ctma140_pm), 3443eb66d9fSLinus Walleij .of_match_table = cy8ctma140_of_match, 3453eb66d9fSLinus Walleij }, 3463eb66d9fSLinus Walleij .id_table = cy8ctma140_idtable, 347*d8bde56dSUwe Kleine-König .probe = cy8ctma140_probe, 3483eb66d9fSLinus Walleij }; 3493eb66d9fSLinus Walleij module_i2c_driver(cy8ctma140_driver); 3503eb66d9fSLinus Walleij 3513eb66d9fSLinus Walleij MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); 3523eb66d9fSLinus Walleij MODULE_DESCRIPTION("CY8CTMA140 TouchScreen Driver"); 3533eb66d9fSLinus Walleij MODULE_LICENSE("GPL v2"); 354