18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Touchscreen driver for the TS-4800 board 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2015 - Savoir-faire Linux 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 78c2ecf20Sopenharmony_ci * License version 2. This program is licensed "as is" without any 88c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/input.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* polling interval in ms */ 228c2ecf20Sopenharmony_ci#define POLL_INTERVAL 3 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define DEBOUNCE_COUNT 1 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* sensor values are 12-bit wide */ 278c2ecf20Sopenharmony_ci#define MAX_12BIT ((1 << 12) - 1) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define PENDOWN_MASK 0x1 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define X_OFFSET 0x0 328c2ecf20Sopenharmony_ci#define Y_OFFSET 0x2 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct ts4800_ts { 358c2ecf20Sopenharmony_ci struct input_dev *input; 368c2ecf20Sopenharmony_ci struct device *dev; 378c2ecf20Sopenharmony_ci char phys[32]; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci void __iomem *base; 408c2ecf20Sopenharmony_ci struct regmap *regmap; 418c2ecf20Sopenharmony_ci unsigned int reg; 428c2ecf20Sopenharmony_ci unsigned int bit; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci bool pendown; 458c2ecf20Sopenharmony_ci int debounce; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int ts4800_ts_open(struct input_dev *input_dev) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct ts4800_ts *ts = input_get_drvdata(input_dev); 518c2ecf20Sopenharmony_ci int error; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci ts->pendown = false; 548c2ecf20Sopenharmony_ci ts->debounce = DEBOUNCE_COUNT; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit); 578c2ecf20Sopenharmony_ci if (error) { 588c2ecf20Sopenharmony_ci dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error); 598c2ecf20Sopenharmony_ci return error; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return 0; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void ts4800_ts_close(struct input_dev *input_dev) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct ts4800_ts *ts = input_get_drvdata(input_dev); 688c2ecf20Sopenharmony_ci int ret; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0); 718c2ecf20Sopenharmony_ci if (ret) 728c2ecf20Sopenharmony_ci dev_warn(ts->dev, "Failed to disable touchscreen\n"); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void ts4800_ts_poll(struct input_dev *input_dev) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct ts4800_ts *ts = input_get_drvdata(input_dev); 798c2ecf20Sopenharmony_ci u16 last_x = readw(ts->base + X_OFFSET); 808c2ecf20Sopenharmony_ci u16 last_y = readw(ts->base + Y_OFFSET); 818c2ecf20Sopenharmony_ci bool pendown = last_x & PENDOWN_MASK; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (pendown) { 848c2ecf20Sopenharmony_ci if (ts->debounce) { 858c2ecf20Sopenharmony_ci ts->debounce--; 868c2ecf20Sopenharmony_ci return; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci if (!ts->pendown) { 908c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 1); 918c2ecf20Sopenharmony_ci ts->pendown = true; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci last_x = ((~last_x) >> 4) & MAX_12BIT; 958c2ecf20Sopenharmony_ci last_y = ((~last_y) >> 4) & MAX_12BIT; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_X, last_x); 988c2ecf20Sopenharmony_ci input_report_abs(input_dev, ABS_Y, last_y); 998c2ecf20Sopenharmony_ci input_sync(input_dev); 1008c2ecf20Sopenharmony_ci } else if (ts->pendown) { 1018c2ecf20Sopenharmony_ci ts->pendown = false; 1028c2ecf20Sopenharmony_ci ts->debounce = DEBOUNCE_COUNT; 1038c2ecf20Sopenharmony_ci input_report_key(input_dev, BTN_TOUCH, 0); 1048c2ecf20Sopenharmony_ci input_sync(input_dev); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic int ts4800_parse_dt(struct platform_device *pdev, 1098c2ecf20Sopenharmony_ci struct ts4800_ts *ts) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1128c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 1138c2ecf20Sopenharmony_ci struct device_node *syscon_np; 1148c2ecf20Sopenharmony_ci u32 reg, bit; 1158c2ecf20Sopenharmony_ci int error; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci syscon_np = of_parse_phandle(np, "syscon", 0); 1188c2ecf20Sopenharmony_ci if (!syscon_np) { 1198c2ecf20Sopenharmony_ci dev_err(dev, "no syscon property\n"); 1208c2ecf20Sopenharmony_ci return -ENODEV; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci ts->regmap = syscon_node_to_regmap(syscon_np); 1248c2ecf20Sopenharmony_ci of_node_put(syscon_np); 1258c2ecf20Sopenharmony_ci if (IS_ERR(ts->regmap)) { 1268c2ecf20Sopenharmony_ci dev_err(dev, "cannot get parent's regmap\n"); 1278c2ecf20Sopenharmony_ci return PTR_ERR(ts->regmap); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci error = of_property_read_u32_index(np, "syscon", 1, ®); 1318c2ecf20Sopenharmony_ci if (error < 0) { 1328c2ecf20Sopenharmony_ci dev_err(dev, "no offset in syscon\n"); 1338c2ecf20Sopenharmony_ci return error; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci ts->reg = reg; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci error = of_property_read_u32_index(np, "syscon", 2, &bit); 1398c2ecf20Sopenharmony_ci if (error < 0) { 1408c2ecf20Sopenharmony_ci dev_err(dev, "no bit in syscon\n"); 1418c2ecf20Sopenharmony_ci return error; 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ts->bit = BIT(bit); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic int ts4800_ts_probe(struct platform_device *pdev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct input_dev *input_dev; 1528c2ecf20Sopenharmony_ci struct ts4800_ts *ts; 1538c2ecf20Sopenharmony_ci int error; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL); 1568c2ecf20Sopenharmony_ci if (!ts) 1578c2ecf20Sopenharmony_ci return -ENOMEM; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci error = ts4800_parse_dt(pdev, ts); 1608c2ecf20Sopenharmony_ci if (error) 1618c2ecf20Sopenharmony_ci return error; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ts->base = devm_platform_ioremap_resource(pdev, 0); 1648c2ecf20Sopenharmony_ci if (IS_ERR(ts->base)) 1658c2ecf20Sopenharmony_ci return PTR_ERR(ts->base); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci input_dev = devm_input_allocate_device(&pdev->dev); 1688c2ecf20Sopenharmony_ci if (!input_dev) 1698c2ecf20Sopenharmony_ci return -ENOMEM; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev)); 1728c2ecf20Sopenharmony_ci ts->input = input_dev; 1738c2ecf20Sopenharmony_ci ts->dev = &pdev->dev; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, ts); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci input_dev->name = "TS-4800 Touchscreen"; 1788c2ecf20Sopenharmony_ci input_dev->phys = ts->phys; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci input_dev->open = ts4800_ts_open; 1818c2ecf20Sopenharmony_ci input_dev->close = ts4800_ts_close; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci input_set_capability(input_dev, EV_KEY, BTN_TOUCH); 1848c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); 1858c2ecf20Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci error = input_setup_polling(input_dev, ts4800_ts_poll); 1888c2ecf20Sopenharmony_ci if (error) { 1898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to set up polling: %d\n", error); 1908c2ecf20Sopenharmony_ci return error; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci input_set_poll_interval(input_dev, POLL_INTERVAL); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci error = input_register_device(input_dev); 1968c2ecf20Sopenharmony_ci if (error) { 1978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 1988c2ecf20Sopenharmony_ci "Unable to register input device: %d\n", error); 1998c2ecf20Sopenharmony_ci return error; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic const struct of_device_id ts4800_ts_of_match[] = { 2068c2ecf20Sopenharmony_ci { .compatible = "technologic,ts4800-ts", }, 2078c2ecf20Sopenharmony_ci { }, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ts4800_ts_of_match); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct platform_driver ts4800_ts_driver = { 2128c2ecf20Sopenharmony_ci .driver = { 2138c2ecf20Sopenharmony_ci .name = "ts4800-ts", 2148c2ecf20Sopenharmony_ci .of_match_table = ts4800_ts_of_match, 2158c2ecf20Sopenharmony_ci }, 2168c2ecf20Sopenharmony_ci .probe = ts4800_ts_probe, 2178c2ecf20Sopenharmony_ci}; 2188c2ecf20Sopenharmony_cimodule_platform_driver(ts4800_ts_driver); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); 2218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TS-4800 Touchscreen Driver"); 2228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2238c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:ts4800_ts"); 224