162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/input.h>
362306a36Sopenharmony_ci#include <linux/module.h>
462306a36Sopenharmony_ci#include <linux/init.h>
562306a36Sopenharmony_ci#include <linux/interrupt.h>
662306a36Sopenharmony_ci#include <asm/io.h>
762306a36Sopenharmony_ci#include <asm/delay.h>
862306a36Sopenharmony_ci#include <asm/adc.h>
962306a36Sopenharmony_ci#include <mach/hp6xx.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define MODNAME "hp680_ts_input"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define HP680_TS_ABS_X_MIN	40
1462306a36Sopenharmony_ci#define HP680_TS_ABS_X_MAX	950
1562306a36Sopenharmony_ci#define HP680_TS_ABS_Y_MIN	80
1662306a36Sopenharmony_ci#define HP680_TS_ABS_Y_MAX	910
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define	PHDR	0xa400012e
1962306a36Sopenharmony_ci#define SCPDR	0xa4000136
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void do_softint(struct work_struct *work);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic struct input_dev *hp680_ts_dev;
2462306a36Sopenharmony_cistatic DECLARE_DELAYED_WORK(work, do_softint);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistatic void do_softint(struct work_struct *work)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	int absx = 0, absy = 0;
2962306a36Sopenharmony_ci	u8 scpdr;
3062306a36Sopenharmony_ci	int touched = 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (__raw_readb(PHDR) & PHDR_TS_PEN_DOWN) {
3362306a36Sopenharmony_ci		scpdr = __raw_readb(SCPDR);
3462306a36Sopenharmony_ci		scpdr |= SCPDR_TS_SCAN_ENABLE;
3562306a36Sopenharmony_ci		scpdr &= ~SCPDR_TS_SCAN_Y;
3662306a36Sopenharmony_ci		__raw_writeb(scpdr, SCPDR);
3762306a36Sopenharmony_ci		udelay(30);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci		absy = adc_single(ADC_CHANNEL_TS_Y);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		scpdr = __raw_readb(SCPDR);
4262306a36Sopenharmony_ci		scpdr |= SCPDR_TS_SCAN_Y;
4362306a36Sopenharmony_ci		scpdr &= ~SCPDR_TS_SCAN_X;
4462306a36Sopenharmony_ci		__raw_writeb(scpdr, SCPDR);
4562306a36Sopenharmony_ci		udelay(30);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci		absx = adc_single(ADC_CHANNEL_TS_X);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci		scpdr = __raw_readb(SCPDR);
5062306a36Sopenharmony_ci		scpdr |= SCPDR_TS_SCAN_X;
5162306a36Sopenharmony_ci		scpdr &= ~SCPDR_TS_SCAN_ENABLE;
5262306a36Sopenharmony_ci		__raw_writeb(scpdr, SCPDR);
5362306a36Sopenharmony_ci		udelay(100);
5462306a36Sopenharmony_ci		touched = __raw_readb(PHDR) & PHDR_TS_PEN_DOWN;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (touched) {
5862306a36Sopenharmony_ci		input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
5962306a36Sopenharmony_ci		input_report_abs(hp680_ts_dev, ABS_X, absx);
6062306a36Sopenharmony_ci		input_report_abs(hp680_ts_dev, ABS_Y, absy);
6162306a36Sopenharmony_ci	} else {
6262306a36Sopenharmony_ci		input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	input_sync(hp680_ts_dev);
6662306a36Sopenharmony_ci	enable_irq(HP680_TS_IRQ);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic irqreturn_t hp680_ts_interrupt(int irq, void *dev)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	disable_irq_nosync(irq);
7262306a36Sopenharmony_ci	schedule_delayed_work(&work, HZ / 20);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	return IRQ_HANDLED;
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int __init hp680_ts_init(void)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	int err;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	hp680_ts_dev = input_allocate_device();
8262306a36Sopenharmony_ci	if (!hp680_ts_dev)
8362306a36Sopenharmony_ci		return -ENOMEM;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
8662306a36Sopenharmony_ci	hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	input_set_abs_params(hp680_ts_dev, ABS_X,
8962306a36Sopenharmony_ci		HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
9062306a36Sopenharmony_ci	input_set_abs_params(hp680_ts_dev, ABS_Y,
9162306a36Sopenharmony_ci		HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	hp680_ts_dev->name = "HP Jornada touchscreen";
9462306a36Sopenharmony_ci	hp680_ts_dev->phys = "hp680_ts/input0";
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
9762306a36Sopenharmony_ci			0, MODNAME, NULL) < 0) {
9862306a36Sopenharmony_ci		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
9962306a36Sopenharmony_ci		       HP680_TS_IRQ);
10062306a36Sopenharmony_ci		err = -EBUSY;
10162306a36Sopenharmony_ci		goto fail1;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	err = input_register_device(hp680_ts_dev);
10562306a36Sopenharmony_ci	if (err)
10662306a36Sopenharmony_ci		goto fail2;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	return 0;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci fail2:	free_irq(HP680_TS_IRQ, NULL);
11162306a36Sopenharmony_ci	cancel_delayed_work_sync(&work);
11262306a36Sopenharmony_ci fail1:	input_free_device(hp680_ts_dev);
11362306a36Sopenharmony_ci	return err;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void __exit hp680_ts_exit(void)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	free_irq(HP680_TS_IRQ, NULL);
11962306a36Sopenharmony_ci	cancel_delayed_work_sync(&work);
12062306a36Sopenharmony_ci	input_unregister_device(hp680_ts_dev);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cimodule_init(hp680_ts_init);
12462306a36Sopenharmony_cimodule_exit(hp680_ts_exit);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciMODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
12762306a36Sopenharmony_ciMODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
12862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
129