1 // SPDX-License-Identifier: GPL-2.0-only 2 #include <linux/input.h> 3 #include <linux/module.h> 4 #include <linux/init.h> 5 #include <linux/interrupt.h> 6 #include <asm/io.h> 7 #include <asm/delay.h> 8 #include <asm/adc.h> 9 #include <mach/hp6xx.h> 10 11 #define MODNAME "hp680_ts_input" 12 13 #define HP680_TS_ABS_X_MIN 40 14 #define HP680_TS_ABS_X_MAX 950 15 #define HP680_TS_ABS_Y_MIN 80 16 #define HP680_TS_ABS_Y_MAX 910 17 18 #define PHDR 0xa400012e 19 #define SCPDR 0xa4000136 20 21 static void do_softint(struct work_struct *work); 22 23 static struct input_dev *hp680_ts_dev; 24 static DECLARE_DELAYED_WORK(work, do_softint); 25 26 static void do_softint(struct work_struct *work) 27 { 28 int absx = 0, absy = 0; 29 u8 scpdr; 30 int touched = 0; 31 32 if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) { 33 scpdr = __raw_readb(SCPDR); 34 scpdr |= SCPDR_TS_SCAN_ENABLE; 35 scpdr &= ~SCPDR_TS_SCAN_Y; 36 __raw_writeb(scpdr, SCPDR); 37 udelay(30); 38 39 absy = adc_single(ADC_CHANNEL_TS_Y); 40 41 scpdr = __raw_readb(SCPDR); 42 scpdr |= SCPDR_TS_SCAN_Y; 43 scpdr &= ~SCPDR_TS_SCAN_X; 44 __raw_writeb(scpdr, SCPDR); 45 udelay(30); 46 47 absx = adc_single(ADC_CHANNEL_TS_X); 48 49 scpdr = __raw_readb(SCPDR); 50 scpdr |= SCPDR_TS_SCAN_X; 51 scpdr &= ~SCPDR_TS_SCAN_ENABLE; 52 __raw_writeb(scpdr, SCPDR); 53 udelay(100); 54 touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN; 55 } 56 57 if (touched) { 58 input_report_key(hp680_ts_dev, BTN_TOUCH, 1); 59 input_report_abs(hp680_ts_dev, ABS_X, absx); 60 input_report_abs(hp680_ts_dev, ABS_Y, absy); 61 } else { 62 input_report_key(hp680_ts_dev, BTN_TOUCH, 0); 63 } 64 65 input_sync(hp680_ts_dev); 66 enable_irq(HP680_TS_IRQ); 67 } 68 69 static irqreturn_t hp680_ts_interrupt(int irq, void *dev) 70 { 71 disable_irq_nosync(irq); 72 schedule_delayed_work(&work, HZ / 20); 73 74 return IRQ_HANDLED; 75 } 76 77 static int __init hp680_ts_init(void) 78 { 79 int err; 80 81 hp680_ts_dev = input_allocate_device(); 82 if (!hp680_ts_dev) 83 return -ENOMEM; 84 85 hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); 86 hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 87 88 input_set_abs_params(hp680_ts_dev, ABS_X, 89 HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0); 90 input_set_abs_params(hp680_ts_dev, ABS_Y, 91 HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0); 92 93 hp680_ts_dev->name = "HP Jornada touchscreen"; 94 hp680_ts_dev->phys = "hp680_ts/input0"; 95 96 if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, 97 0, MODNAME, NULL) < 0) { 98 printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n", 99 HP680_TS_IRQ); 100 err = -EBUSY; 101 goto fail1; 102 } 103 104 err = input_register_device(hp680_ts_dev); 105 if (err) 106 goto fail2; 107 108 return 0; 109 110 fail2: free_irq(HP680_TS_IRQ, NULL); 111 cancel_delayed_work_sync(&work); 112 fail1: input_free_device(hp680_ts_dev); 113 return err; 114 } 115 116 static void __exit hp680_ts_exit(void) 117 { 118 free_irq(HP680_TS_IRQ, NULL); 119 cancel_delayed_work_sync(&work); 120 input_unregister_device(hp680_ts_dev); 121 } 122 123 module_init(hp680_ts_init); 124 module_exit(hp680_ts_exit); 125 126 MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); 127 MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver"); 128 MODULE_LICENSE("GPL"); 129