1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/input/touchscreen/tsc2007.c |
4 | * |
5 | * Copyright (c) 2008 MtekVision Co., Ltd. |
6 | * Kwangwoo Lee <kwlee@mtekvision.com> |
7 | * |
8 | * Using code from: |
9 | * - ads7846.c |
10 | * Copyright (c) 2005 David Brownell |
11 | * Copyright (c) 2006 Nokia Corporation |
12 | * - corgi_ts.c |
13 | * Copyright (C) 2004-2005 Richard Purdie |
14 | * - omap_ts.[hc], ads7846.h, ts_osk.c |
15 | * Copyright (C) 2002 MontaVista Software |
16 | * Copyright (C) 2004 Texas Instruments |
17 | * Copyright (C) 2005 Dirk Behme |
18 | */ |
19 | |
20 | #include <linux/module.h> |
21 | #include <linux/slab.h> |
22 | #include <linux/gpio/consumer.h> |
23 | #include <linux/input.h> |
24 | #include <linux/interrupt.h> |
25 | #include <linux/i2c.h> |
26 | #include <linux/mod_devicetable.h> |
27 | #include <linux/property.h> |
28 | #include <linux/platform_data/tsc2007.h> |
29 | #include "tsc2007.h" |
30 | |
31 | int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) |
32 | { |
33 | s32 data; |
34 | u16 val; |
35 | |
36 | data = i2c_smbus_read_word_data(client: tsc->client, command: cmd); |
37 | if (data < 0) { |
38 | dev_err(&tsc->client->dev, "i2c io error: %d\n" , data); |
39 | return data; |
40 | } |
41 | |
42 | /* The protocol and raw data format from i2c interface: |
43 | * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P |
44 | * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. |
45 | */ |
46 | val = swab16(data) >> 4; |
47 | |
48 | dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n" , data, val); |
49 | |
50 | return val; |
51 | } |
52 | |
53 | static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) |
54 | { |
55 | /* y- still on; turn on only y+ (and ADC) */ |
56 | tc->y = tsc2007_xfer(tsc, READ_Y); |
57 | |
58 | /* turn y- off, x+ on, then leave in lowpower */ |
59 | tc->x = tsc2007_xfer(tsc, READ_X); |
60 | |
61 | /* turn y+ off, x- on; we'll use formula #1 */ |
62 | tc->z1 = tsc2007_xfer(tsc, READ_Z1); |
63 | tc->z2 = tsc2007_xfer(tsc, READ_Z2); |
64 | |
65 | /* Prepare for next touch reading - power down ADC, enable PENIRQ */ |
66 | tsc2007_xfer(tsc, PWRDOWN); |
67 | } |
68 | |
69 | u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc) |
70 | { |
71 | u32 rt = 0; |
72 | |
73 | /* range filtering */ |
74 | if (tc->x == MAX_12BIT) |
75 | tc->x = 0; |
76 | |
77 | if (likely(tc->x && tc->z1)) { |
78 | /* compute touch resistance using equation #1 */ |
79 | rt = tc->z2 - tc->z1; |
80 | rt *= tc->x; |
81 | rt *= tsc->x_plate_ohms; |
82 | rt /= tc->z1; |
83 | rt = (rt + 2047) >> 12; |
84 | } |
85 | |
86 | return rt; |
87 | } |
88 | |
89 | bool tsc2007_is_pen_down(struct tsc2007 *ts) |
90 | { |
91 | /* |
92 | * NOTE: We can't rely on the pressure to determine the pen down |
93 | * state, even though this controller has a pressure sensor. |
94 | * The pressure value can fluctuate for quite a while after |
95 | * lifting the pen and in some cases may not even settle at the |
96 | * expected value. |
97 | * |
98 | * The only safe way to check for the pen up condition is in the |
99 | * work function by reading the pen signal state (it's a GPIO |
100 | * and IRQ). Unfortunately such callback is not always available, |
101 | * in that case we assume that the pen is down and expect caller |
102 | * to fall back on the pressure reading. |
103 | */ |
104 | |
105 | if (!ts->get_pendown_state) |
106 | return true; |
107 | |
108 | return ts->get_pendown_state(&ts->client->dev); |
109 | } |
110 | |
111 | static irqreturn_t tsc2007_soft_irq(int irq, void *handle) |
112 | { |
113 | struct tsc2007 *ts = handle; |
114 | struct input_dev *input = ts->input; |
115 | struct ts_event tc; |
116 | u32 rt; |
117 | |
118 | while (!ts->stopped && tsc2007_is_pen_down(ts)) { |
119 | |
120 | /* pen is down, continue with the measurement */ |
121 | |
122 | mutex_lock(&ts->mlock); |
123 | tsc2007_read_values(tsc: ts, tc: &tc); |
124 | mutex_unlock(lock: &ts->mlock); |
125 | |
126 | rt = tsc2007_calculate_resistance(tsc: ts, tc: &tc); |
127 | |
128 | if (!rt && !ts->get_pendown_state) { |
129 | /* |
130 | * If pressure reported is 0 and we don't have |
131 | * callback to check pendown state, we have to |
132 | * assume that pen was lifted up. |
133 | */ |
134 | break; |
135 | } |
136 | |
137 | if (rt <= ts->max_rt) { |
138 | dev_dbg(&ts->client->dev, |
139 | "DOWN point(%4d,%4d), resistance (%4u)\n" , |
140 | tc.x, tc.y, rt); |
141 | |
142 | rt = ts->max_rt - rt; |
143 | |
144 | input_report_key(dev: input, BTN_TOUCH, value: 1); |
145 | input_report_abs(dev: input, ABS_X, value: tc.x); |
146 | input_report_abs(dev: input, ABS_Y, value: tc.y); |
147 | input_report_abs(dev: input, ABS_PRESSURE, value: rt); |
148 | |
149 | input_sync(dev: input); |
150 | |
151 | } else { |
152 | /* |
153 | * Sample found inconsistent by debouncing or pressure is |
154 | * beyond the maximum. Don't report it to user space, |
155 | * repeat at least once more the measurement. |
156 | */ |
157 | dev_dbg(&ts->client->dev, "ignored pressure %d\n" , rt); |
158 | } |
159 | |
160 | wait_event_timeout(ts->wait, ts->stopped, ts->poll_period); |
161 | } |
162 | |
163 | dev_dbg(&ts->client->dev, "UP\n" ); |
164 | |
165 | input_report_key(dev: input, BTN_TOUCH, value: 0); |
166 | input_report_abs(dev: input, ABS_PRESSURE, value: 0); |
167 | input_sync(dev: input); |
168 | |
169 | if (ts->clear_penirq) |
170 | ts->clear_penirq(); |
171 | |
172 | return IRQ_HANDLED; |
173 | } |
174 | |
175 | static void tsc2007_stop(struct tsc2007 *ts) |
176 | { |
177 | ts->stopped = true; |
178 | mb(); |
179 | wake_up(&ts->wait); |
180 | |
181 | disable_irq(irq: ts->irq); |
182 | } |
183 | |
184 | static int tsc2007_open(struct input_dev *input_dev) |
185 | { |
186 | struct tsc2007 *ts = input_get_drvdata(dev: input_dev); |
187 | int err; |
188 | |
189 | ts->stopped = false; |
190 | mb(); |
191 | |
192 | enable_irq(irq: ts->irq); |
193 | |
194 | /* Prepare for touch readings - power down ADC and enable PENIRQ */ |
195 | err = tsc2007_xfer(tsc: ts, PWRDOWN); |
196 | if (err < 0) { |
197 | tsc2007_stop(ts); |
198 | return err; |
199 | } |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static void tsc2007_close(struct input_dev *input_dev) |
205 | { |
206 | struct tsc2007 *ts = input_get_drvdata(dev: input_dev); |
207 | |
208 | tsc2007_stop(ts); |
209 | } |
210 | |
211 | static int tsc2007_get_pendown_state_gpio(struct device *dev) |
212 | { |
213 | struct i2c_client *client = to_i2c_client(dev); |
214 | struct tsc2007 *ts = i2c_get_clientdata(client); |
215 | |
216 | return gpiod_get_value_cansleep(desc: ts->gpiod); |
217 | } |
218 | |
219 | static int tsc2007_probe_properties(struct device *dev, struct tsc2007 *ts) |
220 | { |
221 | u32 val32; |
222 | u64 val64; |
223 | |
224 | if (!device_property_read_u32(dev, propname: "ti,max-rt" , val: &val32)) |
225 | ts->max_rt = val32; |
226 | else |
227 | ts->max_rt = MAX_12BIT; |
228 | |
229 | if (!device_property_read_u32(dev, propname: "ti,fuzzx" , val: &val32)) |
230 | ts->fuzzx = val32; |
231 | |
232 | if (!device_property_read_u32(dev, propname: "ti,fuzzy" , val: &val32)) |
233 | ts->fuzzy = val32; |
234 | |
235 | if (!device_property_read_u32(dev, propname: "ti,fuzzz" , val: &val32)) |
236 | ts->fuzzz = val32; |
237 | |
238 | if (!device_property_read_u64(dev, propname: "ti,poll-period" , val: &val64)) |
239 | ts->poll_period = msecs_to_jiffies(m: val64); |
240 | else |
241 | ts->poll_period = msecs_to_jiffies(m: 1); |
242 | |
243 | if (!device_property_read_u32(dev, propname: "ti,x-plate-ohms" , val: &val32)) { |
244 | ts->x_plate_ohms = val32; |
245 | } else { |
246 | dev_err(dev, "Missing ti,x-plate-ohms device property\n" ); |
247 | return -EINVAL; |
248 | } |
249 | |
250 | ts->gpiod = devm_gpiod_get_optional(dev, NULL, flags: GPIOD_IN); |
251 | if (IS_ERR(ptr: ts->gpiod)) |
252 | return PTR_ERR(ptr: ts->gpiod); |
253 | |
254 | if (ts->gpiod) |
255 | ts->get_pendown_state = tsc2007_get_pendown_state_gpio; |
256 | else |
257 | dev_warn(dev, "Pen down GPIO is not specified in properties\n" ); |
258 | |
259 | return 0; |
260 | } |
261 | |
262 | static int tsc2007_probe_pdev(struct device *dev, struct tsc2007 *ts, |
263 | const struct tsc2007_platform_data *pdata, |
264 | const struct i2c_device_id *id) |
265 | { |
266 | ts->model = pdata->model; |
267 | ts->x_plate_ohms = pdata->x_plate_ohms; |
268 | ts->max_rt = pdata->max_rt ? : MAX_12BIT; |
269 | ts->poll_period = msecs_to_jiffies(m: pdata->poll_period ? : 1); |
270 | ts->get_pendown_state = pdata->get_pendown_state; |
271 | ts->clear_penirq = pdata->clear_penirq; |
272 | ts->fuzzx = pdata->fuzzx; |
273 | ts->fuzzy = pdata->fuzzy; |
274 | ts->fuzzz = pdata->fuzzz; |
275 | |
276 | if (pdata->x_plate_ohms == 0) { |
277 | dev_err(dev, "x_plate_ohms is not set up in platform data\n" ); |
278 | return -EINVAL; |
279 | } |
280 | |
281 | return 0; |
282 | } |
283 | |
284 | static void tsc2007_call_exit_platform_hw(void *data) |
285 | { |
286 | struct device *dev = data; |
287 | const struct tsc2007_platform_data *pdata = dev_get_platdata(dev); |
288 | |
289 | pdata->exit_platform_hw(); |
290 | } |
291 | |
292 | static int tsc2007_probe(struct i2c_client *client) |
293 | { |
294 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
295 | const struct tsc2007_platform_data *pdata = |
296 | dev_get_platdata(dev: &client->dev); |
297 | struct tsc2007 *ts; |
298 | struct input_dev *input_dev; |
299 | int err; |
300 | |
301 | if (!i2c_check_functionality(adap: client->adapter, |
302 | I2C_FUNC_SMBUS_READ_WORD_DATA)) |
303 | return -EIO; |
304 | |
305 | ts = devm_kzalloc(dev: &client->dev, size: sizeof(struct tsc2007), GFP_KERNEL); |
306 | if (!ts) |
307 | return -ENOMEM; |
308 | |
309 | if (pdata) |
310 | err = tsc2007_probe_pdev(dev: &client->dev, ts, pdata, id); |
311 | else |
312 | err = tsc2007_probe_properties(dev: &client->dev, ts); |
313 | if (err) |
314 | return err; |
315 | |
316 | input_dev = devm_input_allocate_device(&client->dev); |
317 | if (!input_dev) |
318 | return -ENOMEM; |
319 | |
320 | i2c_set_clientdata(client, data: ts); |
321 | |
322 | ts->client = client; |
323 | ts->irq = client->irq; |
324 | ts->input = input_dev; |
325 | |
326 | init_waitqueue_head(&ts->wait); |
327 | mutex_init(&ts->mlock); |
328 | |
329 | snprintf(buf: ts->phys, size: sizeof(ts->phys), |
330 | fmt: "%s/input0" , dev_name(dev: &client->dev)); |
331 | |
332 | input_dev->name = "TSC2007 Touchscreen" ; |
333 | input_dev->phys = ts->phys; |
334 | input_dev->id.bustype = BUS_I2C; |
335 | |
336 | input_dev->open = tsc2007_open; |
337 | input_dev->close = tsc2007_close; |
338 | |
339 | input_set_drvdata(dev: input_dev, data: ts); |
340 | |
341 | input_set_capability(dev: input_dev, EV_KEY, BTN_TOUCH); |
342 | |
343 | input_set_abs_params(dev: input_dev, ABS_X, min: 0, MAX_12BIT, fuzz: ts->fuzzx, flat: 0); |
344 | input_set_abs_params(dev: input_dev, ABS_Y, min: 0, MAX_12BIT, fuzz: ts->fuzzy, flat: 0); |
345 | input_set_abs_params(dev: input_dev, ABS_PRESSURE, min: 0, MAX_12BIT, |
346 | fuzz: ts->fuzzz, flat: 0); |
347 | |
348 | if (pdata) { |
349 | if (pdata->exit_platform_hw) { |
350 | err = devm_add_action(&client->dev, |
351 | tsc2007_call_exit_platform_hw, |
352 | &client->dev); |
353 | if (err) { |
354 | dev_err(&client->dev, |
355 | "Failed to register exit_platform_hw action, %d\n" , |
356 | err); |
357 | return err; |
358 | } |
359 | } |
360 | |
361 | if (pdata->init_platform_hw) |
362 | pdata->init_platform_hw(); |
363 | } |
364 | |
365 | err = devm_request_threaded_irq(dev: &client->dev, irq: ts->irq, |
366 | NULL, thread_fn: tsc2007_soft_irq, |
367 | IRQF_ONESHOT, |
368 | devname: client->dev.driver->name, dev_id: ts); |
369 | if (err) { |
370 | dev_err(&client->dev, "Failed to request irq %d: %d\n" , |
371 | ts->irq, err); |
372 | return err; |
373 | } |
374 | |
375 | tsc2007_stop(ts); |
376 | |
377 | /* power down the chip (TSC2007_SETUP does not ACK on I2C) */ |
378 | err = tsc2007_xfer(tsc: ts, PWRDOWN); |
379 | if (err < 0) { |
380 | dev_err(&client->dev, |
381 | "Failed to setup chip: %d\n" , err); |
382 | return err; /* chip does not respond */ |
383 | } |
384 | |
385 | err = input_register_device(input_dev); |
386 | if (err) { |
387 | dev_err(&client->dev, |
388 | "Failed to register input device: %d\n" , err); |
389 | return err; |
390 | } |
391 | |
392 | err = tsc2007_iio_configure(ts); |
393 | if (err) { |
394 | dev_err(&client->dev, |
395 | "Failed to register with IIO: %d\n" , err); |
396 | return err; |
397 | } |
398 | |
399 | return 0; |
400 | } |
401 | |
402 | static const struct i2c_device_id tsc2007_idtable[] = { |
403 | { "tsc2007" , 0 }, |
404 | { } |
405 | }; |
406 | |
407 | MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); |
408 | |
409 | static const struct of_device_id tsc2007_of_match[] = { |
410 | { .compatible = "ti,tsc2007" }, |
411 | { /* sentinel */ } |
412 | }; |
413 | MODULE_DEVICE_TABLE(of, tsc2007_of_match); |
414 | |
415 | static struct i2c_driver tsc2007_driver = { |
416 | .driver = { |
417 | .name = "tsc2007" , |
418 | .of_match_table = tsc2007_of_match, |
419 | }, |
420 | .id_table = tsc2007_idtable, |
421 | .probe = tsc2007_probe, |
422 | }; |
423 | |
424 | module_i2c_driver(tsc2007_driver); |
425 | |
426 | MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>" ); |
427 | MODULE_DESCRIPTION("TSC2007 TouchScreen Driver" ); |
428 | MODULE_LICENSE("GPL" ); |
429 | |