aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen/sun4i-ts.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2014-05-14 14:20:45 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2014-05-14 19:40:06 -0400
commit6decea7c5438e2955f64e2513ec9a2fac7602a7d (patch)
treedc4e679f87d1042e7ad681daed6575b0eb1a14d6 /drivers/input/touchscreen/sun4i-ts.c
parent125a72d6275dc777d18aa349842e89e53ddbeefd (diff)
Input: add driver for Allwinner sunxi SoC's rtp controller
Note the sun4i-ts controller is capable of detecting a second touch, but when a second touch is present then the accuracy becomes so bad the reported touch location is not useable. The original android driver contains some complicated heuristics using the aprox. distance between the 2 touches to see if the user is making a pinch open / close movement, and then reports emulated multi-touch events around the last touch coordinate (as the dual-touch coordinates are worthless). These kinds of heuristics are just asking for trouble (and don't belong in the kernel). So this driver offers straight forward, reliable single touch functionality only. Signed-off-by: Hans de Goede <hdegoede@redhat.com> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/touchscreen/sun4i-ts.c')
-rw-r--r--drivers/input/touchscreen/sun4i-ts.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
new file mode 100644
index 000000000000..fc03a5fcd8f8
--- /dev/null
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -0,0 +1,262 @@
1/*
2 * Allwinner sunxi resistive touchscreen controller driver
3 *
4 * Copyright (C) 2013 - 2014 Hans de Goede <hdegoede@redhat.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17/*
18 * The sun4i-ts controller is capable of detecting a second touch, but when a
19 * second touch is present then the accuracy becomes so bad the reported touch
20 * location is not useable.
21 *
22 * The original android driver contains some complicated heuristics using the
23 * aprox. distance between the 2 touches to see if the user is making a pinch
24 * open / close movement, and then reports emulated multi-touch events around
25 * the last touch coordinate (as the dual-touch coordinates are worthless).
26 *
27 * These kinds of heuristics are just asking for trouble (and don't belong
28 * in the kernel). So this driver offers straight forward, reliable single
29 * touch functionality only.
30 */
31
32#include <linux/err.h>
33#include <linux/init.h>
34#include <linux/input.h>
35#include <linux/interrupt.h>
36#include <linux/io.h>
37#include <linux/module.h>
38#include <linux/of_platform.h>
39#include <linux/platform_device.h>
40#include <linux/slab.h>
41
42#define TP_CTRL0 0x00
43#define TP_CTRL1 0x04
44#define TP_CTRL2 0x08
45#define TP_CTRL3 0x0c
46#define TP_INT_FIFOC 0x10
47#define TP_INT_FIFOS 0x14
48#define TP_TPR 0x18
49#define TP_CDAT 0x1c
50#define TEMP_DATA 0x20
51#define TP_DATA 0x24
52
53/* TP_CTRL0 bits */
54#define ADC_FIRST_DLY(x) ((x) << 24) /* 8 bits */
55#define ADC_FIRST_DLY_MODE(x) ((x) << 23)
56#define ADC_CLK_SEL(x) ((x) << 22)
57#define ADC_CLK_DIV(x) ((x) << 20) /* 3 bits */
58#define FS_DIV(x) ((x) << 16) /* 4 bits */
59#define T_ACQ(x) ((x) << 0) /* 16 bits */
60
61/* TP_CTRL1 bits */
62#define STYLUS_UP_DEBOUN(x) ((x) << 12) /* 8 bits */
63#define STYLUS_UP_DEBOUN_EN(x) ((x) << 9)
64#define TOUCH_PAN_CALI_EN(x) ((x) << 6)
65#define TP_DUAL_EN(x) ((x) << 5)
66#define TP_MODE_EN(x) ((x) << 4)
67#define TP_ADC_SELECT(x) ((x) << 3)
68#define ADC_CHAN_SELECT(x) ((x) << 0) /* 3 bits */
69
70/* TP_CTRL2 bits */
71#define TP_SENSITIVE_ADJUST(x) ((x) << 28) /* 4 bits */
72#define TP_MODE_SELECT(x) ((x) << 26) /* 2 bits */
73#define PRE_MEA_EN(x) ((x) << 24)
74#define PRE_MEA_THRE_CNT(x) ((x) << 0) /* 24 bits */
75
76/* TP_CTRL3 bits */
77#define FILTER_EN(x) ((x) << 2)
78#define FILTER_TYPE(x) ((x) << 0) /* 2 bits */
79
80/* TP_INT_FIFOC irq and fifo mask / control bits */
81#define TEMP_IRQ_EN(x) ((x) << 18)
82#define OVERRUN_IRQ_EN(x) ((x) << 17)
83#define DATA_IRQ_EN(x) ((x) << 16)
84#define TP_DATA_XY_CHANGE(x) ((x) << 13)
85#define FIFO_TRIG(x) ((x) << 8) /* 5 bits */
86#define DATA_DRQ_EN(x) ((x) << 7)
87#define FIFO_FLUSH(x) ((x) << 4)
88#define TP_UP_IRQ_EN(x) ((x) << 1)
89#define TP_DOWN_IRQ_EN(x) ((x) << 0)
90
91/* TP_INT_FIFOS irq and fifo status bits */
92#define TEMP_DATA_PENDING BIT(18)
93#define FIFO_OVERRUN_PENDING BIT(17)
94#define FIFO_DATA_PENDING BIT(16)
95#define TP_IDLE_FLG BIT(2)
96#define TP_UP_PENDING BIT(1)
97#define TP_DOWN_PENDING BIT(0)
98
99/* TP_TPR bits */
100#define TEMP_ENABLE(x) ((x) << 16)
101#define TEMP_PERIOD(x) ((x) << 0) /* t = x * 256 * 16 / clkin */
102
103struct sun4i_ts_data {
104 struct device *dev;
105 struct input_dev *input;
106 void __iomem *base;
107 unsigned int irq;
108 bool ignore_fifo_data;
109};
110
111static irqreturn_t sun4i_ts_irq(int irq, void *dev_id)
112{
113 struct sun4i_ts_data *ts = dev_id;
114 u32 reg_val, x, y;
115
116 reg_val = readl(ts->base + TP_INT_FIFOS);
117
118 if (reg_val & FIFO_DATA_PENDING) {
119 x = readl(ts->base + TP_DATA);
120 y = readl(ts->base + TP_DATA);
121 /* The 1st location reported after an up event is unreliable */
122 if (!ts->ignore_fifo_data) {
123 input_report_abs(ts->input, ABS_X, x);
124 input_report_abs(ts->input, ABS_Y, y);
125 /*
126 * The hardware has a separate down status bit, but
127 * that gets set before we get the first location,
128 * resulting in reporting a click on the old location.
129 */
130 input_report_key(ts->input, BTN_TOUCH, 1);
131 input_sync(ts->input);
132 } else {
133 ts->ignore_fifo_data = false;
134 }
135 }
136
137 if (reg_val & TP_UP_PENDING) {
138 ts->ignore_fifo_data = true;
139 input_report_key(ts->input, BTN_TOUCH, 0);
140 input_sync(ts->input);
141 }
142
143 writel(reg_val, ts->base + TP_INT_FIFOS);
144
145 return IRQ_HANDLED;
146}
147
148static int sun4i_ts_open(struct input_dev *dev)
149{
150 struct sun4i_ts_data *ts = input_get_drvdata(dev);
151
152 /* Flush, set trig level to 1, enable data and up irqs */
153 writel(DATA_IRQ_EN(1) | FIFO_TRIG(1) | FIFO_FLUSH(1) | TP_UP_IRQ_EN(1),
154 ts->base + TP_INT_FIFOC);
155
156 return 0;
157}
158
159static void sun4i_ts_close(struct input_dev *dev)
160{
161 struct sun4i_ts_data *ts = input_get_drvdata(dev);
162
163 /* Deactivate all IRQs */
164 writel(0, ts->base + TP_INT_FIFOC);
165}
166
167static int sun4i_ts_probe(struct platform_device *pdev)
168{
169 struct sun4i_ts_data *ts;
170 struct device *dev = &pdev->dev;
171 int error;
172
173 ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
174 if (!ts)
175 return -ENOMEM;
176
177 ts->dev = dev;
178 ts->ignore_fifo_data = true;
179
180 ts->input = devm_input_allocate_device(dev);
181 if (!ts->input)
182 return -ENOMEM;
183
184 ts->input->name = pdev->name;
185 ts->input->phys = "sun4i_ts/input0";
186 ts->input->open = sun4i_ts_open;
187 ts->input->close = sun4i_ts_close;
188 ts->input->id.bustype = BUS_HOST;
189 ts->input->id.vendor = 0x0001;
190 ts->input->id.product = 0x0001;
191 ts->input->id.version = 0x0100;
192 ts->input->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
193 __set_bit(BTN_TOUCH, ts->input->keybit);
194 input_set_abs_params(ts->input, ABS_X, 0, 4095, 0, 0);
195 input_set_abs_params(ts->input, ABS_Y, 0, 4095, 0, 0);
196 input_set_drvdata(ts->input, ts);
197
198 ts->base = devm_ioremap_resource(dev,
199 platform_get_resource(pdev, IORESOURCE_MEM, 0));
200 if (IS_ERR(ts->base))
201 return PTR_ERR(ts->base);
202
203 ts->irq = platform_get_irq(pdev, 0);
204 error = devm_request_irq(dev, ts->irq, sun4i_ts_irq, 0, "sun4i-ts", ts);
205 if (error)
206 return error;
207
208 /*
209 * Select HOSC clk, clkin = clk / 6, adc samplefreq = clkin / 8192,
210 * t_acq = clkin / (16 * 64)
211 */
212 writel(ADC_CLK_SEL(0) | ADC_CLK_DIV(2) | FS_DIV(7) | T_ACQ(63),
213 ts->base + TP_CTRL0);
214
215 /*
216 * sensitive_adjust = 15 : max, which is not all that sensitive,
217 * tp_mode = 0 : only x and y coordinates, as we don't use dual touch
218 */
219 writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0),
220 ts->base + TP_CTRL2);
221
222 /* Enable median filter, type 1 : 5/3 */
223 writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3);
224
225 /* Enable temperature measurement, period 1953 (2 seconds) */
226 writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
227
228 /*
229 * Set stylus up debounce to aprox 10 ms, enable debounce, and
230 * finally enable tp mode.
231 */
232 writel(STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1) | TP_MODE_EN(1),
233 ts->base + TP_CTRL1);
234
235 error = input_register_device(ts->input);
236 if (error)
237 return error;
238
239 platform_set_drvdata(pdev, ts);
240 return 0;
241}
242
243static const struct of_device_id sun4i_ts_of_match[] = {
244 { .compatible = "allwinner,sun4i-a10-ts", },
245 { /* sentinel */ }
246};
247MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
248
249static struct platform_driver sun4i_ts_driver = {
250 .driver = {
251 .owner = THIS_MODULE,
252 .name = "sun4i-ts",
253 .of_match_table = of_match_ptr(sun4i_ts_of_match),
254 },
255 .probe = sun4i_ts_probe,
256};
257
258module_platform_driver(sun4i_ts_driver);
259
260MODULE_DESCRIPTION("Allwinner sun4i resistive touchscreen controller driver");
261MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
262MODULE_LICENSE("GPL");