hp680_ts_input.c 2.9 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7
#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>
P
Paul Mundt 已提交
8
#include <asm/hp6xx.h>
L
Linus Torvalds 已提交
9 10 11 12 13 14 15 16 17 18 19

#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

20
static void do_softint(struct work_struct *work);
L
Linus Torvalds 已提交
21

22
static struct input_dev *hp680_ts_dev;
23
static DECLARE_DELAYED_WORK(work, do_softint);
L
Linus Torvalds 已提交
24

25
static void do_softint(struct work_struct *work)
L
Linus Torvalds 已提交
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
{
	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) {
57 58 59
		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);
L
Linus Torvalds 已提交
60
	} else {
61
		input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
L
Linus Torvalds 已提交
62 63
	}

64
	input_sync(hp680_ts_dev);
L
Linus Torvalds 已提交
65 66 67
	enable_irq(HP680_TS_IRQ);
}

68
static irqreturn_t hp680_ts_interrupt(int irq, void *dev)
L
Linus Torvalds 已提交
69 70 71 72 73 74 75 76 77
{
	disable_irq_nosync(irq);
	schedule_delayed_work(&work, HZ / 20);

	return IRQ_HANDLED;
}

static int __init hp680_ts_init(void)
{
78 79
	int err;

80 81 82
	hp680_ts_dev = input_allocate_device();
	if (!hp680_ts_dev)
		return -ENOMEM;
L
Linus Torvalds 已提交
83

84 85
	hp680_ts_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
	hp680_ts_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
L
Linus Torvalds 已提交
86

87 88 89 90
	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);
L
Linus Torvalds 已提交
91

92 93
	hp680_ts_dev->name = "HP Jornada touchscreen";
	hp680_ts_dev->phys = "hp680_ts/input0";
L
Linus Torvalds 已提交
94

95
	if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
96
			IRQF_DISABLED, MODNAME, 0) < 0) {
97
		printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
L
Linus Torvalds 已提交
98
		       HP680_TS_IRQ);
99 100
		err = -EBUSY;
		goto fail1;
L
Linus Torvalds 已提交
101 102
	}

103 104 105 106
	err = input_register_device(hp680_ts_dev);
	if (err)
		goto fail2;

L
Linus Torvalds 已提交
107
	return 0;
108 109 110 111 112 113

 fail2:	free_irq(HP680_TS_IRQ, NULL);
	cancel_delayed_work(&work);
	flush_scheduled_work();
 fail1:	input_free_device(hp680_ts_dev);
	return err;
L
Linus Torvalds 已提交
114 115 116 117
}

static void __exit hp680_ts_exit(void)
{
118
	free_irq(HP680_TS_IRQ, NULL);
L
Linus Torvalds 已提交
119 120
	cancel_delayed_work(&work);
	flush_scheduled_work();
121
	input_unregister_device(hp680_ts_dev);
L
Linus Torvalds 已提交
122 123 124 125 126 127 128 129
}

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");