aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSreedhara DS <sreedhara.ds@intel.com>2010-06-01 07:50:06 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-06-04 16:38:51 -0400
commita4cff8b82a3cd0c95348ad4ae28cee7a918a35cd (patch)
treed8bd405db2ac5c30ec5934f6555c10c129e427af /drivers
parentd7636e0b0769e0f4f437ff33168d723f86e7c080 (diff)
Staging: mid: Intel MID touch screen driver
Touchscreen driver used by intel mid devices. Some clean up by Alan Cox. This driver is basically ready for upstreaming properly but is tied wrongly to the SPI layer and needs firmware/SFI changes to fix that. Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/Kconfig2
-rw-r--r--drivers/staging/Makefile1
-rw-r--r--drivers/staging/mrst-touchscreen/Kconfig7
-rw-r--r--drivers/staging/mrst-touchscreen/Makefile3
-rw-r--r--drivers/staging/mrst-touchscreen/TODO2
-rw-r--r--drivers/staging/mrst-touchscreen/intel-mid-touch.c864
6 files changed, 879 insertions, 0 deletions
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 2a1a7c296429..883c88f3de7a 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -143,5 +143,7 @@ source "drivers/staging/adis16255/Kconfig"
143 143
144source "drivers/staging/xgifb/Kconfig" 144source "drivers/staging/xgifb/Kconfig"
145 145
146source "drivers/staging/mrst-touchscreen/Kconfig"
147
146endif # !STAGING_EXCLUDE_BUILD 148endif # !STAGING_EXCLUDE_BUILD
147endif # STAGING 149endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 4a02c0df9ced..352ca2a973c2 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -52,3 +52,4 @@ obj-$(CONFIG_CXT1E1) += cxt1e1/
52obj-$(CONFIG_TI_ST) += ti-st/ 52obj-$(CONFIG_TI_ST) += ti-st/
53obj-$(CONFIG_ADIS16255) += adis16255/ 53obj-$(CONFIG_ADIS16255) += adis16255/
54obj-$(CONFIG_FB_XGI) += xgifb/ 54obj-$(CONFIG_FB_XGI) += xgifb/
55obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/
diff --git a/drivers/staging/mrst-touchscreen/Kconfig b/drivers/staging/mrst-touchscreen/Kconfig
new file mode 100644
index 000000000000..c2af49217084
--- /dev/null
+++ b/drivers/staging/mrst-touchscreen/Kconfig
@@ -0,0 +1,7 @@
1config TOUCHSCREEN_INTEL_MID
2 tristate "Intel MID platform resistive touchscreen"
3 depends on INTEL_SCU_IPC
4 default y
5 help
6 Say Y here if you have a Intel MID based touchscreen
7 If unsure, say N.
diff --git a/drivers/staging/mrst-touchscreen/Makefile b/drivers/staging/mrst-touchscreen/Makefile
new file mode 100644
index 000000000000..2d638b0d70bf
--- /dev/null
+++ b/drivers/staging/mrst-touchscreen/Makefile
@@ -0,0 +1,3 @@
1obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) := intel_mid_touch.o
2
3
diff --git a/drivers/staging/mrst-touchscreen/TODO b/drivers/staging/mrst-touchscreen/TODO
new file mode 100644
index 000000000000..7157028d634a
--- /dev/null
+++ b/drivers/staging/mrst-touchscreen/TODO
@@ -0,0 +1,2 @@
1- Move the driver to not think it is SPI (requires fixing some of the SFI
2 and firmware side)
diff --git a/drivers/staging/mrst-touchscreen/intel-mid-touch.c b/drivers/staging/mrst-touchscreen/intel-mid-touch.c
new file mode 100644
index 000000000000..1db00975a594
--- /dev/null
+++ b/drivers/staging/mrst-touchscreen/intel-mid-touch.c
@@ -0,0 +1,864 @@
1/*
2 * intel_mid_touch.c - Intel MID Resistive Touch Screen Driver
3 *
4 * Copyright (C) 2008 Intel Corp
5 *
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; ifnot, write to the Free Software Foundation, Inc.,
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20 *
21 * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
22 * Ramesh Agarwal (ramesh.agarwal@intel.com)
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 *
25 * TODO:
26 * kill off mrstouch_debug eventually
27 * review conversion of r/m/w sequences
28 * Replace interrupt mutex abuse
29 * Kill of mrstouchdevp pointer
30 *
31 */
32
33#include <linux/module.h>
34#include <linux/init.h>
35#include <linux/input.h>
36#include <linux/interrupt.h>
37#include <linux/err.h>
38#include <linux/param.h>
39#include <linux/spi/spi.h>
40#include <linux/irq.h>
41#include <linux/delay.h>
42#include <linux/kthread.h>
43#include <asm/intel_scu_ipc.h>
44
45
46#if defined(MRSTOUCH_DEBUG)
47#define mrstouch_debug(fmt, args...)\
48 do { \
49 printk(KERN_DEBUG "\n[MRSTOUCH(%d)] - ", __LINE__); \
50 printk(KERN_DEBUG fmt, ##args); \
51 } while (0);
52#else
53#define mrstouch_debug(fmt, args...)
54#endif
55
56/* PMIC Interrupt registers */
57#define PMIC_REG_ID1 0x00 /*PMIC ID1 register */
58
59/* PMIC Interrupt registers */
60#define PMIC_REG_INT 0x04 /*PMIC interrupt register */
61#define PMIC_REG_MINT 0x05 /*PMIC interrupt mask register */
62
63/* ADC Interrupt registers */
64#define PMIC_REG_ADCINT 0x5F /*ADC interrupt register */
65#define PMIC_REG_MADCINT 0x60 /*ADC interrupt mask register */
66
67/* ADC Control registers */
68#define PMIC_REG_ADCCNTL1 0x61 /*ADC control register */
69
70/* ADC Channel Selection registers */
71#define PMICADDR0 0xA4
72#define END_OF_CHANNEL 0x1F
73
74/* ADC Result register */
75#define PMIC_REG_ADCSNS0H 0x64
76
77/* ADC channels for touch screen */
78#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
79#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
80#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
81#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
82
83/* Touch screen coordinate constants */
84#define TOUCH_PRESSURE 50
85#define TOUCH_PRESSURE_FS 100
86
87#define XMOVE_LIMIT 5
88#define YMOVE_LIMIT 5
89#define XYMOVE_CNT 3
90
91#define MAX_10BIT ((1<<10)-1)
92
93/* Touch screen channel BIAS constants */
94#define XBIAS 0x20
95#define YBIAS 0x40
96#define ZBIAS 0x80
97
98/* Touch screen coordinates */
99#define MIN_X 10
100#define MAX_X 1024
101#define MIN_Y 10
102#define MAX_Y 1024
103#define WAIT_ADC_COMPLETION 10
104
105/* PMIC ADC round robin delays */
106#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
107#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
108
109/* PMIC Vendor Identifiers */
110#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
111#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
112#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
113#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
114
115/* Touch screen device structure */
116struct mrstouch_dev {
117 struct spi_device *spi; /* SPI device associated with touch screen */
118 struct input_dev *input; /* input device for touchscreen*/
119 char phys[32]; /* Device name */
120 struct task_struct *pendet_thrd; /* PENDET interrupt handler */
121 struct mutex lock; /* Sync between interrupt and PENDET handler */
122 bool busy; /* Busy flag */
123 u16 asr; /* Address selection register */
124 int irq; /* Touch screen IRQ # */
125 uint vendor; /* PMIC vendor */
126 uint rev; /* PMIC revision */
127 bool suspended; /* Device suspended status */
128 bool disabled; /* Device disabled status */
129 u16 x; /* X coordinate */
130 u16 y; /* Y coordinate */
131 bool pendown; /* PEN position */
132} ;
133
134
135/* Global Pointer to Touch screen device */
136static struct mrstouch_dev *mrstouchdevp;
137
138/* Utility to read PMIC ID */
139static int mrstouch_pmic_id(uint *vendor, uint *rev)
140{
141 int err;
142 u8 r;
143
144 err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
145 if (err)
146 return err;
147
148 *vendor = r & 0x7;
149 *rev = (r >> 3) & 0x7;
150
151 return 0;
152}
153
154/*
155 * Parse ADC channels to find end of the channel configured by other ADC user
156 * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
157 */
158static int mrstouch_chan_parse(struct mrstouch_dev *tsdev)
159{
160 int err, i, j, found;
161 u32 r32;
162
163 found = -1;
164
165 for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
166 if (found >= 0)
167 break;
168
169 err = intel_scu_ipc_ioread32(PMICADDR0, &r32);
170 if (err)
171 return err;
172
173 for (j = 0; j < 32; j+= 8) {
174 if (((r32 >> j) & 0xFF) == END_OF_CHANNEL) {
175 found = i;
176 break;
177 }
178 }
179 }
180 if (found < 0)
181 return 0;
182
183 if (tsdev->vendor == PMIC_VENDOR_FS) {
184 if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
185 return -ENOSPC;
186 } else {
187 if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
188 return -ENOSPC;
189 }
190 return found;
191}
192
193/* Utility to enable/disable pendet.
194 * pendet set to true enables PENDET interrupt
195 * pendet set to false disables PENDET interrupt
196 * Also clears RND mask bit
197*/
198static int pendet_enable(struct mrstouch_dev *tsdev, bool pendet)
199{
200 u16 reg;
201 u8 r;
202 u8 pendet_enabled = 0;
203 int retry = 0;
204 int err;
205
206 err = intel_scu_ipc_ioread16(PMIC_REG_MADCINT, &reg);
207 if (err)
208 return err;
209
210 if (pendet) {
211 reg &= ~0x0005;
212 reg |= 0x2000; /* Enable pendet */
213 } else
214 reg &= 0xDFFF; /* Disable pendet */
215
216 /* Set MADCINT and update ADCCNTL1 (next reg byte) */
217 err = intel_scu_ipc_iowrite16(PMIC_REG_MADCINT, reg);
218 if (!pendet || err)
219 return err;
220
221 /*
222 * Sometimes even after the register write succeeds
223 * the PMIC register value is not updated. Retry few iterations
224 * to enable pendet.
225 */
226
227 err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r);
228 pendet_enabled = (r >> 5) & 0x01;
229
230 retry = 0;
231 while (!err && !pendet_enabled) {
232 retry++;
233 msleep(10);
234 err = intel_scu_ipc_iowrite8(PMIC_REG_ADCCNTL1, reg >> 8);
235 if (err)
236 break;
237 err = intel_scu_ipc_ioread8(PMIC_REG_ADCCNTL1, &r);
238 if (err == 0)
239 pendet_enabled = (r >> 5) & 0x01;
240 if (retry >= 10) {
241 dev_err(&tsdev->spi->dev, "Touch screen disabled.\n");
242 return -EIO;
243 }
244 }
245 return 0;
246}
247
248/* To read PMIC ADC touch screen result
249 * Reads ADC storage registers for higher 7 and lower 3 bits
250 * converts the two readings to single value and turns off gain bit
251 */
252static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
253{
254 int err;
255 u16 result;
256 u32 res;
257
258 result = PMIC_REG_ADCSNS0H + offset;
259
260 if (chan == MRST_TS_CHAN12)
261 result += 4;
262
263 err = intel_scu_ipc_ioread32(result, &res);
264 if (err)
265 return err;
266
267 /* Mash the bits up */
268
269 *vp = (res & 0xFF) << 3; /* Highest 7 bits */
270 *vp |= (res >> 8) & 0x07; /* Lower 3 bits */
271 *vp &= 0x3FF;
272
273 res >>= 16;
274
275 *vm = (res & 0xFF) << 3; /* Highest 7 bits */
276 *vm |= (res >> 8) & 0x07; /* Lower 3 bits */
277 *vm &= 0x3FF;
278
279 return 0;
280}
281
282/* To configure touch screen channels
283 * Writes touch screen channels to ADC address selection registers
284 */
285static int mrstouch_ts_chan_set(uint offset)
286{
287 int count;
288 u16 chan;
289 u16 reg[5];
290 u8 data[5];
291
292 chan = PMICADDR0 + offset;
293 for (count = 0; count <= 3; count++) {
294 reg[count] = chan++;
295 data[count] = MRST_TS_CHAN10 + count;
296 }
297 reg[count] = chan;
298 data[count] = END_OF_CHANNEL;
299
300 return intel_scu_ipc_writev(reg, data, 5);
301}
302
303/* Initialize ADC */
304static int mrstouch_adc_init(struct mrstouch_dev *tsdev)
305{
306 int err, start;
307 u8 ra, rm;
308
309 err = mrstouch_pmic_id(&tsdev->vendor, &tsdev->rev);
310 if (err) {
311 dev_err(&tsdev->spi->dev, "Unable to read PMIC id\n");
312 return err;
313 }
314
315 start = mrstouch_chan_parse(tsdev);
316 if (start < 0) {
317 dev_err(&tsdev->spi->dev, "Unable to parse channels\n");
318 return start;
319 }
320
321 tsdev->asr = start;
322
323 mrstouch_debug("Channel offset(%d): 0x%X\n", tsdev->asr, tsdev->vendor);
324
325 /* ADC power on, start, enable PENDET and set loop delay
326 * ADC loop delay is set to 4.5 ms approximately
327 * Loop delay more than this results in jitter in adc readings
328 * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
329 * interrupt generation sometimes.
330 */
331
332 if (tsdev->vendor == PMIC_VENDOR_FS) {
333 ra = 0xE0 | ADC_LOOP_DELAY0;
334 rm = 0x5;
335 } else {
336 /* NEC and MAXIm not consistent with loop delay 0 */
337 ra = 0xE0 | ADC_LOOP_DELAY1;
338 rm = 0x0;
339
340 /* configure touch screen channels */
341 err = mrstouch_ts_chan_set(tsdev->asr);
342 if (err)
343 return err;
344 }
345 err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
346 if (err == 0)
347 err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
348 return err;
349}
350
351/* Reports x,y coordinates to event subsystem */
352static void mrstouch_report_xy(struct mrstouch_dev *tsdev, u16 x, u16 y, u16 z)
353{
354 int xdiff, ydiff;
355
356 if (tsdev->pendown && z <= TOUCH_PRESSURE) {
357 /* Pen removed, report button release */
358 mrstouch_debug("BTN REL(%d)", z);
359 input_report_key(tsdev->input, BTN_TOUCH, 0);
360 tsdev->pendown = false;
361 }
362
363 xdiff = abs(x - tsdev->x);
364 ydiff = abs(y - tsdev->y);
365
366 /*
367 if x and y values changes for XYMOVE_CNT readings it is considered
368 as stylus is moving. This is required to differentiate between stylus
369 movement and jitter
370 */
371 if (x < MIN_X || x > MAX_X || y < MIN_Y || y > MAX_Y) {
372 /* Spurious values, release button if touched and return */
373 if (tsdev->pendown) {
374 mrstouch_debug("BTN REL(%d)", z);
375 input_report_key(tsdev->input, BTN_TOUCH, 0);
376 tsdev->pendown = false;
377 }
378 return;
379 } else if (xdiff >= XMOVE_LIMIT || ydiff >= YMOVE_LIMIT) {
380 tsdev->x = x;
381 tsdev->y = y;
382
383 input_report_abs(tsdev->input, ABS_X, x);
384 input_report_abs(tsdev->input, ABS_Y, y);
385 input_sync(tsdev->input);
386 }
387
388
389 if (!tsdev->pendown && z > TOUCH_PRESSURE) {
390 /* Pen touched, report button touch */
391 mrstouch_debug("BTN TCH(%d, %d, %d)", x, y, z);
392 input_report_key(tsdev->input, BTN_TOUCH, 1);
393 tsdev->pendown = true;
394 }
395}
396
397
398/* Utility to start ADC, used by freescale handler */
399static int pendet_mask(void)
400{
401 return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
402}
403
404/* Utility to stop ADC, used by freescale handler */
405static int pendet_umask(void)
406{
407 return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
408}
409
410/* Utility to read ADC, used by freescale handler */
411static int mrstouch_pmic_fs_adc_read(struct mrstouch_dev *tsdev)
412{
413 int err;
414 u16 x, y, z, result;
415 u16 reg[4];
416 u8 data[4];
417
418 result = PMIC_REG_ADCSNS0H + tsdev->asr;
419
420 reg[0] = result + 4;
421 reg[1] = result + 5;
422 reg[2] = result + 16;
423 reg[3] = result + 17;
424
425 err = intel_scu_ipc_readv(reg, data, 4);
426 if (err)
427 goto ipc_error;
428
429 x = data[0] << 3; /* Higher 7 bits */
430 x |= data[1] & 0x7; /* Lower 3 bits */
431 x &= 0x3FF;
432
433 y = data[2] << 3; /* Higher 7 bits */
434 y |= data[3] & 0x7; /* Lower 3 bits */
435 y &= 0x3FF;
436
437 /* Read Z value */
438 reg[0] = result + 28;
439 reg[1] = result + 29;
440
441 err = intel_scu_ipc_readv(reg, data, 4);
442 if (err)
443 goto ipc_error;
444
445 z = data[0] << 3; /* Higher 7 bits */
446 z |= data[1] & 0x7; /* Lower 3 bits */
447 z &= 0x3FF;
448
449#if defined(MRSTOUCH_PRINT_XYZP)
450 mrstouch_debug("X: %d, Y: %d, Z: %d", x, y, z);
451#endif
452
453 if (z >= TOUCH_PRESSURE_FS) {
454 mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE - 1); /* Pen Removed */
455 return TOUCH_PRESSURE - 1;
456 } else {
457 mrstouch_report_xy(tsdev, x, y, TOUCH_PRESSURE + 1); /* Pen Touched */
458 return TOUCH_PRESSURE + 1;
459 }
460
461 return 0;
462
463ipc_error:
464 dev_err(&tsdev->spi->dev, "ipc error during fs_adc read\n");
465 return err;
466}
467
468/* To handle free scale pmic pendet interrupt */
469static int pmic0_pendet(void *dev_id)
470{
471 int err, count;
472 u16 chan;
473 unsigned int touched;
474 struct mrstouch_dev *tsdev = (struct mrstouch_dev *)dev_id;
475 u16 reg[5];
476 u8 data[5];
477
478 chan = PMICADDR0 + tsdev->asr;
479
480 /* Set X BIAS */
481 for (count = 0; count <= 3; count++) {
482 reg[count] = chan++;
483 data[count] = 0x2A;
484 }
485 reg[count] = chan++; /* Dummy */
486 data[count] = 0;
487
488 err = intel_scu_ipc_writev(reg, data, 5);
489 if (err)
490 goto ipc_error;
491
492 msleep(WAIT_ADC_COMPLETION);
493
494 /* Set Y BIAS */
495 for (count = 0; count <= 3; count++) {
496 reg[count] = chan++;
497 data[count] = 0x4A;
498 }
499 reg[count] = chan++; /* Dummy */
500 data[count] = 0;
501
502 err = intel_scu_ipc_writev(reg, data, 5);
503 if (err)
504 goto ipc_error;
505
506 msleep(WAIT_ADC_COMPLETION);
507
508 /* Set Z BIAS */
509 err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
510 if (err)
511 goto ipc_error;
512
513 msleep(WAIT_ADC_COMPLETION);
514
515 /*Read touch screen channels till pen removed
516 * Freescale reports constant value of z for all points
517 * z is high when screen is not touched and low when touched
518 * Map high z value to not touched and low z value to pen touched
519 */
520 touched = mrstouch_pmic_fs_adc_read(tsdev);
521 while (touched > TOUCH_PRESSURE) {
522 touched = mrstouch_pmic_fs_adc_read(tsdev);
523 msleep(WAIT_ADC_COMPLETION);
524 }
525
526 /* Clear all TS channels */
527 chan = PMICADDR0 + tsdev->asr;
528 for (count = 0; count <= 4; count++) {
529 reg[count] = chan++;
530 data[count] = 0;
531 }
532 err = intel_scu_ipc_writev(reg, data, 5);
533 if (err)
534 goto ipc_error;
535
536 for (count = 0; count <= 4; count++) {
537 reg[count] = chan++;
538 data[count] = 0;
539 }
540 err = intel_scu_ipc_writev(reg, data, 5);
541 if (err)
542 goto ipc_error;
543
544 err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
545 if (err)
546 goto ipc_error;
547
548 return 0;
549
550ipc_error:
551 dev_err(&tsdev->spi->dev, "ipc error during pendet\n");
552 return err;
553}
554
555
556/* To enable X, Y and Z bias values
557 * Enables YPYM for X channels and XPXM for Y channels
558 */
559static int mrstouch_ts_bias_set(uint offset, uint bias)
560{
561 int count;
562 u16 chan, start;
563 u16 reg[4];
564 u8 data[4];
565
566 chan = PMICADDR0 + offset;
567 start = MRST_TS_CHAN10;
568
569 for (count = 0; count <= 3; count++) {
570 reg[count] = chan++;
571 data[count] = bias | (start + count);
572 }
573 return intel_scu_ipc_writev(reg, data, 4);
574}
575
576/* To read touch screen channel values */
577static int mrstouch_adc_read(struct mrstouch_dev *tsdev)
578{
579 int err;
580 u16 xp, xm, yp, ym, zp, zm;
581
582 /* configure Y bias for X channels */
583 err = mrstouch_ts_bias_set(tsdev->asr, YBIAS);
584 if (err)
585 goto ipc_error;
586
587 msleep(WAIT_ADC_COMPLETION);
588
589 /* read x+ and x- channels */
590 err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &xp, &xm);
591 if (err)
592 goto ipc_error;
593
594 /* configure x bias for y channels */
595 err = mrstouch_ts_bias_set(tsdev->asr, XBIAS);
596 if (err)
597 goto ipc_error;
598
599 msleep(WAIT_ADC_COMPLETION);
600
601 /* read y+ and y- channels */
602 err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, &yp, &ym);
603 if (err)
604 goto ipc_error;
605
606 /* configure z bias for x and y channels */
607 err = mrstouch_ts_bias_set(tsdev->asr, ZBIAS);
608 if (err)
609 goto ipc_error;
610
611 msleep(WAIT_ADC_COMPLETION);
612
613 /* read z+ and z- channels */
614 err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, &zp, &zm);
615 if (err)
616 goto ipc_error;
617
618#if defined(MRSTOUCH_PRINT_XYZP)
619 printk(KERN_INFO "X+: %d, Y+: %d, Z+: %d\n", xp, yp, zp);
620#endif
621
622#if defined(MRSTOUCH_PRINT_XYZM)
623 printk(KERN_INFO "X-: %d, Y-: %d, Z-: %d\n", xm, ym, zm);
624#endif
625
626 mrstouch_report_xy(tsdev, xp, yp, zp); /* report x and y to eventX */
627
628 return zp;
629
630ipc_error:
631 dev_err(&tsdev->spi->dev, "ipc error during adc read\n");
632 return err;
633}
634
635/* PENDET interrupt handler function for NEC and MAXIM */
636static void pmic12_pendet(void *data)
637{
638 unsigned int touched;
639 struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data;
640
641 /* read touch screen channels till pen removed */
642 do {
643 touched = mrstouch_adc_read(tsdev);
644 } while (touched > TOUCH_PRESSURE);
645}
646
647/* Handler to process PENDET interrupt */
648int mrstouch_pendet(void *data)
649{
650 struct mrstouch_dev *tsdev = (struct mrstouch_dev *)data;
651 while (1) {
652 /* Wait for PENDET interrupt */
653 if (mutex_lock_interruptible(&tsdev->lock)) {
654 msleep(WAIT_ADC_COMPLETION);
655 continue;
656 }
657
658 if (tsdev->busy)
659 return 0;
660
661 tsdev->busy = true;
662
663 if (tsdev->vendor == PMIC_VENDOR_NEC ||
664 tsdev->vendor == PMIC_VENDOR_MAXIM) {
665 /* PENDET must be disabled in NEC before reading ADC */
666 pendet_enable(tsdev,false); /* Disbale PENDET */
667 pmic12_pendet(tsdev);
668 pendet_enable(tsdev, true); /*Enable PENDET */
669 } else if (tsdev->vendor == PMIC_VENDOR_FS) {
670 pendet_umask(); /* Stop ADC */
671 pmic0_pendet(tsdev);
672 pendet_mask(); /* Stop ADC */
673 } else
674 dev_err(&tsdev->spi->dev, "Unsupported touchscreen: %d\n",
675 tsdev->vendor);
676
677 tsdev->busy = false;
678
679 }
680 return 0;
681}
682
683/* PENDET interrupt handler */
684static irqreturn_t pendet_intr_handler(int irq, void *handle)
685{
686 struct mrstouch_dev *tsdev = (struct mrstouch_dev *)handle;
687
688 mutex_unlock(&tsdev->lock);
689 return IRQ_HANDLED;
690}
691
692/* Intializes input device and registers with input subsystem */
693static int ts_input_dev_init(struct mrstouch_dev *tsdev, struct spi_device *spi)
694{
695 int err = 0;
696
697 mrstouch_debug("%s", __func__);
698
699 tsdev->input = input_allocate_device();
700 if (!tsdev->input) {
701 dev_err(&tsdev->spi->dev, "Unable to allocate input device.\n");
702 return -EINVAL;
703 }
704
705 tsdev->input->name = "mrst_touchscreen";
706 snprintf(tsdev->phys, sizeof(tsdev->phys),
707 "%s/input0", dev_name(&spi->dev));
708 tsdev->input->phys = tsdev->phys;
709 tsdev->input->dev.parent = &spi->dev;
710
711 tsdev->input->id.vendor = tsdev->vendor;
712 tsdev->input->id.version = tsdev->rev;
713
714 tsdev->input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
715 tsdev->input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
716
717 input_set_abs_params(tsdev->input, ABS_X, MIN_X, MIN_Y, 0, 0);
718 input_set_abs_params(tsdev->input, ABS_Y, MIN_X, MIN_Y, 0, 0);
719
720 err = input_register_device(tsdev->input);
721 if (err) {
722 dev_err(&tsdev->spi->dev, "unable to register input device\n");
723 input_free_device(tsdev->input);
724 return err;
725 }
726
727 mrstouch_debug("%s", "mrstouch initialized");
728
729 return 0;
730
731}
732
733/* Probe function for touch screen driver */
734static int __devinit mrstouch_probe(struct spi_device *mrstouch_spi)
735{
736 int err;
737 unsigned int myirq;
738 struct mrstouch_dev *tsdev;
739
740 mrstouch_debug("%s(%p)", __func__, mrstouch_spi);
741
742 mrstouchdevp = NULL;
743 myirq = mrstouch_spi->irq;
744
745 if (!mrstouch_spi->irq) {
746 dev_err(&mrstouch_spi->dev, "no interrupt assigned\n");
747 return -EINVAL;
748 }
749
750 tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
751 if (!tsdev) {
752 dev_err(&mrstouch_spi->dev, "unable to allocate memory\n");
753 return -ENOMEM;
754 }
755
756 tsdev->irq = myirq;
757 mrstouchdevp = tsdev;
758
759 err = mrstouch_adc_init(tsdev);
760 if (err) {
761 dev_err(&mrstouch_spi->dev, "ADC init failed\n");
762 goto mrstouch_err_free_mem;
763 }
764
765 dev_set_drvdata(&mrstouch_spi->dev, tsdev);
766 tsdev->spi = mrstouch_spi;
767
768 err = ts_input_dev_init(tsdev, mrstouch_spi);
769 if (err) {
770 dev_err(&tsdev->spi->dev, "ts_input_dev_init failed");
771 goto mrstouch_err_free_mem;
772 }
773
774 mutex_init(&tsdev->lock);
775 mutex_lock(&tsdev->lock)
776
777 mrstouch_debug("Requesting IRQ-%d", myirq);
778 err = request_irq(myirq, pendet_intr_handler,
779 0, "mrstouch", tsdev);
780 if (err) {
781 dev_err(&tsdev->spi->dev, "unable to allocate irq\n");
782 goto mrstouch_err_free_mem;
783 }
784
785 tsdev->pendet_thrd = kthread_run(mrstouch_pendet,
786 (void *)tsdev, "pendet handler");
787 if (IS_ERR(tsdev->pendet_thrd)) {
788 dev_err(&tsdev->spi->dev, "kthread_run failed\n");
789 err = PTR_ERR(tsdev->pendet_thrd);
790 goto mrstouch_err_free_mem;
791 }
792 mrstouch_debug("%s", "Driver initialized");
793 return 0;
794
795mrstouch_err_free_mem:
796 kfree(tsdev);
797 return err;
798}
799
800static int mrstouch_suspend(struct spi_device *spi, pm_message_t msg)
801{
802 mrstouch_debug("%s", __func__);
803 mrstouchdevp->suspended = 1;
804 return 0;
805}
806
807static int mrstouch_resume(struct spi_device *spi)
808{
809 mrstouch_debug("%s", __func__);
810 mrstouchdevp->suspended = 0;
811 return 0;
812}
813
814static int mrstouch_remove(struct spi_device *spi)
815{
816 mrstouch_debug("%s", __func__);
817 free_irq(mrstouchdevp->irq, mrstouchdevp);
818 input_unregister_device(mrstouchdevp->input);
819 input_free_device(mrstouchdevp->input);
820 kfree(mrstouchdevp);
821 if (mrstouchdevp->pendet_thrd)
822 kthread_stop(mrstouchdevp->pendet_thrd);
823 return 0;
824}
825
826static struct spi_driver mrstouch_driver = {
827 .driver = {
828 .name = "pmic_touch",
829 .bus = &spi_bus_type,
830 .owner = THIS_MODULE,
831 },
832 .probe = mrstouch_probe,
833 .suspend = mrstouch_suspend,
834 .resume = mrstouch_resume,
835 .remove = mrstouch_remove,
836};
837
838static int __init mrstouch_module_init(void)
839{
840 int err;
841
842 mrstouch_debug("%s", __func__);
843 err = spi_register_driver(&mrstouch_driver);
844 if (err) {
845 mrstouch_debug("%s(%d)", "SPI PENDET failed", err);
846 return -1;
847 }
848
849 return 0;
850}
851
852static void __exit mrstouch_module_exit(void)
853{
854 mrstouch_debug("%s", __func__);
855 spi_unregister_driver(&mrstouch_driver);
856 return;
857}
858
859module_init(mrstouch_module_init);
860module_exit(mrstouch_module_exit);
861
862MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
863MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
864MODULE_LICENSE("GPL");