summaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen/hp680_ts_input.c
blob: 61c15024c2a0e0ef87738239028500338cd185cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>

#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/adc.h>
#include <asm/hp6xx.h>

#define MODNAME "hp680_ts_input"

#define HP680_TS_ABS_X_MIN	40
#define HP680_TS_ABS_X_MAX	950
#define HP680_TS_ABS_Y_MIN	80
#define HP680_TS_ABS_Y_MAX	910

#define	PHDR	0xa400012e
#define SCPDR	0xa4000136

static void do_softint(void *data);

static struct input_dev *hp680_ts_dev;
static DECLARE_WORK(work, do_softint);

static void do_softint(void *data)
{
	int absx = 0, absy = 0;
	u8 scpdr;
	int touched = 0;

	if (ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN) {
		scpdr = ctrl_inb(SCPDR);
		scpdr |= SCPDR_TS_SCAN_ENABLE;
		scpdr &= ~SCPDR_TS_SCAN_Y;
		ctrl_outb(scpdr, SCPDR);
		udelay(30);

		absy = adc_single(ADC_CHANNEL_TS_Y);

		scpdr = ctrl_inb(SCPDR);
		scpdr |= SCPDR_TS_SCAN_Y;
		scpdr &= ~SCPDR_TS_SCAN_X;
		ctrl_outb(scpdr, SCPDR);
		udelay(30);

		absx = adc_single(ADC_CHANNEL_TS_X);

		scpdr = ctrl_inb(SCPDR);
		scpdr |= SCPDR_TS_SCAN_X;
		scpdr &= ~SCPDR_TS_SCAN_ENABLE;
		ctrl_outb(scpdr, SCPDR);
		udelay(100);
		touched = ctrl_inb(PHDR) & PHDR_TS_PEN_DOWN;
	}

	if (touched) {
		input_report_key(hp680_ts_dev, BTN_TOUCH, 1);
		input_report_abs(hp680_ts_dev, ABS_X, absx);
		input_report_abs(hp680_ts_dev, ABS_Y, absy);
	} else {
		input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
	}

	input_sync(hp680_ts_dev);
	enable_irq(HP680_TS_IRQ);
}

static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
{
	disable_irq_nosync(irq);
	schedule_delayed_work(&work, HZ / 20);

	return IRQ_HANDLED;
}

static int __init hp680_ts_init(void)
{
	int err;

	hp680_ts_dev = input_allocate_device();
	if (!hp680_ts_dev)
		return -ENOMEM;

	hp680_ts_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
	hp680_ts_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);

	input_set_abs_params(hp680_ts_dev, ABS_X,
		HP680_TS_ABS_X_MIN, HP680_TS_ABS_X_MAX, 0, 0);
	input_set_abs_params(hp680_ts_dev, ABS_Y,
		HP680_TS_ABS_Y_MIN, HP680_TS_ABS_Y_MAX, 0, 0);

	hp680_ts_dev->name = "HP Jornada touchscreen";
	hp680_ts_dev->phys = "hp680_ts/input0";

	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
			IRQF_DISABLED, MODNAME, 0) < 0) {
		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
		       HP680_TS_IRQ);
		err = -EBUSY;
		goto fail1;
	}

	err = input_register_device(hp680_ts_dev);
	if (err)
		goto fail2;

	return 0;

 fail2:	free_irq(HP680_TS_IRQ, NULL);
	cancel_delayed_work(&work);
	flush_scheduled_work();
 fail1:	input_free_device(hp680_ts_dev);
	return err;
}

static void __exit hp680_ts_exit(void)
{
	free_irq(HP680_TS_IRQ, NULL);
	cancel_delayed_work(&work);
	flush_scheduled_work();
	input_unregister_device(hp680_ts_dev);
}

module_init(hp680_ts_init);
module_exit(hp680_ts_exit);

MODULE_AUTHOR("Andriy Skulysh, askulysh@image.kiev.ua");
MODULE_DESCRIPTION("HP Jornada 680 touchscreen driver");
MODULE_LICENSE("GPL");