123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248 |
- /*
- * HTC Shift touchscreen driver
- *
- * Copyright (C) 2008 Pau Oliva Fora <pof@eslack.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 as published
- * by the Free Software Foundation.
- */
- #include <linux/errno.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/input.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/init.h>
- #include <linux/irq.h>
- #include <linux/isa.h>
- #include <linux/ioport.h>
- #include <linux/dmi.h>
- MODULE_AUTHOR("Pau Oliva Fora <pau@eslack.org>");
- MODULE_DESCRIPTION("HTC Shift touchscreen driver");
- MODULE_LICENSE("GPL");
- #define HTCPEN_PORT_IRQ_CLEAR 0x068
- #define HTCPEN_PORT_INIT 0x06c
- #define HTCPEN_PORT_INDEX 0x0250
- #define HTCPEN_PORT_DATA 0x0251
- #define HTCPEN_IRQ 3
- #define DEVICE_ENABLE 0xa2
- #define DEVICE_DISABLE 0xa3
- #define X_INDEX 3
- #define Y_INDEX 5
- #define TOUCH_INDEX 0xb
- #define LSB_XY_INDEX 0xc
- #define X_AXIS_MAX 2040
- #define Y_AXIS_MAX 2040
- static bool invert_x;
- module_param(invert_x, bool, 0644);
- MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
- static bool invert_y;
- module_param(invert_y, bool, 0644);
- MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
- static irqreturn_t htcpen_interrupt(int irq, void *handle)
- {
- struct input_dev *htcpen_dev = handle;
- unsigned short x, y, xy;
- /* 0 = press; 1 = release */
- outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
- if (inb_p(HTCPEN_PORT_DATA)) {
- input_report_key(htcpen_dev, BTN_TOUCH, 0);
- } else {
- outb_p(X_INDEX, HTCPEN_PORT_INDEX);
- x = inb_p(HTCPEN_PORT_DATA);
- outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
- y = inb_p(HTCPEN_PORT_DATA);
- outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
- xy = inb_p(HTCPEN_PORT_DATA);
- /* get high resolution value of X and Y using LSB */
- x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
- y = (y * 8) + (xy & 0xf);
- if (invert_x)
- x = X_AXIS_MAX - x;
- if (invert_y)
- y = Y_AXIS_MAX - y;
- if (x != X_AXIS_MAX && x != 0) {
- input_report_key(htcpen_dev, BTN_TOUCH, 1);
- input_report_abs(htcpen_dev, ABS_X, x);
- input_report_abs(htcpen_dev, ABS_Y, y);
- }
- }
- input_sync(htcpen_dev);
- inb_p(HTCPEN_PORT_IRQ_CLEAR);
- return IRQ_HANDLED;
- }
- static int htcpen_open(struct input_dev *dev)
- {
- outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
- return 0;
- }
- static void htcpen_close(struct input_dev *dev)
- {
- outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
- synchronize_irq(HTCPEN_IRQ);
- }
- static int htcpen_isa_probe(struct device *dev, unsigned int id)
- {
- struct input_dev *htcpen_dev;
- int err = -EBUSY;
- if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
- printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
- HTCPEN_PORT_IRQ_CLEAR);
- goto request_region1_failed;
- }
- if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
- printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
- HTCPEN_PORT_INIT);
- goto request_region2_failed;
- }
- if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
- printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
- HTCPEN_PORT_INDEX);
- goto request_region3_failed;
- }
- htcpen_dev = input_allocate_device();
- if (!htcpen_dev) {
- printk(KERN_ERR "htcpen: can't allocate device\n");
- err = -ENOMEM;
- goto input_alloc_failed;
- }
- htcpen_dev->name = "HTC Shift EC TouchScreen";
- htcpen_dev->id.bustype = BUS_ISA;
- htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
- htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
- input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
- input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
- htcpen_dev->open = htcpen_open;
- htcpen_dev->close = htcpen_close;
- err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
- htcpen_dev);
- if (err) {
- printk(KERN_ERR "htcpen: irq busy\n");
- goto request_irq_failed;
- }
- inb_p(HTCPEN_PORT_IRQ_CLEAR);
- err = input_register_device(htcpen_dev);
- if (err)
- goto input_register_failed;
- dev_set_drvdata(dev, htcpen_dev);
- return 0;
- input_register_failed:
- free_irq(HTCPEN_IRQ, htcpen_dev);
- request_irq_failed:
- input_free_device(htcpen_dev);
- input_alloc_failed:
- release_region(HTCPEN_PORT_INDEX, 2);
- request_region3_failed:
- release_region(HTCPEN_PORT_INIT, 1);
- request_region2_failed:
- release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
- request_region1_failed:
- return err;
- }
- static int htcpen_isa_remove(struct device *dev, unsigned int id)
- {
- struct input_dev *htcpen_dev = dev_get_drvdata(dev);
- input_unregister_device(htcpen_dev);
- free_irq(HTCPEN_IRQ, htcpen_dev);
- release_region(HTCPEN_PORT_INDEX, 2);
- release_region(HTCPEN_PORT_INIT, 1);
- release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
- return 0;
- }
- #ifdef CONFIG_PM
- static int htcpen_isa_suspend(struct device *dev, unsigned int n,
- pm_message_t state)
- {
- outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
- return 0;
- }
- static int htcpen_isa_resume(struct device *dev, unsigned int n)
- {
- outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
- return 0;
- }
- #endif
- static struct isa_driver htcpen_isa_driver = {
- .probe = htcpen_isa_probe,
- .remove = htcpen_isa_remove,
- #ifdef CONFIG_PM
- .suspend = htcpen_isa_suspend,
- .resume = htcpen_isa_resume,
- #endif
- .driver = {
- .owner = THIS_MODULE,
- .name = "htcpen",
- }
- };
- static struct dmi_system_id htcshift_dmi_table[] __initdata = {
- {
- .ident = "Shift",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
- },
- },
- { }
- };
- MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
- static int __init htcpen_isa_init(void)
- {
- if (!dmi_check_system(htcshift_dmi_table))
- return -ENODEV;
- return isa_register_driver(&htcpen_isa_driver, 1);
- }
- static void __exit htcpen_isa_exit(void)
- {
- isa_unregister_driver(&htcpen_isa_driver);
- }
- module_init(htcpen_isa_init);
- module_exit(htcpen_isa_exit);
|