Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * HTC Shift touchscreen driver
0004  *
0005  * Copyright (C) 2008 Pau Oliva Fora <pof@eslack.org>
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     /* 0 = press; 1 = release */
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         /* get high resolution value of X and Y using LSB */
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);