Commit | Line | Data |
---|---|---|
1b8be32e RP |
1 | /* |
2 | * TI Touch Screen driver | |
3 | * | |
4 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation version 2. | |
9 | * | |
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | * kind, whether express or implied; without even the implied warranty | |
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | ||
1b8be32e RP |
17 | #include <linux/kernel.h> |
18 | #include <linux/err.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/input.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/interrupt.h> | |
23 | #include <linux/clk.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/io.h> | |
1b8be32e | 26 | #include <linux/delay.h> |
0396310b PR |
27 | #include <linux/of.h> |
28 | #include <linux/of_device.h> | |
1b8be32e | 29 | |
2b99bafa | 30 | #include <linux/mfd/ti_am335x_tscadc.h> |
33f5cc60 PR |
31 | |
32 | #define ADCFSM_STEPID 0x10 | |
1b8be32e | 33 | #define SEQ_SETTLE 275 |
1b8be32e | 34 | #define MAX_12BIT ((1 << 12) - 1) |
1b8be32e | 35 | |
bb76dc09 PR |
36 | static const int config_pins[] = { |
37 | STEPCONFIG_XPP, | |
38 | STEPCONFIG_XNN, | |
39 | STEPCONFIG_YPP, | |
40 | STEPCONFIG_YNN, | |
41 | }; | |
42 | ||
55c04de5 | 43 | struct titsc { |
1b8be32e | 44 | struct input_dev *input; |
2b99bafa | 45 | struct ti_tscadc_dev *mfd_tscadc; |
1b8be32e RP |
46 | unsigned int irq; |
47 | unsigned int wires; | |
48 | unsigned int x_plate_resistance; | |
49 | bool pen_down; | |
0396310b | 50 | int coordinate_readouts; |
bb76dc09 PR |
51 | u32 config_inp[4]; |
52 | u32 bit_xp, bit_xn, bit_yp, bit_yn; | |
53 | u32 inp_xp, inp_xn, inp_yp, inp_yn; | |
baee5399 | 54 | u32 step_mask; |
bf223612 | 55 | u32 charge_delay; |
1b8be32e RP |
56 | }; |
57 | ||
55c04de5 | 58 | static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) |
1b8be32e | 59 | { |
2b99bafa | 60 | return readl(ts->mfd_tscadc->tscadc_base + reg); |
1b8be32e RP |
61 | } |
62 | ||
55c04de5 | 63 | static void titsc_writel(struct titsc *tsc, unsigned int reg, |
1b8be32e RP |
64 | unsigned int val) |
65 | { | |
2b99bafa | 66 | writel(val, tsc->mfd_tscadc->tscadc_base + reg); |
1b8be32e RP |
67 | } |
68 | ||
bb76dc09 PR |
69 | static int titsc_config_wires(struct titsc *ts_dev) |
70 | { | |
71 | u32 analog_line[4]; | |
72 | u32 wire_order[4]; | |
73 | int i, bit_cfg; | |
74 | ||
75 | for (i = 0; i < 4; i++) { | |
76 | /* | |
77 | * Get the order in which TSC wires are attached | |
78 | * w.r.t. each of the analog input lines on the EVM. | |
79 | */ | |
80 | analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4; | |
81 | wire_order[i] = ts_dev->config_inp[i] & 0x0F; | |
82 | if (WARN_ON(analog_line[i] > 7)) | |
83 | return -EINVAL; | |
84 | if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins))) | |
85 | return -EINVAL; | |
86 | } | |
87 | ||
88 | for (i = 0; i < 4; i++) { | |
89 | int an_line; | |
90 | int wi_order; | |
91 | ||
92 | an_line = analog_line[i]; | |
93 | wi_order = wire_order[i]; | |
94 | bit_cfg = config_pins[wi_order]; | |
95 | if (bit_cfg == 0) | |
96 | return -EINVAL; | |
97 | switch (wi_order) { | |
98 | case 0: | |
99 | ts_dev->bit_xp = bit_cfg; | |
100 | ts_dev->inp_xp = an_line; | |
101 | break; | |
102 | ||
103 | case 1: | |
104 | ts_dev->bit_xn = bit_cfg; | |
105 | ts_dev->inp_xn = an_line; | |
106 | break; | |
107 | ||
108 | case 2: | |
109 | ts_dev->bit_yp = bit_cfg; | |
110 | ts_dev->inp_yp = an_line; | |
111 | break; | |
112 | case 3: | |
113 | ts_dev->bit_yn = bit_cfg; | |
114 | ts_dev->inp_yn = an_line; | |
115 | break; | |
116 | } | |
117 | } | |
118 | return 0; | |
119 | } | |
120 | ||
55c04de5 | 121 | static void titsc_step_config(struct titsc *ts_dev) |
1b8be32e RP |
122 | { |
123 | unsigned int config; | |
8c896308 | 124 | int i; |
3a59684c | 125 | int end_step, first_step, tsc_steps; |
8c896308 | 126 | u32 stepenable; |
1b8be32e RP |
127 | |
128 | config = STEPCONFIG_MODE_HWSYNC | | |
bb76dc09 | 129 | STEPCONFIG_AVG_16 | ts_dev->bit_xp; |
1b8be32e RP |
130 | switch (ts_dev->wires) { |
131 | case 4: | |
bb76dc09 | 132 | config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; |
1b8be32e RP |
133 | break; |
134 | case 5: | |
bb76dc09 PR |
135 | config |= ts_dev->bit_yn | |
136 | STEPCONFIG_INP_AN4 | ts_dev->bit_xn | | |
137 | ts_dev->bit_yp; | |
1b8be32e RP |
138 | break; |
139 | case 8: | |
bb76dc09 | 140 | config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; |
1b8be32e RP |
141 | break; |
142 | } | |
143 | ||
3a59684c BG |
144 | tsc_steps = ts_dev->coordinate_readouts * 2 + 2; |
145 | first_step = TOTAL_STEPS - tsc_steps; | |
146 | /* Steps 16 to 16-coordinate_readouts is for X */ | |
147 | end_step = first_step + tsc_steps; | |
148 | for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) { | |
55c04de5 PR |
149 | titsc_writel(ts_dev, REG_STEPCONFIG(i), config); |
150 | titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); | |
1b8be32e RP |
151 | } |
152 | ||
153 | config = 0; | |
154 | config = STEPCONFIG_MODE_HWSYNC | | |
bb76dc09 | 155 | STEPCONFIG_AVG_16 | ts_dev->bit_yn | |
8c896308 | 156 | STEPCONFIG_INM_ADCREFM; |
1b8be32e RP |
157 | switch (ts_dev->wires) { |
158 | case 4: | |
bb76dc09 | 159 | config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); |
1b8be32e RP |
160 | break; |
161 | case 5: | |
bb76dc09 PR |
162 | config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 | |
163 | ts_dev->bit_xn | ts_dev->bit_yp; | |
1b8be32e RP |
164 | break; |
165 | case 8: | |
bb76dc09 | 166 | config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); |
1b8be32e RP |
167 | break; |
168 | } | |
169 | ||
3a59684c BG |
170 | /* 1 ... coordinate_readouts is for Y */ |
171 | end_step = first_step + ts_dev->coordinate_readouts; | |
172 | for (i = first_step; i < end_step; i++) { | |
55c04de5 PR |
173 | titsc_writel(ts_dev, REG_STEPCONFIG(i), config); |
174 | titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); | |
1b8be32e RP |
175 | } |
176 | ||
344d635b | 177 | /* Make CHARGECONFIG same as IDLECONFIG */ |
1b8be32e | 178 | |
344d635b | 179 | config = titsc_readl(ts_dev, REG_IDLECONFIG); |
55c04de5 | 180 | titsc_writel(ts_dev, REG_CHARGECONFIG, config); |
bf223612 | 181 | titsc_writel(ts_dev, REG_CHARGEDELAY, ts_dev->charge_delay); |
1b8be32e | 182 | |
3a59684c | 183 | /* coordinate_readouts + 1 ... coordinate_readouts + 2 is for Z */ |
1b8be32e | 184 | config = STEPCONFIG_MODE_HWSYNC | |
bb76dc09 PR |
185 | STEPCONFIG_AVG_16 | ts_dev->bit_yp | |
186 | ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | | |
187 | STEPCONFIG_INP(ts_dev->inp_xp); | |
8c896308 SAS |
188 | titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); |
189 | titsc_writel(ts_dev, REG_STEPDELAY(end_step), | |
d1fb5743 | 190 | STEPCONFIG_OPENDLY); |
1b8be32e | 191 | |
8c896308 SAS |
192 | end_step++; |
193 | config |= STEPCONFIG_INP(ts_dev->inp_yn); | |
194 | titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); | |
195 | titsc_writel(ts_dev, REG_STEPDELAY(end_step), | |
d1fb5743 | 196 | STEPCONFIG_OPENDLY); |
1b8be32e | 197 | |
3a59684c BG |
198 | /* The steps end ... end - readouts * 2 + 2 and bit 0 for TS_Charge */ |
199 | stepenable = 1; | |
200 | for (i = 0; i < tsc_steps; i++) | |
201 | stepenable |= 1 << (first_step + i + 1); | |
202 | ||
baee5399 | 203 | ts_dev->step_mask = stepenable; |
7e170c6e | 204 | am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); |
1b8be32e RP |
205 | } |
206 | ||
55c04de5 | 207 | static void titsc_read_coordinates(struct titsc *ts_dev, |
8c896308 | 208 | u32 *x, u32 *y, u32 *z1, u32 *z2) |
1b8be32e | 209 | { |
55c04de5 | 210 | unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); |
1b8be32e RP |
211 | unsigned int prev_val_x = ~0, prev_val_y = ~0; |
212 | unsigned int prev_diff_x = ~0, prev_diff_y = ~0; | |
213 | unsigned int read, diff; | |
2b99bafa | 214 | unsigned int i, channel; |
8c896308 | 215 | unsigned int creads = ts_dev->coordinate_readouts; |
3a59684c | 216 | unsigned int first_step = TOTAL_STEPS - (creads * 2 + 2); |
1b8be32e | 217 | |
8c896308 SAS |
218 | *z1 = *z2 = 0; |
219 | if (fifocount % (creads * 2 + 2)) | |
220 | fifocount -= fifocount % (creads * 2 + 2); | |
1b8be32e RP |
221 | /* |
222 | * Delta filter is used to remove large variations in sampled | |
223 | * values from ADC. The filter tries to predict where the next | |
224 | * coordinate could be. This is done by taking a previous | |
225 | * coordinate and subtracting it form current one. Further the | |
226 | * algorithm compares the difference with that of a present value, | |
227 | * if true the value is reported to the sub system. | |
228 | */ | |
8c896308 | 229 | for (i = 0; i < fifocount; i++) { |
2b99bafa | 230 | read = titsc_readl(ts_dev, REG_FIFO0); |
8c896308 SAS |
231 | |
232 | channel = (read & 0xf0000) >> 16; | |
233 | read &= 0xfff; | |
3a59684c | 234 | if (channel > first_step + creads + 2) { |
2b99bafa PR |
235 | diff = abs(read - prev_val_x); |
236 | if (diff < prev_diff_x) { | |
237 | prev_diff_x = diff; | |
238 | *x = read; | |
239 | } | |
240 | prev_val_x = read; | |
1b8be32e | 241 | |
3a59684c BG |
242 | } else if (channel == first_step + creads + 1) { |
243 | *z1 = read; | |
244 | ||
245 | } else if (channel == first_step + creads + 2) { | |
246 | *z2 = read; | |
247 | ||
248 | } else if (channel > first_step) { | |
2b99bafa PR |
249 | diff = abs(read - prev_val_y); |
250 | if (diff < prev_diff_y) { | |
251 | prev_diff_y = diff; | |
252 | *y = read; | |
253 | } | |
254 | prev_val_y = read; | |
1b8be32e | 255 | } |
1b8be32e RP |
256 | } |
257 | } | |
258 | ||
55c04de5 | 259 | static irqreturn_t titsc_irq(int irq, void *dev) |
1b8be32e | 260 | { |
55c04de5 | 261 | struct titsc *ts_dev = dev; |
1b8be32e | 262 | struct input_dev *input_dev = ts_dev->input; |
344d635b | 263 | unsigned int fsm, status, irqclr = 0; |
1b8be32e RP |
264 | unsigned int x = 0, y = 0; |
265 | unsigned int z1, z2, z; | |
1b8be32e | 266 | |
344d635b BG |
267 | status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); |
268 | if (status & IRQENB_HW_PEN) { | |
269 | ts_dev->pen_down = true; | |
270 | titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); | |
271 | titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); | |
272 | irqclr |= IRQENB_HW_PEN; | |
273 | } | |
274 | ||
275 | if (status & IRQENB_PENUP) { | |
276 | fsm = titsc_readl(ts_dev, REG_ADCFSM); | |
277 | if (fsm == ADCFSM_STEPID) { | |
278 | ts_dev->pen_down = false; | |
279 | input_report_key(input_dev, BTN_TOUCH, 0); | |
280 | input_report_abs(input_dev, ABS_PRESSURE, 0); | |
281 | input_sync(input_dev); | |
282 | } else { | |
283 | ts_dev->pen_down = true; | |
284 | } | |
285 | irqclr |= IRQENB_PENUP; | |
286 | } | |
287 | ||
288 | if (status & IRQENB_EOS) | |
289 | irqclr |= IRQENB_EOS; | |
290 | ||
baee5399 ZL |
291 | /* |
292 | * ADC and touchscreen share the IRQ line. | |
293 | * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only | |
294 | */ | |
30af55f9 | 295 | if (status & IRQENB_FIFO0THRES) { |
1b8be32e | 296 | |
8c896308 | 297 | titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2); |
2b99bafa | 298 | |
1b8be32e RP |
299 | if (ts_dev->pen_down && z1 != 0 && z2 != 0) { |
300 | /* | |
301 | * Calculate pressure using formula | |
302 | * Resistance(touch) = x plate resistance * | |
303 | * x postion/4096 * ((z2 / z1) - 1) | |
304 | */ | |
8c896308 | 305 | z = z1 - z2; |
1b8be32e RP |
306 | z *= x; |
307 | z *= ts_dev->x_plate_resistance; | |
8c896308 | 308 | z /= z2; |
1b8be32e RP |
309 | z = (z + 2047) >> 12; |
310 | ||
311 | if (z <= MAX_12BIT) { | |
312 | input_report_abs(input_dev, ABS_X, x); | |
313 | input_report_abs(input_dev, ABS_Y, y); | |
314 | input_report_abs(input_dev, ABS_PRESSURE, z); | |
315 | input_report_key(input_dev, BTN_TOUCH, 1); | |
316 | input_sync(input_dev); | |
317 | } | |
318 | } | |
30af55f9 | 319 | irqclr |= IRQENB_FIFO0THRES; |
1b8be32e | 320 | } |
9a28b883 SAS |
321 | if (irqclr) { |
322 | titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); | |
344d635b BG |
323 | if (status & IRQENB_EOS) |
324 | am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, | |
325 | ts_dev->step_mask); | |
9a28b883 SAS |
326 | return IRQ_HANDLED; |
327 | } | |
328 | return IRQ_NONE; | |
1b8be32e RP |
329 | } |
330 | ||
0396310b PR |
331 | static int titsc_parse_dt(struct platform_device *pdev, |
332 | struct titsc *ts_dev) | |
333 | { | |
334 | struct device_node *node = pdev->dev.of_node; | |
335 | int err; | |
336 | ||
337 | if (!node) | |
338 | return -EINVAL; | |
339 | ||
340 | err = of_property_read_u32(node, "ti,wires", &ts_dev->wires); | |
341 | if (err < 0) | |
342 | return err; | |
343 | switch (ts_dev->wires) { | |
344 | case 4: | |
345 | case 5: | |
346 | case 8: | |
347 | break; | |
348 | default: | |
349 | return -EINVAL; | |
350 | } | |
351 | ||
352 | err = of_property_read_u32(node, "ti,x-plate-resistance", | |
353 | &ts_dev->x_plate_resistance); | |
354 | if (err < 0) | |
355 | return err; | |
356 | ||
c9aeb249 FB |
357 | /* |
358 | * Try with the new binding first. If it fails, try again with | |
359 | * bogus, miss-spelled version. | |
360 | */ | |
361 | err = of_property_read_u32(node, "ti,coordinate-readouts", | |
0396310b | 362 | &ts_dev->coordinate_readouts); |
31972f6e FB |
363 | if (err < 0) { |
364 | dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n"); | |
c9aeb249 FB |
365 | err = of_property_read_u32(node, "ti,coordiante-readouts", |
366 | &ts_dev->coordinate_readouts); | |
31972f6e FB |
367 | } |
368 | ||
0396310b PR |
369 | if (err < 0) |
370 | return err; | |
371 | ||
bf223612 V |
372 | err = of_property_read_u32(node, "ti,charge-delay", |
373 | &ts_dev->charge_delay); | |
374 | /* | |
375 | * If ti,charge-delay value is not specified, then use | |
376 | * CHARGEDLY_OPENDLY as the default value. | |
377 | */ | |
378 | if (err < 0) { | |
379 | ts_dev->charge_delay = CHARGEDLY_OPENDLY; | |
380 | dev_warn(&pdev->dev, "ti,charge-delay not specified\n"); | |
381 | } | |
382 | ||
0396310b PR |
383 | return of_property_read_u32_array(node, "ti,wire-config", |
384 | ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); | |
1b8be32e RP |
385 | } |
386 | ||
387 | /* | |
388 | * The functions for inserting/removing driver as a module. | |
389 | */ | |
390 | ||
31564cbd | 391 | static int titsc_probe(struct platform_device *pdev) |
1b8be32e | 392 | { |
55c04de5 | 393 | struct titsc *ts_dev; |
1b8be32e | 394 | struct input_dev *input_dev; |
a9bce1b0 | 395 | struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); |
1b8be32e | 396 | int err; |
1b8be32e | 397 | |
1b8be32e | 398 | /* Allocate memory for device */ |
55c04de5 | 399 | ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); |
1b8be32e RP |
400 | input_dev = input_allocate_device(); |
401 | if (!ts_dev || !input_dev) { | |
402 | dev_err(&pdev->dev, "failed to allocate memory.\n"); | |
403 | err = -ENOMEM; | |
404 | goto err_free_mem; | |
405 | } | |
406 | ||
2b99bafa PR |
407 | tscadc_dev->tsc = ts_dev; |
408 | ts_dev->mfd_tscadc = tscadc_dev; | |
1b8be32e | 409 | ts_dev->input = input_dev; |
2b99bafa | 410 | ts_dev->irq = tscadc_dev->irq; |
0396310b | 411 | |
b9194fdf | 412 | err = titsc_parse_dt(pdev, ts_dev); |
0396310b PR |
413 | if (err) { |
414 | dev_err(&pdev->dev, "Could not find valid DT data.\n"); | |
415 | goto err_free_mem; | |
416 | } | |
1b8be32e | 417 | |
55c04de5 | 418 | err = request_irq(ts_dev->irq, titsc_irq, |
baee5399 | 419 | IRQF_SHARED, pdev->dev.driver->name, ts_dev); |
1b8be32e RP |
420 | if (err) { |
421 | dev_err(&pdev->dev, "failed to allocate irq.\n"); | |
2b99bafa | 422 | goto err_free_mem; |
1b8be32e | 423 | } |
1b8be32e | 424 | |
55c04de5 | 425 | titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); |
344d635b | 426 | titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS); |
bb76dc09 PR |
427 | err = titsc_config_wires(ts_dev); |
428 | if (err) { | |
429 | dev_err(&pdev->dev, "wrong i/p wire configuration\n"); | |
430 | goto err_free_irq; | |
431 | } | |
55c04de5 | 432 | titsc_step_config(ts_dev); |
8c896308 SAS |
433 | titsc_writel(ts_dev, REG_FIFO0THR, |
434 | ts_dev->coordinate_readouts * 2 + 2 - 1); | |
1b8be32e | 435 | |
2b99bafa | 436 | input_dev->name = "ti-tsc"; |
1b8be32e RP |
437 | input_dev->dev.parent = &pdev->dev; |
438 | ||
439 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | |
440 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | |
441 | ||
442 | input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); | |
443 | input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); | |
444 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); | |
445 | ||
446 | /* register to the input system */ | |
447 | err = input_register_device(input_dev); | |
448 | if (err) | |
2b99bafa | 449 | goto err_free_irq; |
1b8be32e RP |
450 | |
451 | platform_set_drvdata(pdev, ts_dev); | |
452 | return 0; | |
453 | ||
1b8be32e RP |
454 | err_free_irq: |
455 | free_irq(ts_dev->irq, ts_dev); | |
1b8be32e RP |
456 | err_free_mem: |
457 | input_free_device(input_dev); | |
458 | kfree(ts_dev); | |
459 | return err; | |
460 | } | |
461 | ||
31564cbd | 462 | static int titsc_remove(struct platform_device *pdev) |
1b8be32e | 463 | { |
a9bce1b0 SAS |
464 | struct titsc *ts_dev = platform_get_drvdata(pdev); |
465 | u32 steps; | |
1b8be32e RP |
466 | |
467 | free_irq(ts_dev->irq, ts_dev); | |
468 | ||
abeccee4 | 469 | /* total steps followed by the enable mask */ |
0396310b | 470 | steps = 2 * ts_dev->coordinate_readouts + 2; |
abeccee4 PR |
471 | steps = (1 << steps) - 1; |
472 | am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps); | |
473 | ||
1b8be32e RP |
474 | input_unregister_device(ts_dev->input); |
475 | ||
2b99bafa PR |
476 | kfree(ts_dev); |
477 | return 0; | |
478 | } | |
1b8be32e | 479 | |
2b99bafa PR |
480 | #ifdef CONFIG_PM |
481 | static int titsc_suspend(struct device *dev) | |
482 | { | |
a9bce1b0 SAS |
483 | struct titsc *ts_dev = dev_get_drvdata(dev); |
484 | struct ti_tscadc_dev *tscadc_dev; | |
2b99bafa PR |
485 | unsigned int idle; |
486 | ||
a9bce1b0 | 487 | tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); |
2b99bafa PR |
488 | if (device_may_wakeup(tscadc_dev->dev)) { |
489 | idle = titsc_readl(ts_dev, REG_IRQENABLE); | |
490 | titsc_writel(ts_dev, REG_IRQENABLE, | |
491 | (idle | IRQENB_HW_PEN)); | |
492 | titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); | |
493 | } | |
494 | return 0; | |
495 | } | |
1b8be32e | 496 | |
2b99bafa PR |
497 | static int titsc_resume(struct device *dev) |
498 | { | |
a9bce1b0 SAS |
499 | struct titsc *ts_dev = dev_get_drvdata(dev); |
500 | struct ti_tscadc_dev *tscadc_dev; | |
1b8be32e | 501 | |
a9bce1b0 | 502 | tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); |
2b99bafa PR |
503 | if (device_may_wakeup(tscadc_dev->dev)) { |
504 | titsc_writel(ts_dev, REG_IRQWAKEUP, | |
505 | 0x00); | |
506 | titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); | |
507 | } | |
508 | titsc_step_config(ts_dev); | |
509 | titsc_writel(ts_dev, REG_FIFO0THR, | |
8c896308 | 510 | ts_dev->coordinate_readouts * 2 + 2 - 1); |
1b8be32e RP |
511 | return 0; |
512 | } | |
513 | ||
2b99bafa PR |
514 | static const struct dev_pm_ops titsc_pm_ops = { |
515 | .suspend = titsc_suspend, | |
516 | .resume = titsc_resume, | |
517 | }; | |
518 | #define TITSC_PM_OPS (&titsc_pm_ops) | |
519 | #else | |
520 | #define TITSC_PM_OPS NULL | |
521 | #endif | |
522 | ||
0396310b PR |
523 | static const struct of_device_id ti_tsc_dt_ids[] = { |
524 | { .compatible = "ti,am3359-tsc", }, | |
525 | { } | |
526 | }; | |
527 | MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids); | |
528 | ||
1b8be32e | 529 | static struct platform_driver ti_tsc_driver = { |
55c04de5 | 530 | .probe = titsc_probe, |
31564cbd | 531 | .remove = titsc_remove, |
1b8be32e | 532 | .driver = { |
5f184e63 | 533 | .name = "TI-am335x-tsc", |
2b99bafa | 534 | .pm = TITSC_PM_OPS, |
8e6146bf | 535 | .of_match_table = ti_tsc_dt_ids, |
1b8be32e RP |
536 | }, |
537 | }; | |
538 | module_platform_driver(ti_tsc_driver); | |
539 | ||
540 | MODULE_DESCRIPTION("TI touchscreen controller driver"); | |
541 | MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); | |
542 | MODULE_LICENSE("GPL"); |