18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci#include <linux/input.h> 38c2ecf20Sopenharmony_ci#include <linux/module.h> 48c2ecf20Sopenharmony_ci#include <linux/init.h> 58c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 68c2ecf20Sopenharmony_ci#include <asm/io.h> 78c2ecf20Sopenharmony_ci#include <asm/delay.h> 88c2ecf20Sopenharmony_ci#include <asm/adc.h> 98c2ecf20Sopenharmony_ci#include <mach/hp6xx.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define MODNAME "hp680_ts_input" 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define HP680_TS_ABS_X_MIN 40 148c2ecf20Sopenharmony_ci#define HP680_TS_ABS_X_MAX 950 158c2ecf20Sopenharmony_ci#define HP680_TS_ABS_Y_MIN 80 168c2ecf20Sopenharmony_ci#define HP680_TS_ABS_Y_MAX 910 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PHDR 0xa400012e 198c2ecf20Sopenharmony_ci#define SCPDR 0xa4000136 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistatic void do_softint(struct work_struct *work); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic struct input_dev *hp680_ts_dev; 248c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(work, do_softint); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void do_softint(struct work_struct *work) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci int absx = 0, absy = 0; 298c2ecf20Sopenharmony_ci u8 scpdr; 308c2ecf20Sopenharmony_ci int touched = 0; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) { 338c2ecf20Sopenharmony_ci scpdr = __raw_readb(SCPDR); 348c2ecf20Sopenharmony_ci scpdr |= SCPDR_TS_SCAN_ENABLE; 358c2ecf20Sopenharmony_ci scpdr &= ~SCPDR_TS_SCAN_Y; 368c2ecf20Sopenharmony_ci __raw_writeb(scpdr, SCPDR); 378c2ecf20Sopenharmony_ci udelay(30); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci absy = adc_single(ADC_CHANNEL_TS_Y); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci scpdr = __raw_readb(SCPDR); 428c2ecf20Sopenharmony_ci scpdr |= SCPDR_TS_SCAN_Y; 438c2ecf20Sopenharmony_ci scpdr &= ~SCPDR_TS_SCAN_X; 448c2ecf20Sopenharmony_ci __raw_writeb(scpdr, SCPDR); 458c2ecf20Sopenharmony_ci udelay(30); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci absx = adc_single(ADC_CHANNEL_TS_X); 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci scpdr = __raw_readb(SCPDR); 508c2ecf20Sopenharmony_ci scpdr |= SCPDR_TS_SCAN_X; 518c2ecf20Sopenharmony_ci scpdr &= ~SCPDR_TS_SCAN_ENABLE; 528c2ecf20Sopenharmony_ci __raw_writeb(scpdr, SCPDR); 538c2ecf20Sopenharmony_ci udelay(100); 548c2ecf20Sopenharmony_ci touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (touched) { 588c2ecf20Sopenharmony_ci input_report_key(hp680_ts_dev, BTN_TOUCH, 1); 598c2ecf20Sopenharmony_ci input_report_abs(hp680_ts_dev, ABS_X, absx); 608c2ecf20Sopenharmony_ci input_report_abs(hp680_ts_dev, ABS_Y, absy); 618c2ecf20Sopenharmony_ci } else { 628c2ecf20Sopenharmony_ci input_report_key(hp680_ts_dev, BTN_TOUCH, 0); 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci input_sync(hp680_ts_dev); 668c2ecf20Sopenharmony_ci enable_irq(HP680_TS_IRQ); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic irqreturn_t hp680_ts_interrupt(int irq, void *dev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci disable_irq_nosync(irq); 728c2ecf20Sopenharmony_ci schedule_delayed_work(&work, HZ / 20); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int __init hp680_ts_init(void) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci int err; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci hp680_ts_dev = input_allocate_device(); 828c2ecf20Sopenharmony_ci if (!hp680_ts_dev) 838c2ecf20Sopenharmony_ci return -ENOMEM; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); 868c2ecf20Sopenharmony_ci hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci input_set_abs_params(hp680_ts_dev, ABS_X, 898c2ecf20Sopenharmony_ci HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0); 908c2ecf20Sopenharmony_ci input_set_abs_params(hp680_ts_dev, ABS_Y, 918c2ecf20Sopenharmony_ci HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci hp680_ts_dev->name = "HP Jornada touchscreen"; 948c2ecf20Sopenharmony_ci hp680_ts_dev->phys = "hp680_ts/input0"; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt, 978c2ecf20Sopenharmony_ci 0, MODNAME, NULL) < 0) { 988c2ecf20Sopenharmony_ci printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n", 998c2ecf20Sopenharmony_ci HP680_TS_IRQ); 1008c2ecf20Sopenharmony_ci err = -EBUSY; 1018c2ecf20Sopenharmony_ci goto fail1; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci err = input_register_device(hp680_ts_dev); 1058c2ecf20Sopenharmony_ci if (err) 1068c2ecf20Sopenharmony_ci goto fail2; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci fail2: free_irq(HP680_TS_IRQ, NULL); 1118c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&work); 1128c2ecf20Sopenharmony_ci fail1: input_free_device(hp680_ts_dev); 1138c2ecf20Sopenharmony_ci return err; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic void __exit hp680_ts_exit(void) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci free_irq(HP680_TS_IRQ, NULL); 1198c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&work); 1208c2ecf20Sopenharmony_ci input_unregister_device(hp680_ts_dev); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cimodule_init(hp680_ts_init); 1248c2ecf20Sopenharmony_cimodule_exit(hp680_ts_exit); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ciMODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua"); 1278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("HP Jornada 680 touchscreen driver"); 1288c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 129