]>
Commit | Line | Data |
---|---|---|
50b6f1f4 KL |
1 | /* |
2 | * drivers/input/touchscreen/tsc2007.c | |
3 | * | |
4 | * Copyright (c) 2008 MtekVision Co., Ltd. | |
5 | * Kwangwoo Lee <kwlee@mtekvision.com> | |
6 | * | |
7 | * Using code from: | |
8 | * - ads7846.c | |
9 | * Copyright (c) 2005 David Brownell | |
10 | * Copyright (c) 2006 Nokia Corporation | |
11 | * - corgi_ts.c | |
12 | * Copyright (C) 2004-2005 Richard Purdie | |
13 | * - omap_ts.[hc], ads7846.h, ts_osk.c | |
14 | * Copyright (C) 2002 MontaVista Software | |
15 | * Copyright (C) 2004 Texas Instruments | |
16 | * Copyright (C) 2005 Dirk Behme | |
17 | * | |
18 | * This program is free software; you can redistribute it and/or modify | |
19 | * it under the terms of the GNU General Public License version 2 as | |
20 | * published by the Free Software Foundation. | |
21 | */ | |
22 | ||
23 | #include <linux/module.h> | |
50b6f1f4 KL |
24 | #include <linux/slab.h> |
25 | #include <linux/input.h> | |
26 | #include <linux/interrupt.h> | |
27 | #include <linux/i2c.h> | |
28 | #include <linux/i2c/tsc2007.h> | |
29 | ||
141586bc DT |
30 | #define TS_POLL_DELAY 1 /* ms delay between samples */ |
31 | #define TS_POLL_PERIOD 1 /* ms delay between samples */ | |
50b6f1f4 KL |
32 | |
33 | #define TSC2007_MEASURE_TEMP0 (0x0 << 4) | |
34 | #define TSC2007_MEASURE_AUX (0x2 << 4) | |
35 | #define TSC2007_MEASURE_TEMP1 (0x4 << 4) | |
36 | #define TSC2007_ACTIVATE_XN (0x8 << 4) | |
37 | #define TSC2007_ACTIVATE_YN (0x9 << 4) | |
38 | #define TSC2007_ACTIVATE_YP_XN (0xa << 4) | |
39 | #define TSC2007_SETUP (0xb << 4) | |
40 | #define TSC2007_MEASURE_X (0xc << 4) | |
41 | #define TSC2007_MEASURE_Y (0xd << 4) | |
42 | #define TSC2007_MEASURE_Z1 (0xe << 4) | |
43 | #define TSC2007_MEASURE_Z2 (0xf << 4) | |
44 | ||
45 | #define TSC2007_POWER_OFF_IRQ_EN (0x0 << 2) | |
46 | #define TSC2007_ADC_ON_IRQ_DIS0 (0x1 << 2) | |
47 | #define TSC2007_ADC_OFF_IRQ_EN (0x2 << 2) | |
48 | #define TSC2007_ADC_ON_IRQ_DIS1 (0x3 << 2) | |
49 | ||
50 | #define TSC2007_12BIT (0x0 << 1) | |
51 | #define TSC2007_8BIT (0x1 << 1) | |
52 | ||
53 | #define MAX_12BIT ((1 << 12) - 1) | |
54 | ||
55 | #define ADC_ON_12BIT (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0) | |
56 | ||
57 | #define READ_Y (ADC_ON_12BIT | TSC2007_MEASURE_Y) | |
58 | #define READ_Z1 (ADC_ON_12BIT | TSC2007_MEASURE_Z1) | |
59 | #define READ_Z2 (ADC_ON_12BIT | TSC2007_MEASURE_Z2) | |
60 | #define READ_X (ADC_ON_12BIT | TSC2007_MEASURE_X) | |
61 | #define PWRDOWN (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN) | |
62 | ||
63 | struct ts_event { | |
64 | u16 x; | |
65 | u16 y; | |
66 | u16 z1, z2; | |
67 | }; | |
68 | ||
69 | struct tsc2007 { | |
70 | struct input_dev *input; | |
71 | char phys[32]; | |
75fba3b0 | 72 | struct delayed_work work; |
50b6f1f4 KL |
73 | |
74 | struct i2c_client *client; | |
75 | ||
50b6f1f4 KL |
76 | u16 model; |
77 | u16 x_plate_ohms; | |
78 | ||
141586bc | 79 | bool pendown; |
50b6f1f4 KL |
80 | int irq; |
81 | ||
82 | int (*get_pendown_state)(void); | |
83 | void (*clear_penirq)(void); | |
84 | }; | |
85 | ||
86 | static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd) | |
87 | { | |
88 | s32 data; | |
89 | u16 val; | |
90 | ||
91 | data = i2c_smbus_read_word_data(tsc->client, cmd); | |
92 | if (data < 0) { | |
93 | dev_err(&tsc->client->dev, "i2c io error: %d\n", data); | |
94 | return data; | |
95 | } | |
96 | ||
97 | /* The protocol and raw data format from i2c interface: | |
98 | * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P | |
99 | * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit]. | |
100 | */ | |
101 | val = swab16(data) >> 4; | |
102 | ||
103 | dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val); | |
104 | ||
105 | return val; | |
106 | } | |
107 | ||
d570e9ef | 108 | static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc) |
50b6f1f4 | 109 | { |
d570e9ef DT |
110 | /* y- still on; turn on only y+ (and ADC) */ |
111 | tc->y = tsc2007_xfer(tsc, READ_Y); | |
112 | ||
113 | /* turn y- off, x+ on, then leave in lowpower */ | |
114 | tc->x = tsc2007_xfer(tsc, READ_X); | |
115 | ||
116 | /* turn y+ off, x- on; we'll use formula #1 */ | |
117 | tc->z1 = tsc2007_xfer(tsc, READ_Z1); | |
118 | tc->z2 = tsc2007_xfer(tsc, READ_Z2); | |
119 | ||
120 | /* Prepare for next touch reading - power down ADC, enable PENIRQ */ | |
121 | tsc2007_xfer(tsc, PWRDOWN); | |
122 | } | |
50b6f1f4 | 123 | |
d570e9ef DT |
124 | static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc) |
125 | { | |
126 | u32 rt = 0; | |
50b6f1f4 KL |
127 | |
128 | /* range filtering */ | |
d570e9ef DT |
129 | if (tc->x == MAX_12BIT) |
130 | tc->x = 0; | |
50b6f1f4 | 131 | |
d570e9ef | 132 | if (likely(tc->x && tc->z1)) { |
50b6f1f4 | 133 | /* compute touch pressure resistance using equation #1 */ |
d570e9ef DT |
134 | rt = tc->z2 - tc->z1; |
135 | rt *= tc->x; | |
136 | rt *= tsc->x_plate_ohms; | |
137 | rt /= tc->z1; | |
50b6f1f4 | 138 | rt = (rt + 2047) >> 12; |
50b6f1f4 KL |
139 | } |
140 | ||
d570e9ef DT |
141 | return rt; |
142 | } | |
143 | ||
144 | static void tsc2007_send_up_event(struct tsc2007 *tsc) | |
145 | { | |
146 | struct input_dev *input = tsc->input; | |
147 | ||
148 | dev_dbg(&tsc->client->dev, "UP\n"); | |
149 | ||
150 | input_report_key(input, BTN_TOUCH, 0); | |
151 | input_report_abs(input, ABS_PRESSURE, 0); | |
152 | input_sync(input); | |
153 | } | |
154 | ||
155 | static void tsc2007_work(struct work_struct *work) | |
156 | { | |
157 | struct tsc2007 *ts = | |
158 | container_of(to_delayed_work(work), struct tsc2007, work); | |
159 | struct ts_event tc; | |
160 | u32 rt; | |
161 | ||
141586bc DT |
162 | /* |
163 | * NOTE: We can't rely on the pressure to determine the pen down | |
d570e9ef DT |
164 | * state, even though this controller has a pressure sensor. |
165 | * The pressure value can fluctuate for quite a while after | |
166 | * lifting the pen and in some cases may not even settle at the | |
167 | * expected value. | |
50b6f1f4 KL |
168 | * |
169 | * The only safe way to check for the pen up condition is in the | |
d570e9ef DT |
170 | * work function by reading the pen signal state (it's a GPIO |
171 | * and IRQ). Unfortunately such callback is not always available, | |
172 | * in that case we have rely on the pressure anyway. | |
50b6f1f4 | 173 | */ |
d570e9ef DT |
174 | if (ts->get_pendown_state) { |
175 | if (unlikely(!ts->get_pendown_state())) { | |
176 | tsc2007_send_up_event(ts); | |
177 | ts->pendown = false; | |
178 | goto out; | |
179 | } | |
180 | ||
181 | dev_dbg(&ts->client->dev, "pen is still down\n"); | |
182 | } | |
183 | ||
184 | tsc2007_read_values(ts, &tc); | |
185 | ||
186 | rt = tsc2007_calculate_pressure(ts, &tc); | |
187 | if (rt > MAX_12BIT) { | |
188 | /* | |
189 | * Sample found inconsistent by debouncing or pressure is | |
190 | * beyond the maximum. Don't report it to user space, | |
191 | * repeat at least once more the measurement. | |
192 | */ | |
193 | dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt); | |
194 | goto out; | |
195 | ||
196 | } | |
197 | ||
50b6f1f4 KL |
198 | if (rt) { |
199 | struct input_dev *input = ts->input; | |
200 | ||
201 | if (!ts->pendown) { | |
202 | dev_dbg(&ts->client->dev, "DOWN\n"); | |
203 | ||
204 | input_report_key(input, BTN_TOUCH, 1); | |
141586bc | 205 | ts->pendown = true; |
50b6f1f4 KL |
206 | } |
207 | ||
d570e9ef DT |
208 | input_report_abs(input, ABS_X, tc.x); |
209 | input_report_abs(input, ABS_Y, tc.y); | |
50b6f1f4 KL |
210 | input_report_abs(input, ABS_PRESSURE, rt); |
211 | ||
212 | input_sync(input); | |
213 | ||
214 | dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n", | |
d570e9ef | 215 | tc.x, tc.y, rt); |
50b6f1f4 | 216 | |
d570e9ef DT |
217 | } else if (!ts->get_pendown_state && ts->pendown) { |
218 | /* | |
219 | * We don't have callback to check pendown state, so we | |
220 | * have to assume that since pressure reported is 0 the | |
221 | * pen was lifted up. | |
222 | */ | |
223 | tsc2007_send_up_event(ts); | |
141586bc | 224 | ts->pendown = false; |
d570e9ef | 225 | } |
141586bc | 226 | |
d570e9ef DT |
227 | out: |
228 | if (ts->pendown) | |
141586bc DT |
229 | schedule_delayed_work(&ts->work, |
230 | msecs_to_jiffies(TS_POLL_PERIOD)); | |
d570e9ef DT |
231 | else |
232 | enable_irq(ts->irq); | |
50b6f1f4 KL |
233 | } |
234 | ||
235 | static irqreturn_t tsc2007_irq(int irq, void *handle) | |
236 | { | |
237 | struct tsc2007 *ts = handle; | |
50b6f1f4 | 238 | |
d570e9ef | 239 | if (!ts->get_pendown_state || likely(ts->get_pendown_state())) { |
29fa98bd | 240 | disable_irq_nosync(ts->irq); |
141586bc DT |
241 | schedule_delayed_work(&ts->work, |
242 | msecs_to_jiffies(TS_POLL_DELAY)); | |
50b6f1f4 KL |
243 | } |
244 | ||
245 | if (ts->clear_penirq) | |
246 | ts->clear_penirq(); | |
247 | ||
50b6f1f4 KL |
248 | return IRQ_HANDLED; |
249 | } | |
250 | ||
141586bc DT |
251 | static void tsc2007_free_irq(struct tsc2007 *ts) |
252 | { | |
253 | free_irq(ts->irq, ts); | |
254 | if (cancel_delayed_work_sync(&ts->work)) { | |
255 | /* | |
256 | * Work was pending, therefore we need to enable | |
257 | * IRQ here to balance the disable_irq() done in the | |
258 | * interrupt handler. | |
259 | */ | |
260 | enable_irq(ts->irq); | |
261 | } | |
262 | } | |
263 | ||
264 | static int __devinit tsc2007_probe(struct i2c_client *client, | |
265 | const struct i2c_device_id *id) | |
50b6f1f4 KL |
266 | { |
267 | struct tsc2007 *ts; | |
268 | struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data; | |
269 | struct input_dev *input_dev; | |
270 | int err; | |
271 | ||
d570e9ef | 272 | if (!pdata) { |
50b6f1f4 KL |
273 | dev_err(&client->dev, "platform data is required!\n"); |
274 | return -EINVAL; | |
275 | } | |
276 | ||
277 | if (!i2c_check_functionality(client->adapter, | |
278 | I2C_FUNC_SMBUS_READ_WORD_DATA)) | |
279 | return -EIO; | |
280 | ||
281 | ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL); | |
282 | input_dev = input_allocate_device(); | |
283 | if (!ts || !input_dev) { | |
284 | err = -ENOMEM; | |
285 | goto err_free_mem; | |
286 | } | |
287 | ||
288 | ts->client = client; | |
141586bc | 289 | ts->irq = client->irq; |
50b6f1f4 | 290 | ts->input = input_dev; |
141586bc | 291 | INIT_DELAYED_WORK(&ts->work, tsc2007_work); |
50b6f1f4 | 292 | |
50b6f1f4 KL |
293 | ts->model = pdata->model; |
294 | ts->x_plate_ohms = pdata->x_plate_ohms; | |
295 | ts->get_pendown_state = pdata->get_pendown_state; | |
296 | ts->clear_penirq = pdata->clear_penirq; | |
297 | ||
4e8718a1 KS |
298 | snprintf(ts->phys, sizeof(ts->phys), |
299 | "%s/input0", dev_name(&client->dev)); | |
50b6f1f4 KL |
300 | |
301 | input_dev->name = "TSC2007 Touchscreen"; | |
302 | input_dev->phys = ts->phys; | |
303 | input_dev->id.bustype = BUS_I2C; | |
304 | ||
305 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | |
306 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | |
307 | ||
308 | input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); | |
309 | input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); | |
310 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); | |
311 | ||
cad065fd RR |
312 | if (pdata->init_platform_hw) |
313 | pdata->init_platform_hw(); | |
75fba3b0 | 314 | |
50b6f1f4 KL |
315 | err = request_irq(ts->irq, tsc2007_irq, 0, |
316 | client->dev.driver->name, ts); | |
317 | if (err < 0) { | |
318 | dev_err(&client->dev, "irq %d busy?\n", ts->irq); | |
319 | goto err_free_mem; | |
320 | } | |
321 | ||
cf5f439b RR |
322 | /* Prepare for touch readings - power down ADC and enable PENIRQ */ |
323 | err = tsc2007_xfer(ts, PWRDOWN); | |
324 | if (err < 0) | |
325 | goto err_free_irq; | |
326 | ||
50b6f1f4 KL |
327 | err = input_register_device(input_dev); |
328 | if (err) | |
329 | goto err_free_irq; | |
330 | ||
141586bc DT |
331 | i2c_set_clientdata(client, ts); |
332 | ||
50b6f1f4 KL |
333 | return 0; |
334 | ||
335 | err_free_irq: | |
141586bc | 336 | tsc2007_free_irq(ts); |
cad065fd RR |
337 | if (pdata->exit_platform_hw) |
338 | pdata->exit_platform_hw(); | |
50b6f1f4 KL |
339 | err_free_mem: |
340 | input_free_device(input_dev); | |
341 | kfree(ts); | |
342 | return err; | |
343 | } | |
344 | ||
141586bc | 345 | static int __devexit tsc2007_remove(struct i2c_client *client) |
50b6f1f4 KL |
346 | { |
347 | struct tsc2007 *ts = i2c_get_clientdata(client); | |
141586bc | 348 | struct tsc2007_platform_data *pdata = client->dev.platform_data; |
50b6f1f4 | 349 | |
141586bc | 350 | tsc2007_free_irq(ts); |
75fba3b0 | 351 | |
cad065fd RR |
352 | if (pdata->exit_platform_hw) |
353 | pdata->exit_platform_hw(); | |
50b6f1f4 | 354 | |
50b6f1f4 KL |
355 | input_unregister_device(ts->input); |
356 | kfree(ts); | |
357 | ||
358 | return 0; | |
359 | } | |
360 | ||
ce7b39a1 | 361 | static const struct i2c_device_id tsc2007_idtable[] = { |
50b6f1f4 KL |
362 | { "tsc2007", 0 }, |
363 | { } | |
364 | }; | |
365 | ||
366 | MODULE_DEVICE_TABLE(i2c, tsc2007_idtable); | |
367 | ||
368 | static struct i2c_driver tsc2007_driver = { | |
369 | .driver = { | |
370 | .owner = THIS_MODULE, | |
371 | .name = "tsc2007" | |
372 | }, | |
373 | .id_table = tsc2007_idtable, | |
374 | .probe = tsc2007_probe, | |
141586bc | 375 | .remove = __devexit_p(tsc2007_remove), |
50b6f1f4 KL |
376 | }; |
377 | ||
378 | static int __init tsc2007_init(void) | |
379 | { | |
380 | return i2c_add_driver(&tsc2007_driver); | |
381 | } | |
382 | ||
383 | static void __exit tsc2007_exit(void) | |
384 | { | |
385 | i2c_del_driver(&tsc2007_driver); | |
386 | } | |
387 | ||
388 | module_init(tsc2007_init); | |
389 | module_exit(tsc2007_exit); | |
390 | ||
391 | MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>"); | |
392 | MODULE_DESCRIPTION("TSC2007 TouchScreen Driver"); | |
393 | MODULE_LICENSE("GPL"); |