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

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

23
static struct input_dev *hp680_ts_dev;
24
static DECLARE_WORK(work, do_softint);
L
Linus Torvalds 已提交
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

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) {
58 59 60
		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 已提交
61
	} else {
62
		input_report_key(hp680_ts_dev, BTN_TOUCH, 0);
L
Linus Torvalds 已提交
63 64
	}

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

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

	return IRQ_HANDLED;
}

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

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

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

88 89 90 91
	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 已提交
92

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

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

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

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

 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 已提交
115 116 117 118
}

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

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