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, &reg);
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