sync hh.org
[hh.org.git] / arch / arm / mach-pxa / htcbeetles / htcbeetles_ts.c
blob589addae6969e0bead91a8600656b71ba1c242c3
1 /* Touch screen driver for the TI something-or-other
3 * Copyright © 2005 SDG Systems, LLC
5 * Based on code that was based on the SAMCOP driver.
6 * Copyright © 2003, 2004 Compaq Computer Corporation.
8 * Use consistent with the GNU GPL is permitted,
9 * provided that this copyright notice is
10 * preserved in its entirety in all copies and derived works.
12 * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
13 * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
14 * FITNESS FOR ANY PARTICULAR PURPOSE.
16 * Author: Keith Packard <keith.packard@hp.com>
17 * May 2003
19 * Updates:
21 * 2004-02-11 Michael Opdenacker Renamed names from samcop to shamcop,
22 * Goal:support HAMCOP and SAMCOP.
23 * 2004-02-14 Michael Opdenacker Temporary fix for device id handling
25 * 2005-02-18 Aric Blumer Converted basic structure to support hx4700
27 * 2005-06-07 Aric Blumer Added tssim device handling so we can
28 * hook in the fbvncserver.
31 #include <linux/module.h>
32 #include <linux/version.h>
33 #include <linux/config.h>
35 #include <linux/init.h>
36 #include <linux/fs.h>
37 #include <linux/cdev.h>
38 #include <linux/interrupt.h>
39 #include <linux/sched.h>
40 #include <linux/pm.h>
41 #include <linux/delay.h>
42 #include <linux/input.h>
43 #include <linux/platform_device.h>
44 #include <linux/battery.h>
46 #include <asm/arch/hardware.h>
47 #include <asm/irq.h>
48 #include <asm/mach/irq.h>
49 #include <asm/io.h>
50 #include <asm/semaphore.h>
52 #include <asm/arch/pxa-regs.h>
53 #include <asm/arch/htcbeetles-gpio.h>
54 #include <asm/arch/htcbeetles-asic.h>
56 #include <asm/hardware/ipaq-asic3.h>
57 #include <linux/soc/asic3_base.h>
59 extern struct platform_device htcbeetles_asic3;
61 enum touchscreen_state {
62 STATE_WAIT_FOR_TOUCH, /* Waiting for a PEN interrupt */
63 STATE_SAMPLING /* Actively sampling ADC */
66 struct touchscreen_data {
67 enum touchscreen_state state;
68 struct timer_list timer;
69 int irq;
70 struct input_dev *input;
73 static unsigned long poll_sample_time = 10; /* Sample every 10 milliseconds */
75 static struct touchscreen_data *ts_data;
77 #define TS_SAMPLES 5
79 module_param(poll_sample_time, ulong, 0644);
80 MODULE_PARM_DESC(poll_sample_time, "Poll sample time");
82 static inline void
83 report_touchpanel(struct touchscreen_data *ts, int pressure, int x, int y)
85 input_report_abs(ts->input, ABS_PRESSURE, pressure);
86 input_report_abs(ts->input, ABS_X, x);
87 input_report_abs(ts->input, ABS_Y, y);
88 input_sync(ts->input);
91 static struct work_struct serial_work;
93 static irqreturn_t
94 pen_isr(int irq, void *irq_desc, struct pt_regs *regs)
96 /* struct touchscreen_data *ts = dev_id->data; */
97 struct touchscreen_data *ts = ts_data;
100 if(irq == HTCBEETLES_IRQ(TOUCHPANEL_IRQ_N)) {
102 if (ts->state == STATE_WAIT_FOR_TOUCH) {
104 * There is ground bounce or noise or something going on here:
105 * when you write to the SSP port to get the X and Y values, it
106 * causes a TOUCHPANEL_IRQ_N interrupt to occur. So if that
107 * happens, we can check to make sure the pen is actually down and
108 * disregard the interrupt if it's not.
110 if(GET_HTCBEETLES_GPIO(TOUCHPANEL_IRQ_N) == 0) {
112 * Disable the pen interrupt. It's reenabled when the user lifts the
113 * pen.
115 disable_irq(HTCBEETLES_IRQ(TOUCHPANEL_IRQ_N));
117 ts->state = STATE_SAMPLING;
118 schedule_work(&serial_work);
120 } else {
121 /* Shouldn't happen */
122 printk(KERN_ERR "Unexpected ts interrupt\n");
126 return IRQ_HANDLED;
129 static void
130 ssp_init(void)
133 pxa_set_cken(CKEN3_SSP2, 1);
135 /* *** Set up the SPI Registers *** */
136 SSCR0_P2 =
137 (1 << 20) /* Extended Data Size Select */
138 | (6 << 8) /* Serial Clock Rate */
139 | (0 << 7) /* Synchronous Serial Enable (Disable for now) */
140 | (0 << 4) /* Motorola SPI Interface */
141 | (7 << 0) /* Data Size Select (24-bit) */
143 SSCR1_P2 = 0;
144 SSPSP_P2 = 0;
146 /* Clear the Status */
147 SSSR_P2 = SSSR_P2 & 0x00fcfffc;
149 /* Now enable it */
150 SSCR0_P2 =
151 (1 << 20) /* Extended Data Size Select */
152 | (6 << 8) /* Serial Clock Rate */
153 | (1 << 7) /* Synchronous Serial Enable */
154 | (0 << 4) /* Motorola SPI Interface */
155 | (7 << 0) /* Data Size Select (24-bit) */
157 /* enable_irq(HTCBEETLES_IRQ(TOUCHPANEL_IRQ_N)); */
160 DECLARE_MUTEX(serial_mutex);
162 static void
163 start_read(void *in)
165 struct touchscreen_data *touch = in;
166 unsigned long inc = (poll_sample_time * HZ) / 1000;
167 int i;
169 down(&serial_mutex);
171 /* Write here to the serial port.
172 * Then we have to wait for poll_sample_time before we read out the serial
173 * port. Then, when we read it out, we check to see if the pen is still
174 * down. If so, then we issue another request here.
177 for(i = 0; i < TS_SAMPLES; i++) {
178 while(!(SSSR_P2 & (1 << 2)))
180 /* It's not full. Write the command for X */
181 SSDR_P2 = 0xd30000;
182 while(!(SSSR_P2 & (1 << 2)))
184 /* It's not full. Write the command for Y */
185 SSDR_P2 = 0x930000;
189 * Enable the timer. We should get an interrupt, but we want keep a timer
190 * to ensure that we can detect missing data
192 mod_timer(&touch->timer, jiffies + inc);
195 static int
196 do_delta_calc(int x1, int y1, int x2, int y2, int x3, int y3)
198 /* This is based on Jamey Hicks' description on IRC. */
199 int dx2_a, dy2_a, dx2_b, dy2_b;
201 dx2_a = x2 - x1;
202 dx2_a = dx2_a * dx2_a; /* If dx2_a was negative, it's not now */
203 dy2_a = y2 - y1;
204 dy2_a = dy2_a * dy2_a; /* If dy2_a was negative, it's not now */
206 dx2_b = x3 - x2;
207 dx2_b = dx2_b * dx2_b; /* If dx2_b was negative, it's not now */
208 dy2_b = y3 - y2;
209 dy2_b = dy2_b * dy2_b; /* If dy2_b was negative, it's not now */
211 #if 0
212 /* This was described in the algorithm by Jamey, but it doesn't do much
213 * good.
215 if(dx2_a + dy2_a < dx2_b + dy2_b) return 0;
216 #endif
218 /* dx2_a + dy2_a is the distance squared */
220 ((dx2_a + dy2_a) > 8000)
221 || ((dx2_b + dy2_b) > 8000)
223 return 0;
224 } else {
225 return 1;
228 if((dx2_b + dy2_b) > 5000) {
229 return 0;
230 } else {
231 return 1;
235 static void
236 ts_timer_callback(unsigned long data)
238 struct touchscreen_data *ts = (struct touchscreen_data *)data;
239 int x, y;
240 int ssrval;
243 * Check here to see if there is anything in the SPI FIFO. If so,
244 * return it if there has been a change. If not, then we have a
245 * timeout. Generate an erro somehow.
247 ssrval = SSSR_P2;
248 if(ssrval & (1 << 3)) { /* Look at Rx Not Empty bit */
249 int number_of_entries_in_fifo;
251 /* The FIFO is not emtpy. Good! Now make sure there are at least two
252 * entries. */
254 number_of_entries_in_fifo = ((ssrval >> 12) & 0xf) + 1;
256 if(number_of_entries_in_fifo < (TS_SAMPLES * 2)) {
257 /* Not ready yet. Come back later. */
258 unsigned long inc = (poll_sample_time * HZ) / 1000;
259 mod_timer(&ts->timer, jiffies + inc);
260 return;
263 if(number_of_entries_in_fifo == TS_SAMPLES * 2) {
264 int i, result, keep;
265 int X[TS_SAMPLES], Y[TS_SAMPLES];
267 for(i = 0; i < TS_SAMPLES; i++) {
268 X[i] = SSDR_P2;
269 Y[i] = SSDR_P2;
272 up(&serial_mutex);
274 keep = 0;
275 x = y = 0;
277 result = 0;
278 if(do_delta_calc(X[0], Y[0], X[1], Y[1], X[2], Y[2])) {
279 result |= (1 << 2);
281 if(do_delta_calc(X[1], Y[1], X[2], Y[2], X[3], Y[3])) {
282 result |= (1 << 1);
284 if(do_delta_calc(X[2], Y[2], X[3], Y[3], X[4], Y[4])) {
285 result |= (1 << 0);
287 switch(result) {
288 case 0:
289 /* Take average of point 0 and point 3 */
290 X[2] = (X[1] + X[3]) / 2;
291 Y[2] = (Y[1] + Y[3]) / 2;
292 /* don't keep */
293 break;
294 case 1:
295 /* Just ignore this one */
296 break;
297 case 2:
298 case 3:
299 case 6:
300 /* keep this sample */
301 x = (X[1] + (2 * X[2]) + X[3]) / 4;
302 y = (Y[1] + (2 * Y[2]) + Y[3]) / 4;
303 keep = 1;
304 break;
305 case 4:
306 X[1] = (X[0] + X[2]) / 2;
307 Y[1] = (Y[0] + Y[2]) / 2;
308 /* don't keep */
309 break;
310 case 5:
311 case 7:
312 x = (X[0] + (4 * X[1]) + (6 * X[2]) + (4 * X[3]) + X[4]) >> 4;
313 y = (Y[0] + (4 * Y[1]) + (6 * Y[2]) + (4 * Y[3]) + Y[4]) >> 4;
314 keep = 1;
315 break;
318 if(GET_HTCBEETLES_GPIO(TOUCHPANEL_IRQ_N) == 0) {
319 /* Still down */
320 if(keep) {
321 report_touchpanel(ts, 1, x, y);
323 start_read(ts);
324 } else {
325 /* Up */
326 report_touchpanel(ts, 0, 0, 0);
327 ts->state = STATE_WAIT_FOR_TOUCH;
328 /* Re-enable pen down interrupt */
329 enable_irq(HTCBEETLES_IRQ(TOUCHPANEL_IRQ_N));
332 } else {
333 /* We have an error! Too many entries. */
334 printk(KERN_ERR "TS: Expected %d entries. Got %d\n", 2, number_of_entries_in_fifo);
335 /* Try to clear the FIFO */
336 while(number_of_entries_in_fifo--) {
337 (void)SSDR_P2;
339 up(&serial_mutex);
340 if(GET_HTCBEETLES_GPIO(TOUCHPANEL_IRQ_N) == 0) {
341 start_read(ts);
343 return;
345 } else {
346 /* Not ready yet. Come back later. */
347 unsigned long inc = (poll_sample_time * HZ) / 1000;
348 mod_timer(&ts->timer, jiffies + inc);
349 return;
353 static int
354 ts_probe (struct device *dev)
356 int retval;
357 struct touchscreen_data *ts;
359 ts = ts_data = kmalloc (sizeof (*ts), GFP_KERNEL);
360 if (ts == NULL) {
361 printk( KERN_NOTICE "htcbeetles_ts: unable to allocate memory\n" );
362 return -ENOMEM;
364 memset (ts, 0, sizeof (*ts));
367 /* *** Set up the input subsystem stuff *** */
368 // memset(ts->input, 0, sizeof(struct input_dev));
369 ts->input = input_allocate_device();
370 if (ts->input == NULL) {
371 printk( KERN_NOTICE "htcbeetles_ts: unable to allocation touchscreen input\n" );
372 kfree(ts);
373 return -ENOMEM;
375 ts->input->evbit[0] = BIT(EV_ABS);
376 ts->input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
377 ts->input->absmin[ABS_X] = 0;
378 ts->input->absmax[ABS_X] = 32767;
379 ts->input->absmin[ABS_Y] = 0;
380 ts->input->absmax[ABS_Y] = 32767;
381 ts->input->absmin[ABS_PRESSURE] = 0;
382 ts->input->absmax[ABS_PRESSURE] = 1;
384 ts->input->name = "htcbeetles_ts";
385 ts->input->private = ts;
387 input_register_device(ts->input);
389 ts->timer.function = ts_timer_callback;
390 ts->timer.data = (unsigned long)ts;
391 ts->state = STATE_WAIT_FOR_TOUCH;
392 init_timer (&ts->timer);
394 INIT_WORK(&serial_work, start_read, ts);
396 dev_set_drvdata(dev, ts);
398 /* *** Initialize the SSP interface *** */
399 ssp_init();
401 down(&serial_mutex);
402 /* Make sure the device is in such a state that it can give pen
403 * interrupts. */
404 while(!(SSSR_P2 & (1 << 2)))
406 SSDR_P2 = 0xd30000;
408 for(retval = 0; retval < 100; retval++) {
409 if(SSSR_P2 & (1 << 3)) {
410 while(SSSR_P2 & (1 << 3)) {
411 (void)SSDR_P2;
413 break;
415 mdelay(1);
417 up(&serial_mutex);
420 ts->irq = HTCBEETLES_IRQ( TOUCHPANEL_IRQ_N );
421 retval = request_irq(ts->irq, pen_isr, SA_INTERRUPT, "htcbeetles_ts", ts);
422 if(retval) {
423 printk("Unable to get interrupt\n");
424 input_unregister_device (ts->input);
425 return -ENODEV;
427 set_irq_type(ts->irq, IRQT_FALLING);
429 return 0;
432 static int
433 ts_remove (struct device *dev)
435 struct touchscreen_data *ts = dev_get_drvdata(dev);
437 input_unregister_device (ts->input);
438 del_timer_sync (&ts->timer);
439 free_irq (ts->irq, ts);
441 kfree(ts);
442 pxa_set_cken(CKEN3_SSP2, 0);
443 return 0;
446 static int
447 ts_suspend (struct device *dev, pm_message_t state)
449 disable_irq(HTCBEETLES_IRQ(TOUCHPANEL_IRQ_N));
450 return 0;
453 static int
454 ts_resume (struct device *dev)
456 struct touchscreen_data *ts = dev_get_drvdata(dev);
458 ts->state = STATE_WAIT_FOR_TOUCH;
459 ssp_init();
460 enable_irq(HTCBEETLES_IRQ(TOUCHPANEL_IRQ_N));
462 return 0;
465 static struct device_driver ts_driver = {
466 .name = "htcbeetles-ts",
467 .bus = &platform_bus_type,
468 .probe = ts_probe,
469 .remove = ts_remove,
470 .suspend = ts_suspend,
471 .resume = ts_resume
474 static int tssim_init(void);
476 static int
477 ts_module_init (void)
479 printk(KERN_NOTICE "htcbeetles Touch Screen Driver\n");
480 if(tssim_init()) {
481 printk(KERN_NOTICE " TS Simulator Not Installed\n");
482 } else {
483 printk(KERN_NOTICE " TS Simulator Installed\n");
485 return driver_register(&ts_driver);
488 static void tssim_exit(void);
490 static void
491 ts_module_cleanup (void)
493 tssim_exit();
494 driver_unregister (&ts_driver);
497 /************* Code for Touch Screen Simulation for FBVNC Server **********/
498 static dev_t dev;
499 static struct cdev *cdev;
501 static long a0 = -1122, a2 = 33588528, a4 = 1452, a5 = -2970720, a6 = 65536;
503 /* The input into the input subsystem is prior to correction from calibration.
504 * So we have to undo the effects of the calibration. It's actually a
505 * complicated equation where the calibrated value of X depends on the
506 * uncalibrated values of X and Y. Fortunately, at least on the hx4700, the
507 * multiplier for the Y value is zero, so I assume that here. It is a shame
508 * that the tslib does not allow multiple inputs. Then we could do another
509 * driver for this (as it was originally) that give input that does not
510 * require calibration.
512 static int
513 tssim_ioctl(struct inode *inode, struct file *fp, unsigned int ioctlnum, unsigned long parm)
515 switch(ioctlnum) {
516 case 0: a0 = parm; break;
517 case 1: break;
518 case 2: a2 = parm; break;
519 case 3: break;
520 case 4: a4 = parm; break;
521 case 5: a5 = parm; break;
522 case 6:
523 a6 = parm;
524 printk(KERN_DEBUG "a0 = %ld, a2 = %ld, a4 = %ld, a5 = %ld, a6 = %ld\n",
525 a0, a2, a4, a5, a6);
526 break;
527 default: return -ENOTTY;
529 return 0;
532 static int
533 tssim_open(struct inode *inode, struct file *fp)
535 /* Nothing to do here */
536 return 0;
539 static ssize_t
540 tssim_write(struct file *fp, const char __user *data, size_t bytes, loff_t *offset)
542 unsigned long pressure;
543 long y;
544 long x;
546 x = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); data += 4;
547 y = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); data += 4;
548 pressure = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); data += 4;
550 input_report_abs(ts_data->input, ABS_PRESSURE, pressure?1:0);
551 input_report_abs(ts_data->input, ABS_X, ((x * a6) - a2)/a0);
552 input_report_abs(ts_data->input, ABS_Y, ((y * a6) - a5)/a4);
553 input_sync(ts_data->input);
555 return bytes;
558 int tssim_close(struct inode *inode, struct file *fp)
560 return 0;
563 struct file_operations fops = {
564 THIS_MODULE,
565 .write = tssim_write,
566 .open = tssim_open,
567 .release = tssim_close,
568 .ioctl = tssim_ioctl,
571 static int battery_class;
573 static int get_min_voltage(struct battery *b)
575 return 1000;
577 static int get_max_voltage(struct battery *b)
579 return 1400; /* mV */
581 static int get_max_charge(struct battery *b)
583 return 100;
585 static int get_voltage(struct battery *b)
587 static int battery_sample;
589 if(!down_interruptible(&serial_mutex)) {
590 int i;
591 int ssrval;
593 while(!(SSSR_P2 & (1 << 2)))
595 SSDR_P2 = 0xe70000;
596 while(!(SSSR_P2 & (1 << 2)))
598 SSDR_P2 = 0xe70000;
599 while(!(SSSR_P2 & (1 << 2)))
601 SSDR_P2 = 0xd30000; /* Dummy command to allow pen interrupts again */
603 for(i = 0; i < 10; i++) {
604 ssrval = SSSR_P2;
605 if(ssrval & (1 << 3)) { /* Look at Rx Not Empty bit */
606 int number_of_entries_in_fifo;
608 number_of_entries_in_fifo = ((ssrval >> 12) & 0xf) + 1;
609 if(number_of_entries_in_fifo == 3) {
610 break;
613 msleep(1);
616 if(i < 1000) {
617 (void) SSDR_P2;
618 battery_sample = SSDR_P2 & 0xfff;
619 (void) SSDR_P2;
620 } else {
621 /* Make sure the FIFO is empty */
622 while(SSSR_P2 & (1 << 3)) {
623 (void) SSDR_P2;
625 battery_sample = -1;
627 up(&serial_mutex);
630 return battery_sample;
632 static int get_charge(struct battery *b)
634 return 100;
636 static int get_status(struct battery *b)
638 return 1;
641 static struct battery htcbeetles_power = {
642 .name = "htcbeetles_backup",
643 .id = "backup",
644 .get_min_voltage = get_min_voltage,
645 .get_min_current = 0,
646 .get_min_charge = 0,
647 .get_max_voltage = get_max_voltage,
648 .get_max_current = 0,
649 .get_max_charge = get_max_charge,
650 .get_temp = 0,
651 .get_voltage = get_voltage,
652 .get_current = 0,
653 .get_charge = get_charge,
654 .get_status = get_status,
657 static int
658 battery_class_uevent(struct class_device *dev, char **envp, int num_envp,
659 char *buffer, int buffer_size)
661 return 0;
664 static void
665 battery_class_release(struct class_device *dev)
669 static void
670 battery_class_class_release(struct class *class)
674 static int
675 tssim_init(void)
677 int retval;
679 retval = alloc_chrdev_region(&dev, 0, 1, "tssim");
680 if(retval) {
681 printk(KERN_ERR "TSSIM Unable to allocate device numbers\n");
682 return retval;
685 cdev = cdev_alloc();
686 cdev->owner = THIS_MODULE;
687 cdev->ops = &fops;
688 retval = cdev_add(cdev, dev, 1);
689 if(retval) {
690 printk(KERN_ERR "Unable to add cdev\n");
691 unregister_chrdev_region(dev, 1);
692 return retval;
695 battery_class = 0;
696 if(battery_class_register(&htcbeetles_power)) {
697 printk(KERN_ERR "htcbeetles_ts: Could not register battery class\n");
698 } else {
699 battery_class = 1;
700 htcbeetles_power.class_dev.class->uevent = battery_class_uevent;
701 htcbeetles_power.class_dev.class->release = battery_class_release;
702 htcbeetles_power.class_dev.class->class_release = battery_class_class_release;
705 return 0;
708 static void
709 tssim_exit(void)
711 cdev_del(cdev);
712 unregister_chrdev_region(dev, 1);
713 if(battery_class) {
714 battery_class_unregister(&htcbeetles_power);
716 return;
719 module_init(ts_module_init);
720 module_exit(ts_module_cleanup);
722 MODULE_LICENSE("GPL");
723 MODULE_AUTHOR("Aric Blumer, SDG Systems, LLC");
724 MODULE_DESCRIPTION("htcbeetles Touch Screen Driver");