0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/errno.h>
0009 #include <linux/kernel.h>
0010 #include <linux/module.h>
0011 #include <linux/input.h>
0012 #include <linux/interrupt.h>
0013 #include <linux/io.h>
0014 #include <linux/init.h>
0015 #include <linux/irq.h>
0016 #include <linux/isa.h>
0017 #include <linux/ioport.h>
0018 #include <linux/dmi.h>
0019
0020 MODULE_AUTHOR("Pau Oliva Fora <pau@eslack.org>");
0021 MODULE_DESCRIPTION("HTC Shift touchscreen driver");
0022 MODULE_LICENSE("GPL");
0023
0024 #define HTCPEN_PORT_IRQ_CLEAR 0x068
0025 #define HTCPEN_PORT_INIT 0x06c
0026 #define HTCPEN_PORT_INDEX 0x0250
0027 #define HTCPEN_PORT_DATA 0x0251
0028 #define HTCPEN_IRQ 3
0029
0030 #define DEVICE_ENABLE 0xa2
0031 #define DEVICE_DISABLE 0xa3
0032
0033 #define X_INDEX 3
0034 #define Y_INDEX 5
0035 #define TOUCH_INDEX 0xb
0036 #define LSB_XY_INDEX 0xc
0037 #define X_AXIS_MAX 2040
0038 #define Y_AXIS_MAX 2040
0039
0040 static bool invert_x;
0041 module_param(invert_x, bool, 0644);
0042 MODULE_PARM_DESC(invert_x, "If set, X axis is inverted");
0043 static bool invert_y;
0044 module_param(invert_y, bool, 0644);
0045 MODULE_PARM_DESC(invert_y, "If set, Y axis is inverted");
0046
0047 static irqreturn_t htcpen_interrupt(int irq, void *handle)
0048 {
0049 struct input_dev *htcpen_dev = handle;
0050 unsigned short x, y, xy;
0051
0052
0053 outb_p(TOUCH_INDEX, HTCPEN_PORT_INDEX);
0054
0055 if (inb_p(HTCPEN_PORT_DATA)) {
0056 input_report_key(htcpen_dev, BTN_TOUCH, 0);
0057 } else {
0058 outb_p(X_INDEX, HTCPEN_PORT_INDEX);
0059 x = inb_p(HTCPEN_PORT_DATA);
0060
0061 outb_p(Y_INDEX, HTCPEN_PORT_INDEX);
0062 y = inb_p(HTCPEN_PORT_DATA);
0063
0064 outb_p(LSB_XY_INDEX, HTCPEN_PORT_INDEX);
0065 xy = inb_p(HTCPEN_PORT_DATA);
0066
0067
0068 x = X_AXIS_MAX - ((x * 8) + ((xy >> 4) & 0xf));
0069 y = (y * 8) + (xy & 0xf);
0070 if (invert_x)
0071 x = X_AXIS_MAX - x;
0072 if (invert_y)
0073 y = Y_AXIS_MAX - y;
0074
0075 if (x != X_AXIS_MAX && x != 0) {
0076 input_report_key(htcpen_dev, BTN_TOUCH, 1);
0077 input_report_abs(htcpen_dev, ABS_X, x);
0078 input_report_abs(htcpen_dev, ABS_Y, y);
0079 }
0080 }
0081
0082 input_sync(htcpen_dev);
0083
0084 inb_p(HTCPEN_PORT_IRQ_CLEAR);
0085
0086 return IRQ_HANDLED;
0087 }
0088
0089 static int htcpen_open(struct input_dev *dev)
0090 {
0091 outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
0092
0093 return 0;
0094 }
0095
0096 static void htcpen_close(struct input_dev *dev)
0097 {
0098 outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
0099 synchronize_irq(HTCPEN_IRQ);
0100 }
0101
0102 static int htcpen_isa_probe(struct device *dev, unsigned int id)
0103 {
0104 struct input_dev *htcpen_dev;
0105 int err = -EBUSY;
0106
0107 if (!request_region(HTCPEN_PORT_IRQ_CLEAR, 1, "htcpen")) {
0108 printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
0109 HTCPEN_PORT_IRQ_CLEAR);
0110 goto request_region1_failed;
0111 }
0112
0113 if (!request_region(HTCPEN_PORT_INIT, 1, "htcpen")) {
0114 printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
0115 HTCPEN_PORT_INIT);
0116 goto request_region2_failed;
0117 }
0118
0119 if (!request_region(HTCPEN_PORT_INDEX, 2, "htcpen")) {
0120 printk(KERN_ERR "htcpen: unable to get IO region 0x%x\n",
0121 HTCPEN_PORT_INDEX);
0122 goto request_region3_failed;
0123 }
0124
0125 htcpen_dev = input_allocate_device();
0126 if (!htcpen_dev) {
0127 printk(KERN_ERR "htcpen: can't allocate device\n");
0128 err = -ENOMEM;
0129 goto input_alloc_failed;
0130 }
0131
0132 htcpen_dev->name = "HTC Shift EC TouchScreen";
0133 htcpen_dev->id.bustype = BUS_ISA;
0134
0135 htcpen_dev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
0136 htcpen_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
0137 input_set_abs_params(htcpen_dev, ABS_X, 0, X_AXIS_MAX, 0, 0);
0138 input_set_abs_params(htcpen_dev, ABS_Y, 0, Y_AXIS_MAX, 0, 0);
0139
0140 htcpen_dev->open = htcpen_open;
0141 htcpen_dev->close = htcpen_close;
0142
0143 err = request_irq(HTCPEN_IRQ, htcpen_interrupt, 0, "htcpen",
0144 htcpen_dev);
0145 if (err) {
0146 printk(KERN_ERR "htcpen: irq busy\n");
0147 goto request_irq_failed;
0148 }
0149
0150 inb_p(HTCPEN_PORT_IRQ_CLEAR);
0151
0152 err = input_register_device(htcpen_dev);
0153 if (err)
0154 goto input_register_failed;
0155
0156 dev_set_drvdata(dev, htcpen_dev);
0157
0158 return 0;
0159
0160 input_register_failed:
0161 free_irq(HTCPEN_IRQ, htcpen_dev);
0162 request_irq_failed:
0163 input_free_device(htcpen_dev);
0164 input_alloc_failed:
0165 release_region(HTCPEN_PORT_INDEX, 2);
0166 request_region3_failed:
0167 release_region(HTCPEN_PORT_INIT, 1);
0168 request_region2_failed:
0169 release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
0170 request_region1_failed:
0171 return err;
0172 }
0173
0174 static void htcpen_isa_remove(struct device *dev, unsigned int id)
0175 {
0176 struct input_dev *htcpen_dev = dev_get_drvdata(dev);
0177
0178 input_unregister_device(htcpen_dev);
0179
0180 free_irq(HTCPEN_IRQ, htcpen_dev);
0181
0182 release_region(HTCPEN_PORT_INDEX, 2);
0183 release_region(HTCPEN_PORT_INIT, 1);
0184 release_region(HTCPEN_PORT_IRQ_CLEAR, 1);
0185 }
0186
0187 #ifdef CONFIG_PM
0188 static int htcpen_isa_suspend(struct device *dev, unsigned int n,
0189 pm_message_t state)
0190 {
0191 outb_p(DEVICE_DISABLE, HTCPEN_PORT_INIT);
0192
0193 return 0;
0194 }
0195
0196 static int htcpen_isa_resume(struct device *dev, unsigned int n)
0197 {
0198 outb_p(DEVICE_ENABLE, HTCPEN_PORT_INIT);
0199
0200 return 0;
0201 }
0202 #endif
0203
0204 static struct isa_driver htcpen_isa_driver = {
0205 .probe = htcpen_isa_probe,
0206 .remove = htcpen_isa_remove,
0207 #ifdef CONFIG_PM
0208 .suspend = htcpen_isa_suspend,
0209 .resume = htcpen_isa_resume,
0210 #endif
0211 .driver = {
0212 .owner = THIS_MODULE,
0213 .name = "htcpen",
0214 }
0215 };
0216
0217 static const struct dmi_system_id htcshift_dmi_table[] __initconst = {
0218 {
0219 .ident = "Shift",
0220 .matches = {
0221 DMI_MATCH(DMI_SYS_VENDOR, "High Tech Computer Corp"),
0222 DMI_MATCH(DMI_PRODUCT_NAME, "Shift"),
0223 },
0224 },
0225 { }
0226 };
0227 MODULE_DEVICE_TABLE(dmi, htcshift_dmi_table);
0228
0229 static int __init htcpen_isa_init(void)
0230 {
0231 if (!dmi_check_system(htcshift_dmi_table))
0232 return -ENODEV;
0233
0234 return isa_register_driver(&htcpen_isa_driver, 1);
0235 }
0236
0237 static void __exit htcpen_isa_exit(void)
0238 {
0239 isa_unregister_driver(&htcpen_isa_driver);
0240 }
0241
0242 module_init(htcpen_isa_init);
0243 module_exit(htcpen_isa_exit);