1/*
2 * Touchscreen driver for the TS-4800 board
3 *
4 * Copyright (c) 2015 - Savoir-faire Linux
5 *
6 * This file is licensed under the terms of the GNU General Public
7 * License version 2. This program is licensed "as is" without any
8 * warranty of any kind, whether express or implied.
9 */
10
11#include <linux/bitops.h>
12#include <linux/input.h>
13#include <linux/io.h>
14#include <linux/kernel.h>
15#include <linux/mfd/syscon.h>
16#include <linux/module.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/regmap.h>
20
21/* polling interval in ms */
22#define POLL_INTERVAL		3
23
24#define DEBOUNCE_COUNT		1
25
26/* sensor values are 12-bit wide */
27#define MAX_12BIT		((1 << 12) - 1)
28
29#define PENDOWN_MASK		0x1
30
31#define X_OFFSET		0x0
32#define Y_OFFSET		0x2
33
34struct ts4800_ts {
35	struct input_dev        *input;
36	struct device           *dev;
37	char                    phys[32];
38
39	void __iomem            *base;
40	struct regmap           *regmap;
41	unsigned int            reg;
42	unsigned int            bit;
43
44	bool                    pendown;
45	int                     debounce;
46};
47
48static int ts4800_ts_open(struct input_dev *input_dev)
49{
50	struct ts4800_ts *ts = input_get_drvdata(input_dev);
51	int error;
52
53	ts->pendown = false;
54	ts->debounce = DEBOUNCE_COUNT;
55
56	error = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
57	if (error) {
58		dev_warn(ts->dev, "Failed to enable touchscreen: %d\n", error);
59		return error;
60	}
61
62	return 0;
63}
64
65static void ts4800_ts_close(struct input_dev *input_dev)
66{
67	struct ts4800_ts *ts = input_get_drvdata(input_dev);
68	int ret;
69
70	ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
71	if (ret)
72		dev_warn(ts->dev, "Failed to disable touchscreen\n");
73
74}
75
76static void ts4800_ts_poll(struct input_dev *input_dev)
77{
78	struct ts4800_ts *ts = input_get_drvdata(input_dev);
79	u16 last_x = readw(ts->base + X_OFFSET);
80	u16 last_y = readw(ts->base + Y_OFFSET);
81	bool pendown = last_x & PENDOWN_MASK;
82
83	if (pendown) {
84		if (ts->debounce) {
85			ts->debounce--;
86			return;
87		}
88
89		if (!ts->pendown) {
90			input_report_key(input_dev, BTN_TOUCH, 1);
91			ts->pendown = true;
92		}
93
94		last_x = ((~last_x) >> 4) & MAX_12BIT;
95		last_y = ((~last_y) >> 4) & MAX_12BIT;
96
97		input_report_abs(input_dev, ABS_X, last_x);
98		input_report_abs(input_dev, ABS_Y, last_y);
99		input_sync(input_dev);
100	} else if (ts->pendown) {
101		ts->pendown = false;
102		ts->debounce = DEBOUNCE_COUNT;
103		input_report_key(input_dev, BTN_TOUCH, 0);
104		input_sync(input_dev);
105	}
106}
107
108static int ts4800_parse_dt(struct platform_device *pdev,
109			   struct ts4800_ts *ts)
110{
111	struct device *dev = &pdev->dev;
112	struct device_node *np = dev->of_node;
113	struct device_node *syscon_np;
114	u32 reg, bit;
115	int error;
116
117	syscon_np = of_parse_phandle(np, "syscon", 0);
118	if (!syscon_np) {
119		dev_err(dev, "no syscon property\n");
120		return -ENODEV;
121	}
122
123	ts->regmap = syscon_node_to_regmap(syscon_np);
124	of_node_put(syscon_np);
125	if (IS_ERR(ts->regmap)) {
126		dev_err(dev, "cannot get parent's regmap\n");
127		return PTR_ERR(ts->regmap);
128	}
129
130	error = of_property_read_u32_index(np, "syscon", 1, &reg);
131	if (error < 0) {
132		dev_err(dev, "no offset in syscon\n");
133		return error;
134	}
135
136	ts->reg = reg;
137
138	error = of_property_read_u32_index(np, "syscon", 2, &bit);
139	if (error < 0) {
140		dev_err(dev, "no bit in syscon\n");
141		return error;
142	}
143
144	ts->bit = BIT(bit);
145
146	return 0;
147}
148
149static int ts4800_ts_probe(struct platform_device *pdev)
150{
151	struct input_dev *input_dev;
152	struct ts4800_ts *ts;
153	int error;
154
155	ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
156	if (!ts)
157		return -ENOMEM;
158
159	error = ts4800_parse_dt(pdev, ts);
160	if (error)
161		return error;
162
163	ts->base = devm_platform_ioremap_resource(pdev, 0);
164	if (IS_ERR(ts->base))
165		return PTR_ERR(ts->base);
166
167	input_dev = devm_input_allocate_device(&pdev->dev);
168	if (!input_dev)
169		return -ENOMEM;
170
171	snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
172	ts->input = input_dev;
173	ts->dev = &pdev->dev;
174
175	input_set_drvdata(input_dev, ts);
176
177	input_dev->name = "TS-4800 Touchscreen";
178	input_dev->phys = ts->phys;
179
180	input_dev->open = ts4800_ts_open;
181	input_dev->close = ts4800_ts_close;
182
183	input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
184	input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
185	input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
186
187	error = input_setup_polling(input_dev, ts4800_ts_poll);
188	if (error) {
189		dev_err(&pdev->dev, "Unable to set up polling: %d\n", error);
190		return error;
191	}
192
193	input_set_poll_interval(input_dev, POLL_INTERVAL);
194
195	error = input_register_device(input_dev);
196	if (error) {
197		dev_err(&pdev->dev,
198			"Unable to register input device: %d\n", error);
199		return error;
200	}
201
202	return 0;
203}
204
205static const struct of_device_id ts4800_ts_of_match[] = {
206	{ .compatible = "technologic,ts4800-ts", },
207	{ },
208};
209MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
210
211static struct platform_driver ts4800_ts_driver = {
212	.driver = {
213		.name = "ts4800-ts",
214		.of_match_table = ts4800_ts_of_match,
215	},
216	.probe = ts4800_ts_probe,
217};
218module_platform_driver(ts4800_ts_driver);
219
220MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
221MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
222MODULE_LICENSE("GPL v2");
223MODULE_ALIAS("platform:ts4800_ts");
224