11da177e4SLinus Torvalds #include <linux/input.h> 21da177e4SLinus Torvalds #include <linux/module.h> 31da177e4SLinus Torvalds #include <linux/init.h> 41da177e4SLinus Torvalds #include <linux/interrupt.h> 51da177e4SLinus Torvalds #include <asm/io.h> 61da177e4SLinus Torvalds #include <asm/delay.h> 71da177e4SLinus Torvalds #include <asm/adc.h> 8082c44d2SPaul Mundt #include <asm/hp6xx.h> 91da177e4SLinus Torvalds 101da177e4SLinus Torvalds #define MODNAME "hp680_ts_input" 111da177e4SLinus Torvalds 121da177e4SLinus Torvalds #define HP680_TS_ABS_X_MIN 40 131da177e4SLinus Torvalds #define HP680_TS_ABS_X_MAX 950 141da177e4SLinus Torvalds #define HP680_TS_ABS_Y_MIN 80 151da177e4SLinus Torvalds #define HP680_TS_ABS_Y_MAX 910 161da177e4SLinus Torvalds 171da177e4SLinus Torvalds #define PHDR 0xa400012e 181da177e4SLinus Torvalds #define SCPDR 0xa4000136 191da177e4SLinus Torvalds 20*049fa57cSKristoffer Ericson static void do_softint(struct work_struct *work); 211da177e4SLinus Torvalds 22eca1ed19SDmitry Torokhov static struct input_dev *hp680_ts_dev; 23*049fa57cSKristoffer Ericson static DECLARE_DELAYED_WORK(work, do_softint); 241da177e4SLinus Torvalds 25*049fa57cSKristoffer Ericson static void do_softint(struct work_struct *work) 261da177e4SLinus Torvalds { 271da177e4SLinus Torvalds int absx = 0, absy = 0; 281da177e4SLinus Torvalds u8 scpdr; 291da177e4SLinus Torvalds int touched = 0; 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds if (ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN) { 321da177e4SLinus Torvalds scpdr = ctrl_inb(SCPDR); 331da177e4SLinus Torvalds scpdr |= SCPDR_TS_SCAN_ENABLE; 341da177e4SLinus Torvalds scpdr &= ~SCPDR_TS_SCAN_Y; 351da177e4SLinus Torvalds ctrl_outb(scpdr, SCPDR); 361da177e4SLinus Torvalds udelay(30); 371da177e4SLinus Torvalds 381da177e4SLinus Torvalds absy = adc_single(ADC_CHANNEL_TS_Y); 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds scpdr = ctrl_inb(SCPDR); 411da177e4SLinus Torvalds scpdr |= SCPDR_TS_SCAN_Y; 421da177e4SLinus Torvalds scpdr &= ~SCPDR_TS_SCAN_X; 431da177e4SLinus Torvalds ctrl_outb(scpdr, SCPDR); 441da177e4SLinus Torvalds udelay(30); 451da177e4SLinus Torvalds 461da177e4SLinus Torvalds absx = adc_single(ADC_CHANNEL_TS_X); 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds scpdr = ctrl_inb(SCPDR); 491da177e4SLinus Torvalds scpdr |= SCPDR_TS_SCAN_X; 501da177e4SLinus Torvalds scpdr &= ~SCPDR_TS_SCAN_ENABLE; 511da177e4SLinus Torvalds ctrl_outb(scpdr, SCPDR); 521da177e4SLinus Torvalds udelay(100); 531da177e4SLinus Torvalds touched = ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN; 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds if (touched) { 57eca1ed19SDmitry Torokhov input_report_key(hp680_ts_dev, BTN_TOUCH, 1); 58eca1ed19SDmitry Torokhov input_report_abs(hp680_ts_dev, ABS_X, absx); 59eca1ed19SDmitry Torokhov input_report_abs(hp680_ts_dev, ABS_Y, absy); 601da177e4SLinus Torvalds } else { 61eca1ed19SDmitry Torokhov input_report_key(hp680_ts_dev, BTN_TOUCH, 0); 621da177e4SLinus Torvalds } 631da177e4SLinus Torvalds 64eca1ed19SDmitry Torokhov input_sync(hp680_ts_dev); 651da177e4SLinus Torvalds enable_irq(HP680_TS_IRQ); 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds 687d12e780SDavid Howells static irqreturn_t hp680_ts_interrupt(int irq, void *dev) 691da177e4SLinus Torvalds { 701da177e4SLinus Torvalds disable_irq_nosync(irq); 711da177e4SLinus Torvalds schedule_delayed_work(&work, HZ / 20); 721da177e4SLinus Torvalds 731da177e4SLinus Torvalds return IRQ_HANDLED; 741da177e4SLinus Torvalds } 751da177e4SLinus Torvalds 761da177e4SLinus Torvalds static int __init hp680_ts_init(void) 771da177e4SLinus Torvalds { 7852c1f570SDmitry Torokhov int err; 7952c1f570SDmitry Torokhov 80eca1ed19SDmitry Torokhov hp680_ts_dev = input_allocate_device(); 81eca1ed19SDmitry Torokhov if (!hp680_ts_dev) 82eca1ed19SDmitry Torokhov return -ENOMEM; 831da177e4SLinus Torvalds 84eca1ed19SDmitry Torokhov hp680_ts_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); 85eca1ed19SDmitry Torokhov hp680_ts_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); 861da177e4SLinus Torvalds 8752c1f570SDmitry Torokhov input_set_abs_params(hp680_ts_dev, ABS_X, 8852c1f570SDmitry Torokhov HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0); 8952c1f570SDmitry Torokhov input_set_abs_params(hp680_ts_dev, ABS_Y, 9052c1f570SDmitry Torokhov HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0); 911da177e4SLinus Torvalds 92eca1ed19SDmitry Torokhov hp680_ts_dev->name = "HP Jornada touchscreen"; 93eca1ed19SDmitry Torokhov hp680_ts_dev->phys = "hp680_ts/input0"; 941da177e4SLinus Torvalds 95eca1ed19SDmitry Torokhov if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, 96dace1453SThomas Gleixner IRQF_DISABLED, MODNAME, 0) < 0) { 971da177e4SLinus Torvalds printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n", 981da177e4SLinus Torvalds HP680_TS_IRQ); 9952c1f570SDmitry Torokhov err = -EBUSY; 10052c1f570SDmitry Torokhov goto fail1; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 10352c1f570SDmitry Torokhov err = input_register_device(hp680_ts_dev); 10452c1f570SDmitry Torokhov if (err) 10552c1f570SDmitry Torokhov goto fail2; 10652c1f570SDmitry Torokhov 1071da177e4SLinus Torvalds return 0; 10852c1f570SDmitry Torokhov 10952c1f570SDmitry Torokhov fail2: free_irq(HP680_TS_IRQ, NULL); 11052c1f570SDmitry Torokhov cancel_delayed_work(&work); 11152c1f570SDmitry Torokhov flush_scheduled_work(); 11252c1f570SDmitry Torokhov fail1: input_free_device(hp680_ts_dev); 11352c1f570SDmitry Torokhov return err; 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static void __exit hp680_ts_exit(void) 1171da177e4SLinus Torvalds { 11852c1f570SDmitry Torokhov free_irq(HP680_TS_IRQ, NULL); 1191da177e4SLinus Torvalds cancel_delayed_work(&work); 1201da177e4SLinus Torvalds flush_scheduled_work(); 121eca1ed19SDmitry Torokhov input_unregister_device(hp680_ts_dev); 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds module_init(hp680_ts_init); 1251da177e4SLinus Torvalds module_exit(hp680_ts_exit); 1261da177e4SLinus Torvalds 1271da177e4SLinus Torvalds MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); 1281da177e4SLinus Torvalds MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver"); 1291da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 130