blob: fcb4bcda43292061fb21e6f8f435a3f7f9239c88 [file] [log] [blame]
/*
* Platform driver for the HP Jornada 620/660/680/690 Touchscreen.
*
* Copyright 2008 Kristoffer Ericson <kristoffer.ericson@gmail.com>
* Copyright ...-2007 Andriy Skulysh <askulysh@image.kiev.ua>
*/
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/delay.h>
#include <asm/adc.h>
#include <asm/hp6xx.h>
#define MODNAME "hp680_ts_input"
#define PHDR 0xa400012e /* PORT H DATA REGISTER */
#define SCPDR 0xa4000136 /* PORT SC DATA REGISTER */
struct input_dev *dev;
static void do_softint(struct delayed_work *work);
static DECLARE_DELAYED_WORK(work, do_softint);
static void do_softint(struct delayed_work *work)
{
u8 scpdr;
int touched = 0;
int x, y;
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);
y = 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);
x = 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_abs(dev, ABS_X, x);
input_report_abs(dev, ABS_Y, y);
input_report_abs(dev, ABS_PRESSURE, 1);
input_report_key(dev, BTN_TOUCH, 1);
input_sync(dev);
} else {
input_report_abs(dev, ABS_PRESSURE, 0);
input_report_key(dev, BTN_TOUCH, 0);
input_sync(dev);
}
enable_irq(HP680_TS_IRQ);
}
static irqreturn_t hp680_ts_interrupt(int irq, void *pdev)
{
disable_irq_nosync(irq);
schedule_delayed_work(&work, HZ / 20);
return IRQ_HANDLED;
}
static int __devinit jornada680_ts_probe(struct platform_device *pdev)
{
int error;
dev = input_allocate_device();
if (!dev) {
printk(KERN_INFO "ts: failed to allocate device\n");
error = -ENODEV;
return error;
}
dev->name = "HP Jornada 6xx Touchscreen";
dev->phys = "jornadats/input0";
dev->id.bustype = BUS_HOST;
dev->dev.parent = &pdev->dev;
dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
dev->absbit[0] = BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(dev, ABS_X, 40, 950, 0, 0);
input_set_abs_params(dev, ABS_Y, 80, 910, 0, 0);
error = input_register_device(dev);
if (error)
goto fail2;
error = request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
IRQF_DISABLED, "HP6xx Touchscreen Driver", NULL);
if (error) {
printk(KERN_INFO "ts: Unable to aquire irq %d\n", HP680_TS_IRQ);
error = -ENODEV;
goto fail3;
}
return 0;
fail3:
input_unregister_device(dev);
fail2:
input_free_device(dev);
return error;
}
static int __devexit jornada680_ts_remove(struct platform_device *pdev)
{
cancel_delayed_work(&work);
flush_scheduled_work();
free_irq(HP680_TS_IRQ, pdev);
input_unregister_device(dev);
return 0;
}
static struct platform_driver jornada680_ts_driver = {
.probe = jornada680_ts_probe,
.remove = jornada680_ts_remove,
.driver = {
.name = "jornada_ts",
},
};
static int __init hp680_ts_init(void)
{
return platform_driver_register(&jornada680_ts_driver);
}
static void __exit hp680_ts_exit(void)
{
platform_driver_unregister(&jornada680_ts_driver);
}
module_init(hp680_ts_init);
module_exit(hp680_ts_exit);
MODULE_AUTHOR("Andriy Skulysh <askulysh@image.kiev.ua>, Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 6xx touchscreen driver");
MODULE_LICENSE("GPL");