]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Jan 2016 19:14:05 +0000 (11:14 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Jan 2016 19:14:05 +0000 (11:14 -0800)
Pull input updates from Dmitry Torokhov:
 - new driver for eGalaxTouch serial touchscreen
 - new driver for TS-4800 touchscreen
 - an update for Goodix touchscreen driver
 - PS/2 mouse module was reworked to limit number of protocols we try on
   pass-through ports to speed up their detection time
 - wacom_w8001 touchscreen driver now reports pen and touch via separate
   instances of input devices
 - other driver changes

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (42 commits)
  Input: elantech - mark protocols v2 and v3 as semi-mt
  Input: wacom_w8001 - drop use of ABS_MT_TOOL_TYPE
  Input: gpio-keys - fix check for disabling unsupported keys
  Input: omap-keypad - remove dead check
  Input: ti_am335x_tsc - fix HWPEN interrupt handling
  Input: omap-keypad - set tasklet data earlier
  Input: rohm_bu21023 - fix handling of retrying firmware update
  Input: ALPS - report v3 pinnacle trackstick device only if is present
  Input: ALPS - detect trackstick presence for v7 protocol
  Input: pcap_ts - use to_delayed_work
  Input: bma150 - constify bma150_cfg structure
  Input: i8042 - add Fujitsu Lifebook U745 to the nomux list
  Input: egalax_ts_serial - fix potential NULL dereference on error
  Input: uinput - sanity check on ff_effects_max and EV_FF
  Input: uinput - rework ABS validation
  Input: uinput - add new UINPUT_DEV_SETUP and UI_ABS_SETUP ioctl
  Input: goodix - use "inverted_[xy]" flags instead of "rotated_screen"
  Input: goodix - add axis swapping and axis inversion support
  Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send
  Input: goodix - add power management support
  ...

33 files changed:
Documentation/devicetree/bindings/input/touchscreen/goodix.txt
Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt [new file with mode: 0644]
drivers/gpio/gpiolib-acpi.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/omap-keypad.c
drivers/input/misc/bma150.c
drivers/input/misc/da9063_onkey.c
drivers/input/misc/sparcspkr.c
drivers/input/misc/uinput.c
drivers/input/mouse/alps.c
drivers/input/mouse/elantech.c
drivers/input/mouse/focaltech.c
drivers/input/mouse/focaltech.h
drivers/input/mouse/logips2pp.c
drivers/input/mouse/logips2pp.h
drivers/input/mouse/psmouse-base.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/egalax_ts_serial.c [new file with mode: 0644]
drivers/input/touchscreen/goodix.c
drivers/input/touchscreen/pcap_ts.c
drivers/input/touchscreen/pixcir_i2c_ts.c
drivers/input/touchscreen/rohm_bu21023.c
drivers/input/touchscreen/ti_am335x_tsc.c
drivers/input/touchscreen/ts4800-ts.c [new file with mode: 0644]
drivers/input/touchscreen/wacom_w8001.c
include/linux/uinput.h
include/uapi/linux/serio.h
include/uapi/linux/uinput.h

index 8ba98eec765bc5cf27ee1e620bb53a282bf8d921..c98757a69110dbc6a997998d1e27753c205bdbdd 100644 (file)
@@ -13,6 +13,17 @@ Required properties:
  - interrupt-parent    : Interrupt controller to which the chip is connected
  - interrupts          : Interrupt to which the chip is connected
 
+Optional properties:
+
+ - irq-gpios           : GPIO pin used for IRQ. The driver uses the
+                         interrupt gpio pin as output to reset the device.
+ - reset-gpios         : GPIO pin used for reset
+
+ - touchscreen-inverted-x  : X axis is inverted (boolean)
+ - touchscreen-inverted-y  : Y axis is inverted (boolean)
+ - touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
+                             (swapping is done after inverting the axis)
+
 Example:
 
        i2c@00000000 {
@@ -23,6 +34,9 @@ Example:
                        reg = <0x5d>;
                        interrupt-parent = <&gpio>;
                        interrupts = <0 0>;
+
+                       irq-gpios = <&gpio1 0 0>;
+                       reset-gpios = <&gpio1 1 0>;
                };
 
                /* ... */
index 8eb240a287c8aec13881a395eaa25a3da06c7194..697a3e7831e734e00173ffee012d3bc0f40381da 100644 (file)
@@ -9,7 +9,9 @@ Required properties:
 - touchscreen-size-y: vertical resolution of touchscreen (in pixels)
 
 Optional properties:
-- reset-gpio: GPIO connected to the RESET line of the chip
+- reset-gpios: GPIO connected to the RESET line of the chip
+- enable-gpios: GPIO connected to the ENABLE line of the chip
+- wake-gpios: GPIO connected to the WAKE line of the chip
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/ts4800-ts.txt
new file mode 100644 (file)
index 0000000..4c1c092
--- /dev/null
@@ -0,0 +1,11 @@
+* TS-4800 Touchscreen bindings
+
+Required properties:
+- compatible: must be "technologic,ts4800-ts"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- syscon: phandle / integers array that points to the syscon node which
+          describes the FPGA's syscon registers.
+          - phandle to FPGA's syscon
+          - offset to the touchscreen register
+          - offset to the touchscreen enable bit
index cbbb67a6f1d6064d15481bba121eb4383a73409d..bc34bc51a9487904a7ad4e44917e5d64700fa8a0 100644 (file)
@@ -943,3 +943,46 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
        }
        return count;
 }
+
+struct acpi_crs_lookup {
+       struct list_head node;
+       struct acpi_device *adev;
+       const char *con_id;
+};
+
+static DEFINE_MUTEX(acpi_crs_lookup_lock);
+static LIST_HEAD(acpi_crs_lookup_list);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
+{
+       struct acpi_crs_lookup *l, *lookup = NULL;
+
+       /* Never allow fallback if the device has properties */
+       if (adev->data.properties || adev->driver_gpios)
+               return false;
+
+       mutex_lock(&acpi_crs_lookup_lock);
+
+       list_for_each_entry(l, &acpi_crs_lookup_list, node) {
+               if (l->adev == adev) {
+                       lookup = l;
+                       break;
+               }
+       }
+
+       if (!lookup) {
+               lookup = kmalloc(sizeof(*lookup), GFP_KERNEL);
+               if (lookup) {
+                       lookup->adev = adev;
+                       lookup->con_id = con_id;
+                       list_add_tail(&lookup->node, &acpi_crs_lookup_list);
+               }
+       }
+
+       mutex_unlock(&acpi_crs_lookup_lock);
+
+       return lookup &&
+               ((!lookup->con_id && !con_id) ||
+                (lookup->con_id && con_id &&
+                 strcmp(lookup->con_id, con_id) == 0));
+}
index 5d8d7ab969163e332ce795d61632d2d982b39ccd..3346abd29b5204408114068f17338282991f0ec4 100644 (file)
@@ -1874,6 +1874,9 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
 
        /* Then from plain _CRS GPIOs */
        if (IS_ERR(desc)) {
+               if (!acpi_can_fallback_to_crs(adev, con_id))
+                       return ERR_PTR(-ENOENT);
+
                desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
                if (IS_ERR(desc))
                        return desc;
index 5ac3b88a2e0acff769aad50376b92ce15a0272dd..be3a977d0349d4c99b435f0f1dcce7dd0c9ef867 100644 (file)
@@ -48,6 +48,8 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
                                      struct acpi_gpio_info *info);
 
 int acpi_gpio_count(struct device *dev, const char *con_id);
+
+bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
 #else
 static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
 static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
@@ -74,6 +76,12 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id)
 {
        return -ENODEV;
 }
+
+static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
+                                           const char *con_id)
+{
+       return false;
+}
 #endif
 
 struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
index bef317ff7352ffd73885ea6a11efd58efba9663d..b9f01bd1b7ef1b76a7b4c0d46f71fc3e4ace1818 100644 (file)
@@ -96,13 +96,29 @@ struct gpio_keys_drvdata {
  * Return value of this function can be used to allocate bitmap
  * large enough to hold all bits for given type.
  */
-static inline int get_n_events_by_type(int type)
+static int get_n_events_by_type(int type)
 {
        BUG_ON(type != EV_SW && type != EV_KEY);
 
        return (type == EV_KEY) ? KEY_CNT : SW_CNT;
 }
 
+/**
+ * get_bm_events_by_type() - returns bitmap of supported events per @type
+ * @input: input device from which bitmap is retrieved
+ * @type: type of button (%EV_KEY, %EV_SW)
+ *
+ * Return value of this function can be used to allocate bitmap
+ * large enough to hold all bits for given type.
+ */
+static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
+                                                 int type)
+{
+       BUG_ON(type != EV_SW && type != EV_KEY);
+
+       return (type == EV_KEY) ? dev->keybit : dev->swbit;
+}
+
 /**
  * gpio_keys_disable_button() - disables given GPIO button
  * @bdata: button data for button to be disabled
@@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
                                           const char *buf, unsigned int type)
 {
        int n_events = get_n_events_by_type(type);
+       const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
        unsigned long *bits;
        ssize_t error;
        int i;
@@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
                goto out;
 
        /* First validate */
+       if (!bitmap_subset(bits, bitmap, n_events)) {
+               error = -EINVAL;
+               goto out;
+       }
+
        for (i = 0; i < ddata->pdata->nbuttons; i++) {
                struct gpio_button_data *bdata = &ddata->data[i];
 
@@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
                }
        }
 
-       if (i == ddata->pdata->nbuttons) {
-               error = -EINVAL;
-               goto out;
-       }
-
        mutex_lock(&ddata->disable_lock);
 
        for (i = 0; i < ddata->pdata->nbuttons; i++) {
index 7502e46165facb0aeed086277a39f5bd0ec58c10..e0d72c8c01e428290ca56b6263087a4b013a9631 100644 (file)
@@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data)
                               "pressed" : "released");
 #else
                        key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
-                       if (key < 0) {
-                               printk(KERN_WARNING
-                                     "omap-keypad: Spurious key event %d-%d\n",
-                                      col, row);
-                               /* We scan again after a couple of seconds */
-                               spurious = 1;
-                               continue;
-                       }
 
                        if (!(kp_cur_group == (key & GROUP_MASK) ||
                              kp_cur_group == -1))
@@ -292,8 +284,8 @@ static int omap_kp_probe(struct platform_device *pdev)
        setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
 
        /* get the irq and init timer*/
-       tasklet_enable(&kp_tasklet);
        kp_tasklet.data = (unsigned long) omap_kp;
+       tasklet_enable(&kp_tasklet);
 
        ret = device_create_file(&pdev->dev, &dev_attr_enable);
        if (ret < 0)
index 1d0e61d7c13188d2dbc4d9fe811ada66787ea67f..b0d445390ee44dfe3fb2bf3fea11d0d8f9257a62 100644 (file)
@@ -147,7 +147,7 @@ struct bma150_data {
  * are stated and verified by Bosch Sensortec where they are configured
  * to provide a generic sensitivity performance.
  */
-static struct bma150_cfg default_cfg = {
+static const struct bma150_cfg default_cfg = {
        .any_motion_int = 1,
        .hg_int = 1,
        .lg_int = 1,
index 8eb697db82d03606839e0002c5232513cfb80477..bb863e062b03b3cf66317c8899401478e2f06930 100644 (file)
@@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
                input_report_key(onkey->input, KEY_POWER, 1);
                input_sync(onkey->input);
                schedule_delayed_work(&onkey->work, 0);
-               dev_dbg(onkey->dev, "KEY_POWER pressed.\n");
+               dev_dbg(onkey->dev, "KEY_POWER long press.\n");
        } else {
-               input_report_key(onkey->input, KEY_SLEEP, 1);
+               input_report_key(onkey->input, KEY_POWER, 1);
                input_sync(onkey->input);
-               input_report_key(onkey->input, KEY_SLEEP, 0);
+               input_report_key(onkey->input, KEY_POWER, 0);
                input_sync(onkey->input);
-               dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n");
+               dev_dbg(onkey->dev, "KEY_POWER short press.\n");
        }
 
        return IRQ_HANDLED;
index 6f997aa49183ae40daa709361e1b66f19fb862a8..4a5afc7fe96ead167bdf1ef918e39b6095c7cfd4 100644 (file)
@@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = {
        .shutdown       = sparcspkr_shutdown,
 };
 
+static struct platform_driver * const drivers[] = {
+       &bbc_beep_driver,
+       &grover_beep_driver,
+};
+
 static int __init sparcspkr_init(void)
 {
-       int err = platform_driver_register(&bbc_beep_driver);
-
-       if (!err) {
-               err = platform_driver_register(&grover_beep_driver);
-               if (err)
-                       platform_driver_unregister(&bbc_beep_driver);
-       }
-
-       return err;
+       return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
 }
 
 static void __exit sparcspkr_exit(void)
 {
-       platform_driver_unregister(&bbc_beep_driver);
-       platform_driver_unregister(&grover_beep_driver);
+       platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
 }
 
 module_init(sparcspkr_init);
index 5adbcedcb81cf4391bfdddefe11aae5d3131dd76..4eb9e4d94f46965e884f71326b96620982d9043a 100644 (file)
@@ -256,13 +256,29 @@ static void uinput_destroy_device(struct uinput_device *udev)
 static int uinput_create_device(struct uinput_device *udev)
 {
        struct input_dev *dev = udev->dev;
-       int error;
+       int error, nslot;
 
        if (udev->state != UIST_SETUP_COMPLETE) {
                printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
                return -EINVAL;
        }
 
+       if (test_bit(ABS_MT_SLOT, dev->absbit)) {
+               nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
+               error = input_mt_init_slots(dev, nslot, 0);
+               if (error)
+                       goto fail1;
+       } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+               input_set_events_per_packet(dev, 60);
+       }
+
+       if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) {
+               printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n",
+                       UINPUT_NAME);
+               error = -EINVAL;
+               goto fail1;
+       }
+
        if (udev->ff_effects_max) {
                error = input_ff_create(dev, udev->ff_effects_max);
                if (error)
@@ -308,10 +324,35 @@ static int uinput_open(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
+                                  const struct input_absinfo *abs)
+{
+       int min, max;
+
+       min = abs->minimum;
+       max = abs->maximum;
+
+       if ((min != 0 || max != 0) && max <= min) {
+               printk(KERN_DEBUG
+                      "%s: invalid abs[%02x] min:%d max:%d\n",
+                      UINPUT_NAME, code, min, max);
+               return -EINVAL;
+       }
+
+       if (abs->flat > max - min) {
+               printk(KERN_DEBUG
+                      "%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
+                      UINPUT_NAME, code, abs->flat, min, max);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int uinput_validate_absbits(struct input_dev *dev)
 {
        unsigned int cnt;
-       int nslot;
+       int error;
 
        if (!test_bit(EV_ABS, dev->evbit))
                return 0;
@@ -321,38 +362,12 @@ static int uinput_validate_absbits(struct input_dev *dev)
         */
 
        for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
-               int min, max;
-
-               min = input_abs_get_min(dev, cnt);
-               max = input_abs_get_max(dev, cnt);
-
-               if ((min != 0 || max != 0) && max <= min) {
-                       printk(KERN_DEBUG
-                               "%s: invalid abs[%02x] min:%d max:%d\n",
-                               UINPUT_NAME, cnt,
-                               input_abs_get_min(dev, cnt),
-                               input_abs_get_max(dev, cnt));
+               if (!dev->absinfo)
                        return -EINVAL;
-               }
 
-               if (input_abs_get_flat(dev, cnt) >
-                   input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
-                       printk(KERN_DEBUG
-                               "%s: abs_flat #%02x out of range: %d "
-                               "(min:%d/max:%d)\n",
-                               UINPUT_NAME, cnt,
-                               input_abs_get_flat(dev, cnt),
-                               input_abs_get_min(dev, cnt),
-                               input_abs_get_max(dev, cnt));
-                       return -EINVAL;
-               }
-       }
-
-       if (test_bit(ABS_MT_SLOT, dev->absbit)) {
-               nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
-               input_mt_init_slots(dev, nslot, 0);
-       } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
-               input_set_events_per_packet(dev, 60);
+               error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
+               if (error)
+                       return error;
        }
 
        return 0;
@@ -370,8 +385,71 @@ static int uinput_allocate_device(struct uinput_device *udev)
        return 0;
 }
 
-static int uinput_setup_device(struct uinput_device *udev,
-                              const char __user *buffer, size_t count)
+static int uinput_dev_setup(struct uinput_device *udev,
+                           struct uinput_setup __user *arg)
+{
+       struct uinput_setup setup;
+       struct input_dev *dev;
+
+       if (udev->state == UIST_CREATED)
+               return -EINVAL;
+
+       if (copy_from_user(&setup, arg, sizeof(setup)))
+               return -EFAULT;
+
+       if (!setup.name[0])
+               return -EINVAL;
+
+       dev = udev->dev;
+       dev->id = setup.id;
+       udev->ff_effects_max = setup.ff_effects_max;
+
+       kfree(dev->name);
+       dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
+       if (!dev->name)
+               return -ENOMEM;
+
+       udev->state = UIST_SETUP_COMPLETE;
+       return 0;
+}
+
+static int uinput_abs_setup(struct uinput_device *udev,
+                           struct uinput_setup __user *arg, size_t size)
+{
+       struct uinput_abs_setup setup = {};
+       struct input_dev *dev;
+       int error;
+
+       if (size > sizeof(setup))
+               return -E2BIG;
+
+       if (udev->state == UIST_CREATED)
+               return -EINVAL;
+
+       if (copy_from_user(&setup, arg, size))
+               return -EFAULT;
+
+       if (setup.code > ABS_MAX)
+               return -ERANGE;
+
+       dev = udev->dev;
+
+       error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
+       if (error)
+               return error;
+
+       input_alloc_absinfo(dev);
+       if (!dev->absinfo)
+               return -ENOMEM;
+
+       set_bit(setup.code, dev->absbit);
+       dev->absinfo[setup.code] = setup.absinfo;
+       return 0;
+}
+
+/* legacy setup via write() */
+static int uinput_setup_device_legacy(struct uinput_device *udev,
+                                     const char __user *buffer, size_t count)
 {
        struct uinput_user_dev  *user_dev;
        struct input_dev        *dev;
@@ -474,7 +552,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
 
        retval = udev->state == UIST_CREATED ?
                        uinput_inject_events(udev, buffer, count) :
-                       uinput_setup_device(udev, buffer, count);
+                       uinput_setup_device_legacy(udev, buffer, count);
 
        mutex_unlock(&udev->mutex);
 
@@ -735,6 +813,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
                        uinput_destroy_device(udev);
                        goto out;
 
+               case UI_DEV_SETUP:
+                       retval = uinput_dev_setup(udev, p);
+                       goto out;
+
+               /* UI_ABS_SETUP is handled in the variable size ioctls */
+
                case UI_SET_EVBIT:
                        retval = uinput_set_bit(arg, evbit, EV_MAX);
                        goto out;
@@ -879,6 +963,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
                name = dev_name(&udev->dev->dev);
                retval = uinput_str_to_user(p, name, size);
                goto out;
+
+       case UI_ABS_SETUP & ~IOCSIZE_MASK:
+               retval = uinput_abs_setup(udev, p, size);
+               goto out;
        }
 
        retval = -EINVAL;
index 41e6cb501e6a1d5b07801463460fe4ebbaace887..936f07a4e35f41265de96e84c2bab08f13544ad6 100644 (file)
@@ -31,6 +31,7 @@
 #define ALPS_CMD_NIBBLE_10     0x01f2
 
 #define ALPS_REG_BASE_RUSHMORE 0xc2c0
+#define ALPS_REG_BASE_V7       0xc2c0
 #define ALPS_REG_BASE_PINNACLE 0x0000
 
 static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
@@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse)
        return 0;
 }
 
-static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
+static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base)
 {
        int ret = -EIO, reg_val;
 
@@ -2128,15 +2129,12 @@ error:
 
 static int alps_hw_init_v3(struct psmouse *psmouse)
 {
+       struct alps_data *priv = psmouse->private;
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        int reg_val;
        unsigned char param[4];
 
-       reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
-       if (reg_val == -EIO)
-               goto error;
-
-       if (reg_val == 0 &&
+       if ((priv->flags & ALPS_DUALPOINT) &&
            alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
                goto error;
 
@@ -2613,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->decode_fields = alps_decode_pinnacle;
                priv->nibble_commands = alps_v3_nibble_commands;
                priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
+
+               if (alps_probe_trackstick_v3_v7(psmouse,
+                                               ALPS_REG_BASE_PINNACLE) < 0)
+                       priv->flags &= ~ALPS_DUALPOINT;
+
                break;
 
        case ALPS_PROTO_V3_RUSHMORE:
@@ -2625,8 +2628,8 @@ static int alps_set_protocol(struct psmouse *psmouse,
                priv->x_bits = 16;
                priv->y_bits = 12;
 
-               if (alps_probe_trackstick_v3(psmouse,
-                                            ALPS_REG_BASE_RUSHMORE) < 0)
+               if (alps_probe_trackstick_v3_v7(psmouse,
+                                               ALPS_REG_BASE_RUSHMORE) < 0)
                        priv->flags &= ~ALPS_DUALPOINT;
 
                break;
@@ -2676,6 +2679,9 @@ static int alps_set_protocol(struct psmouse *psmouse,
                if (priv->fw_ver[1] != 0xba)
                        priv->flags |= ALPS_BUTTONPAD;
 
+               if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0)
+                       priv->flags &= ~ALPS_DUALPOINT;
+
                break;
 
        case ALPS_PROTO_V8:
index 537ebb0e193a2a6f47e919c172151f37aa14b4c4..78f93cf68840d24328ebd0630f86c4e372d051bf 100644 (file)
@@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                        input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
                                             ETP_WMAX_V2, 0, 0);
                }
-               input_mt_init_slots(dev, 2, 0);
+               input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT);
                input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
                break;
index 4d5576de81be68efa489d8ca7df302a84790e48a..c8c6a8cc329d711cafe11a532011c6c7b20bc3d3 100644 (file)
@@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
        return 0;
 }
 
-static void focaltech_reset(struct psmouse *psmouse)
-{
-       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-       psmouse_reset(psmouse);
-}
-
 #ifdef CONFIG_MOUSE_PS2_FOCALTECH
 
 /*
@@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse)
        return 0;
 }
 
+static void focaltech_reset(struct psmouse *psmouse)
+{
+       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+       psmouse_reset(psmouse);
+}
+
 static void focaltech_disconnect(struct psmouse *psmouse)
 {
        focaltech_reset(psmouse);
@@ -456,14 +456,4 @@ fail:
        kfree(priv);
        return error;
 }
-
-#else /* CONFIG_MOUSE_PS2_FOCALTECH */
-
-int focaltech_init(struct psmouse *psmouse)
-{
-       focaltech_reset(psmouse);
-
-       return 0;
-}
-
 #endif /* CONFIG_MOUSE_PS2_FOCALTECH */
index ca61ebff373e99a194011c1d8554d8ffb0766981..783b28e8e10bec5b7013ae4f964afe6583e09ce2 100644 (file)
 #define _FOCALTECH_H
 
 int focaltech_detect(struct psmouse *psmouse, bool set_properties);
+
+#ifdef CONFIG_MOUSE_PS2_FOCALTECH
 int focaltech_init(struct psmouse *psmouse);
+#else
+static inline int focaltech_init(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+#endif
 
 #endif
index 136e222e2a16426b1ac4512348255074577c510e..422da1cd9e763839381267f0c3395100cf9df648 100644 (file)
@@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
  * that support it.
  */
 
-int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[4];
index 0c186f0282d973461f0c90dfbb12f40eabc0b85f..bf629453e0953c475332f46776883ca91016abc8 100644 (file)
@@ -12,9 +12,9 @@
 #define _LOGIPS2PP_H
 
 #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
-int ps2pp_init(struct psmouse *psmouse, bool set_properties);
+int ps2pp_detect(struct psmouse *psmouse, bool set_properties);
 #else
-inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
+static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
 {
        return -ENOSYS;
 }
index ad18dab0ac476323f1e7d7b73276c41f8524d337..b9e4ee34c132df58525a1969346520325c6a2490 100644 (file)
@@ -119,6 +119,7 @@ struct psmouse_protocol {
        enum psmouse_type type;
        bool maxproto;
        bool ignore_parity; /* Protocol should ignore parity errors from KBC */
+       bool try_passthru; /* Try protocol also on passthrough ports */
        const char *name;
        const char *alias;
        int (*detect)(struct psmouse *, bool);
@@ -129,7 +130,6 @@ struct psmouse_protocol {
  * psmouse_process_byte() analyzes the PS/2 data stream and reports
  * relevant events to the input module once full packet has arrived.
  */
-
 psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
@@ -138,22 +138,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
        if (psmouse->pktcnt < psmouse->pktsize)
                return PSMOUSE_GOOD_DATA;
 
-/*
- * Full packet accumulated, process it
- */
+       /* Full packet accumulated, process it */
 
-/*
- * Scroll wheel on IntelliMice, scroll buttons on NetMice
- */
-
-       if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
+       switch (psmouse->type) {
+       case PSMOUSE_IMPS:
+               /* IntelliMouse has scroll wheel */
                input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
+               break;
 
-/*
- * Scroll wheel and buttons on IntelliMouse Explorer
- */
-
-       if (psmouse->type == PSMOUSE_IMEX) {
+       case PSMOUSE_IMEX:
+               /* Scroll wheel and buttons on IntelliMouse Explorer */
                switch (packet[3] & 0xC0) {
                case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
                        input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
@@ -168,39 +162,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
                        input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
                        break;
                }
-       }
+               break;
 
-/*
- * Extra buttons on Genius NewNet 3D
- */
+       case PSMOUSE_GENPS:
+               /* Report scroll buttons on NetMice */
+               input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
 
-       if (psmouse->type == PSMOUSE_GENPS) {
+               /* Extra buttons on Genius NewNet 3D */
                input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
                input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
-       }
+               break;
 
-/*
- * Extra button on ThinkingMouse
- */
-       if (psmouse->type == PSMOUSE_THINKPS) {
+       case PSMOUSE_THINKPS:
+               /* Extra button on ThinkingMouse */
                input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
-               /* Without this bit of weirdness moving up gives wildly high Y changes. */
+
+               /*
+                * Without this bit of weirdness moving up gives wildly
+                * high Y changes.
+                */
                packet[1] |= (packet[0] & 0x40) << 1;
-       }
+               break;
 
-/*
- * Cortron PS2 Trackball reports SIDE button on the 4th bit of the first
- * byte.
- */
-       if (psmouse->type == PSMOUSE_CORTRON) {
+       case PSMOUSE_CORTRON:
+               /*
+                * Cortron PS2 Trackball reports SIDE button in the
+                * 4th bit of the first byte.
+                */
                input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
                packet[0] |= 0x08;
-       }
+               break;
 
-/*
- * Generic PS/2 Mouse
- */
+       default:
+               break;
+       }
 
+       /* Generic PS/2 Mouse */
        input_report_key(dev, BTN_LEFT,    packet[0]       & 1);
        input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
        input_report_key(dev, BTN_RIGHT,  (packet[0] >> 1) & 1);
@@ -222,7 +219,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
 /*
  * __psmouse_set_state() sets new psmouse state and resets all flags.
  */
-
 static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
 {
        psmouse->state = new_state;
@@ -231,13 +227,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta
        psmouse->last = jiffies;
 }
 
-
 /*
  * psmouse_set_state() sets new psmouse state and resets all flags and
  * counters while holding serio lock so fighting with interrupt handler
  * is not a concern.
  */
-
 void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
 {
        serio_pause_rx(psmouse->ps2dev.serio);
@@ -249,7 +243,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
  * psmouse_handle_byte() processes one byte of the input data stream
  * by calling corresponding protocol handler.
  */
-
 static int psmouse_handle_byte(struct psmouse *psmouse)
 {
        psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
@@ -292,7 +285,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
  * psmouse_interrupt() handles incoming characters, either passing them
  * for normal processing or gathering them as command response.
  */
-
 static irqreturn_t psmouse_interrupt(struct serio *serio,
                unsigned char data, unsigned int flags)
 {
@@ -335,9 +327,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
        }
 
        psmouse->packet[psmouse->pktcnt++] = data;
-/*
- * Check if this is a new device announcement (0xAA 0x00)
- */
+
+       /* Check if this is a new device announcement (0xAA 0x00) */
        if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
                if (psmouse->pktcnt == 1) {
                        psmouse->last = jiffies;
@@ -351,9 +342,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                        serio_reconnect(serio);
                        goto out;
                }
-/*
- * Not a new device, try processing first byte normally
- */
+
+               /* Not a new device, try processing first byte normally */
                psmouse->pktcnt = 1;
                if (psmouse_handle_byte(psmouse))
                        goto out;
@@ -361,9 +351,10 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                psmouse->packet[psmouse->pktcnt++] = data;
        }
 
-/*
- * See if we need to force resync because mouse was idle for too long
- */
+       /*
+        * See if we need to force resync because mouse was idle for
+        * too long.
+        */
        if (psmouse->state == PSMOUSE_ACTIVATED &&
            psmouse->pktcnt == 1 && psmouse->resync_time &&
            time_after(jiffies, psmouse->last + psmouse->resync_time * HZ)) {
@@ -380,7 +371,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
        return IRQ_HANDLED;
 }
 
-
 /*
  * psmouse_sliced_command() sends an extended PS/2 command to the mouse
  * using sliced syntax, understood by advanced devices, such as Logitech
@@ -404,7 +394,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
        return 0;
 }
 
-
 /*
  * psmouse_reset() resets the mouse into power-on state.
  */
@@ -424,7 +413,6 @@ int psmouse_reset(struct psmouse *psmouse)
 /*
  * Here we set the mouse resolution.
  */
-
 void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 {
        static const unsigned char params[] = { 0, 1, 2, 2, 3 };
@@ -441,7 +429,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
 /*
  * Here we set the mouse report rate.
  */
-
 static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
 {
        static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
@@ -457,7 +444,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
 /*
  * Here we set the mouse scaling.
  */
-
 static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
 {
        ps2_command(&psmouse->ps2dev, NULL,
@@ -468,7 +454,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
 /*
  * psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
  */
-
 static int psmouse_poll(struct psmouse *psmouse)
 {
        return ps2_command(&psmouse->ps2dev, psmouse->packet,
@@ -602,7 +587,7 @@ static int im_explorer_detect(struct psmouse *psmouse, bool set_properties)
        if (param[0] != 4)
                return -1;
 
-/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
+       /* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
        param[0] = 200;
        ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
        param[0] =  80;
@@ -672,10 +657,10 @@ static int ps2bare_detect(struct psmouse *psmouse, bool set_properties)
                if (!psmouse->name)
                        psmouse->name = "Mouse";
 
-/*
- * We have no way of figuring true number of buttons so let's
- * assume that the device has 3.
- */
+               /*
               * We have no way of figuring true number of buttons so let's
               * assume that the device has 3.
               */
                __set_bit(BTN_MIDDLE, psmouse->dev->keybit);
        }
 
@@ -699,284 +684,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)
        return 0;
 }
 
-/*
- * Apply default settings to the psmouse structure. Most of them will
- * be overridden by individual protocol initialization routines.
- */
-
-static void psmouse_apply_defaults(struct psmouse *psmouse)
-{
-       struct input_dev *input_dev = psmouse->dev;
-
-       memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
-       memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
-       memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
-       memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
-       memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
-
-       __set_bit(EV_KEY, input_dev->evbit);
-       __set_bit(EV_REL, input_dev->evbit);
-
-       __set_bit(BTN_LEFT, input_dev->keybit);
-       __set_bit(BTN_RIGHT, input_dev->keybit);
-
-       __set_bit(REL_X, input_dev->relbit);
-       __set_bit(REL_Y, input_dev->relbit);
-
-       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
-
-       psmouse->set_rate = psmouse_set_rate;
-       psmouse->set_resolution = psmouse_set_resolution;
-       psmouse->set_scale = psmouse_set_scale;
-       psmouse->poll = psmouse_poll;
-       psmouse->protocol_handler = psmouse_process_byte;
-       psmouse->pktsize = 3;
-       psmouse->reconnect = NULL;
-       psmouse->disconnect = NULL;
-       psmouse->cleanup = NULL;
-       psmouse->pt_activate = NULL;
-       psmouse->pt_deactivate = NULL;
-}
-
-/*
- * Apply default settings to the psmouse structure and call specified
- * protocol detection or initialization routine.
- */
-static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse,
-                                          bool set_properties),
-                            struct psmouse *psmouse, bool set_properties)
-{
-       if (set_properties)
-               psmouse_apply_defaults(psmouse);
-
-       return detect(psmouse, set_properties);
-}
-
-/*
- * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
- * the mouse may have.
- */
-
-static int psmouse_extensions(struct psmouse *psmouse,
-                             unsigned int max_proto, bool set_properties)
-{
-       bool synaptics_hardware = false;
-
-/* Always check for focaltech, this is safe as it uses pnp-id matching */
-       if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
-               if (max_proto > PSMOUSE_IMEX) {
-                       if (!set_properties || focaltech_init(psmouse) == 0) {
-                               if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH))
-                                       return PSMOUSE_FOCALTECH;
-                               /*
-                                * Note that we need to also restrict
-                                * psmouse_max_proto so that psmouse_initialize()
-                                * does not try to reset rate and resolution,
-                                * because even that upsets the device.
-                                */
-                               psmouse_max_proto = PSMOUSE_PS2;
-                               return PSMOUSE_PS2;
-                       }
-               }
-       }
-
-/*
- * We always check for lifebook because it does not disturb mouse
- * (it only checks DMI information).
- */
-       if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) {
-               if (max_proto > PSMOUSE_IMEX) {
-                       if (!set_properties || lifebook_init(psmouse) == 0)
-                               return PSMOUSE_LIFEBOOK;
-               }
-       }
-
-       if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) {
-               if (max_proto > PSMOUSE_IMEX) {
-                       if (!set_properties || vmmouse_init(psmouse) == 0)
-                               return PSMOUSE_VMMOUSE;
-               }
-       }
-
-/*
- * Try Kensington ThinkingMouse (we try first, because synaptics probe
- * upsets the thinkingmouse).
- */
-
-       if (max_proto > PSMOUSE_IMEX &&
-           psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) {
-               return PSMOUSE_THINKPS;
-       }
-
-/*
- * Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
- * support is disabled in config - we need to know if it is synaptics so we
- * can reset it properly after probing for intellimouse.
- */
-       if (max_proto > PSMOUSE_PS2 &&
-           psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) {
-               synaptics_hardware = true;
-
-               if (max_proto > PSMOUSE_IMEX) {
-/*
- * Try activating protocol, but check if support is enabled first, since
- * we try detecting Synaptics even when protocol is disabled.
- */
-                       if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
-                           (!set_properties || synaptics_init(psmouse) == 0)) {
-                               return PSMOUSE_SYNAPTICS;
-                       }
-
-/*
- * Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
- * Unfortunately Logitech/Genius probes confuse some firmware versions so
- * we'll have to skip them.
- */
-                       max_proto = PSMOUSE_IMEX;
-               }
-/*
- * Make sure that touchpad is in relative mode, gestures (taps) are enabled
- */
-               synaptics_reset(psmouse);
-       }
-
-/*
- * Try Cypress Trackpad.
- * Must try it before Finger Sensing Pad because Finger Sensing Pad probe
- * upsets some modules of Cypress Trackpads.
- */
-       if (max_proto > PSMOUSE_IMEX &&
-                       cypress_detect(psmouse, set_properties) == 0) {
-               if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) {
-                       if (cypress_init(psmouse) == 0)
-                               return PSMOUSE_CYPRESS;
-
-                       /*
-                        * Finger Sensing Pad probe upsets some modules of
-                        * Cypress Trackpad, must avoid Finger Sensing Pad
-                        * probe if Cypress Trackpad device detected.
-                        */
-                       return PSMOUSE_PS2;
-               }
-
-               max_proto = PSMOUSE_IMEX;
-       }
-
-/*
- * Try ALPS TouchPad
- */
-       if (max_proto > PSMOUSE_IMEX) {
-               ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-               if (psmouse_do_detect(alps_detect,
-                                     psmouse, set_properties) == 0) {
-                       if (!set_properties || alps_init(psmouse) == 0)
-                               return PSMOUSE_ALPS;
-/*
- * Init failed, try basic relative protocols
- */
-                       max_proto = PSMOUSE_IMEX;
-               }
-       }
-
-/*
- * Try OLPC HGPK touchpad.
- */
-       if (max_proto > PSMOUSE_IMEX &&
-           psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) {
-               if (!set_properties || hgpk_init(psmouse) == 0)
-                       return PSMOUSE_HGPK;
-/*
- * Init failed, try basic relative protocols
- */
-               max_proto = PSMOUSE_IMEX;
-       }
-
-/*
- * Try Elantech touchpad.
- */
-       if (max_proto > PSMOUSE_IMEX &&
-           psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) {
-               if (!set_properties || elantech_init(psmouse) == 0)
-                       return PSMOUSE_ELANTECH;
-/*
- * Init failed, try basic relative protocols
- */
-               max_proto = PSMOUSE_IMEX;
-       }
-
-       if (max_proto > PSMOUSE_IMEX) {
-               if (psmouse_do_detect(genius_detect,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_GENPS;
-
-               if (psmouse_do_detect(ps2pp_init,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_PS2PP;
-
-               if (psmouse_do_detect(trackpoint_detect,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_TRACKPOINT;
-
-               if (psmouse_do_detect(touchkit_ps2_detect,
-                                     psmouse, set_properties) == 0)
-                       return PSMOUSE_TOUCHKIT_PS2;
-       }
-
-/*
- * Try Finger Sensing Pad. We do it here because its probe upsets
- * Trackpoint devices (causing TP_READ_ID command to time out).
- */
-       if (max_proto > PSMOUSE_IMEX) {
-               if (psmouse_do_detect(fsp_detect,
-                                     psmouse, set_properties) == 0) {
-                       if (!set_properties || fsp_init(psmouse) == 0)
-                               return PSMOUSE_FSP;
-/*
- * Init failed, try basic relative protocols
- */
-                       max_proto = PSMOUSE_IMEX;
-               }
-       }
-
-/*
- * Reset to defaults in case the device got confused by extended
- * protocol probes. Note that we follow up with full reset because
- * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
- */
-       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
-       psmouse_reset(psmouse);
-
-       if (max_proto >= PSMOUSE_IMEX &&
-           psmouse_do_detect(im_explorer_detect,
-                             psmouse, set_properties) == 0) {
-               return PSMOUSE_IMEX;
-       }
-
-       if (max_proto >= PSMOUSE_IMPS &&
-           psmouse_do_detect(intellimouse_detect,
-                             psmouse, set_properties) == 0) {
-               return PSMOUSE_IMPS;
-       }
-
-/*
- * Okay, all failed, we have a standard mouse here. The number of the buttons
- * is still a question, though. We assume 3.
- */
-       psmouse_do_detect(ps2bare_detect, psmouse, set_properties);
-
-       if (synaptics_hardware) {
-/*
- * We detected Synaptics hardware but it did not respond to IMPS/2 probes.
- * We need to reset the touchpad because if there is a track point on the
- * pass through port it could get disabled while probing for protocol
- * extensions.
- */
-               psmouse_reset(psmouse);
-       }
-
-       return PSMOUSE_PS2;
-}
-
 static const struct psmouse_protocol psmouse_protocols[] = {
        {
                .type           = PSMOUSE_PS2,
@@ -985,13 +692,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .maxproto       = true,
                .ignore_parity  = true,
                .detect         = ps2bare_detect,
+               .try_passthru   = true,
        },
 #ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
        {
                .type           = PSMOUSE_PS2PP,
                .name           = "PS2++",
                .alias          = "logitech",
-               .detect         = ps2pp_init,
+               .detect         = ps2pp_detect,
        },
 #endif
        {
@@ -1022,6 +730,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .maxproto       = true,
                .ignore_parity  = true,
                .detect         = intellimouse_detect,
+               .try_passthru   = true,
        },
        {
                .type           = PSMOUSE_IMEX,
@@ -1030,6 +739,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .maxproto       = true,
                .ignore_parity  = true,
                .detect         = im_explorer_detect,
+               .try_passthru   = true,
        },
 #ifdef CONFIG_MOUSE_PS2_SYNAPTICS
        {
@@ -1061,6 +771,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .type           = PSMOUSE_LIFEBOOK,
                .name           = "LBPS/2",
                .alias          = "lifebook",
+               .detect         = lifebook_detect,
                .init           = lifebook_init,
        },
 #endif
@@ -1070,6 +781,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .name           = "TPPS/2",
                .alias          = "trackpoint",
                .detect         = trackpoint_detect,
+               .try_passthru   = true,
        },
 #endif
 #ifdef CONFIG_MOUSE_PS2_TOUCHKIT
@@ -1138,7 +850,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
        },
 };
 
-static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type)
 {
        int i;
 
@@ -1146,6 +858,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type
                if (psmouse_protocols[i].type == type)
                        return &psmouse_protocols[i];
 
+       return NULL;
+}
+
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+{
+       const struct psmouse_protocol *proto;
+
+       proto = __psmouse_protocol_by_type(type);
+       if (proto)
+               return proto;
+
        WARN_ON(1);
        return &psmouse_protocols[0];
 }
@@ -1166,23 +889,288 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name,
        return NULL;
 }
 
+/*
+ * Apply default settings to the psmouse structure. Most of them will
+ * be overridden by individual protocol initialization routines.
+ */
+static void psmouse_apply_defaults(struct psmouse *psmouse)
+{
+       struct input_dev *input_dev = psmouse->dev;
+
+       memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
+       memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
+       memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
+       memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
+       memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
+
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_REL, input_dev->evbit);
+
+       __set_bit(BTN_LEFT, input_dev->keybit);
+       __set_bit(BTN_RIGHT, input_dev->keybit);
+
+       __set_bit(REL_X, input_dev->relbit);
+       __set_bit(REL_Y, input_dev->relbit);
+
+       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
+
+       psmouse->set_rate = psmouse_set_rate;
+       psmouse->set_resolution = psmouse_set_resolution;
+       psmouse->set_scale = psmouse_set_scale;
+       psmouse->poll = psmouse_poll;
+       psmouse->protocol_handler = psmouse_process_byte;
+       psmouse->pktsize = 3;
+       psmouse->reconnect = NULL;
+       psmouse->disconnect = NULL;
+       psmouse->cleanup = NULL;
+       psmouse->pt_activate = NULL;
+       psmouse->pt_deactivate = NULL;
+}
+
+static bool psmouse_try_protocol(struct psmouse *psmouse,
+                                enum psmouse_type type,
+                                unsigned int *max_proto,
+                                bool set_properties, bool init_allowed)
+{
+       const struct psmouse_protocol *proto;
+
+       proto = __psmouse_protocol_by_type(type);
+       if (!proto)
+               return false;
+
+       if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU &&
+           !proto->try_passthru) {
+               return false;
+       }
+
+       if (set_properties)
+               psmouse_apply_defaults(psmouse);
+
+       if (proto->detect(psmouse, set_properties) != 0)
+               return false;
+
+       if (set_properties && proto->init && init_allowed) {
+               if (proto->init(psmouse) != 0) {
+                       /*
+                        * We detected device, but init failed. Adjust
+                        * max_proto so we only try standard protocols.
+                        */
+                       if (*max_proto > PSMOUSE_IMEX)
+                               *max_proto = PSMOUSE_IMEX;
+
+                       return false;
+               }
+       }
+
+       return true;
+}
 
 /*
- * psmouse_probe() probes for a PS/2 mouse.
+ * psmouse_extensions() probes for any extensions to the basic PS/2 protocol
+ * the mouse may have.
  */
+static int psmouse_extensions(struct psmouse *psmouse,
+                             unsigned int max_proto, bool set_properties)
+{
+       bool synaptics_hardware = false;
+
+       /*
+        * Always check for focaltech, this is safe as it uses pnp-id
+        * matching.
+        */
+       if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH,
+                                &max_proto, set_properties, false)) {
+               if (max_proto > PSMOUSE_IMEX &&
+                   IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
+                   (!set_properties || focaltech_init(psmouse) == 0)) {
+                       return PSMOUSE_FOCALTECH;
+               }
+               /*
+                * Restrict psmouse_max_proto so that psmouse_initialize()
+                * does not try to reset rate and resolution, because even
+                * that upsets the device.
+                * This also causes us to basically fall through to basic
+                * protocol detection, where we fully reset the mouse,
+                * and set it up as bare PS/2 protocol device.
+                */
+               psmouse_max_proto = max_proto = PSMOUSE_PS2;
+       }
+
+       /*
+        * We always check for LifeBook because it does not disturb mouse
+        * (it only checks DMI information).
+        */
+       if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
+                                set_properties, max_proto > PSMOUSE_IMEX))
+               return PSMOUSE_LIFEBOOK;
 
+       if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
+                                set_properties, max_proto > PSMOUSE_IMEX))
+               return PSMOUSE_VMMOUSE;
+
+       /*
+        * Try Kensington ThinkingMouse (we try first, because Synaptics
+        * probe upsets the ThinkingMouse).
+        */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
+                                set_properties, true)) {
+               return PSMOUSE_THINKPS;
+       }
+
+       /*
+        * Try Synaptics TouchPad. Note that probing is done even if
+        * Synaptics protocol support is disabled in config - we need to
+        * know if it is Synaptics so we can reset it properly after
+        * probing for IntelliMouse.
+        */
+       if (max_proto > PSMOUSE_PS2 &&
+           psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto,
+                                set_properties, false)) {
+               synaptics_hardware = true;
+
+               if (max_proto > PSMOUSE_IMEX) {
+                       /*
+                        * Try activating protocol, but check if support is
+                        * enabled first, since we try detecting Synaptics
+                        * even when protocol is disabled.
+                        */
+                       if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
+                           (!set_properties || synaptics_init(psmouse) == 0)) {
+                               return PSMOUSE_SYNAPTICS;
+                       }
+
+                       /*
+                        * Some Synaptics touchpads can emulate extended
+                        * protocols (like IMPS/2).  Unfortunately
+                        * Logitech/Genius probes confuse some firmware
+                        * versions so we'll have to skip them.
+                        */
+                       max_proto = PSMOUSE_IMEX;
+               }
+
+               /*
+                * Make sure that touchpad is in relative mode, gestures
+                * (taps) are enabled.
+                */
+               synaptics_reset(psmouse);
+       }
+
+       /*
+        * Try Cypress Trackpad. We must try it before Finger Sensing Pad
+        * because Finger Sensing Pad probe upsets some modules of Cypress
+        * Trackpads.
+        */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
+                                set_properties, true)) {
+               return PSMOUSE_CYPRESS;
+       }
+
+       /* Try ALPS TouchPad */
+       if (max_proto > PSMOUSE_IMEX) {
+               ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+               if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_ALPS;
+       }
+
+       /* Try OLPC HGPK touchpad */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto,
+                                set_properties, true)) {
+               return PSMOUSE_HGPK;
+       }
+
+       /* Try Elantech touchpad */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_ELANTECH;
+       }
+
+       if (max_proto > PSMOUSE_IMEX) {
+               if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_GENPS;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_PS2PP;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_TRACKPOINT;
+
+               if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
+                                        &max_proto, set_properties, true))
+                       return PSMOUSE_TOUCHKIT_PS2;
+       }
+
+       /*
+        * Try Finger Sensing Pad. We do it here because its probe upsets
+        * Trackpoint devices (causing TP_READ_ID command to time out).
+        */
+       if (max_proto > PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_FSP,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_FSP;
+       }
+
+       /*
+        * Reset to defaults in case the device got confused by extended
+        * protocol probes. Note that we follow up with full reset because
+        * some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
+        */
+       ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
+       psmouse_reset(psmouse);
+
+       if (max_proto >= PSMOUSE_IMEX &&
+           psmouse_try_protocol(psmouse, PSMOUSE_IMEX,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_IMEX;
+       }
+
+       if (max_proto >= PSMOUSE_IMPS &&
+           psmouse_try_protocol(psmouse, PSMOUSE_IMPS,
+                                &max_proto, set_properties, true)) {
+               return PSMOUSE_IMPS;
+       }
+
+       /*
+        * Okay, all failed, we have a standard mouse here. The number of
+        * the buttons is still a question, though. We assume 3.
+        */
+       psmouse_try_protocol(psmouse, PSMOUSE_PS2,
+                            &max_proto, set_properties, true);
+
+       if (synaptics_hardware) {
+               /*
+                * We detected Synaptics hardware but it did not respond to
+                * IMPS/2 probes.  We need to reset the touchpad because if
+                * there is a track point on the pass through port it could
+                * get disabled while probing for protocol extensions.
+                */
+               psmouse_reset(psmouse);
+       }
+
+       return PSMOUSE_PS2;
+}
+
+/*
+ * psmouse_probe() probes for a PS/2 mouse.
+ */
 static int psmouse_probe(struct psmouse *psmouse)
 {
        struct ps2dev *ps2dev = &psmouse->ps2dev;
        unsigned char param[2];
 
-/*
- * First, we check if it's a mouse. It should send 0x00 or 0x03
- * in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
- * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent
- * ID queries, probably due to a firmware bug.
- */
-
+       /*
+        * First, we check if it's a mouse. It should send 0x00 or 0x03 in
+        * case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
+        * Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and
+        * subsequent ID queries, probably due to a firmware bug.
+        */
        param[0] = 0xa5;
        if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
                return -1;
@@ -1191,10 +1179,10 @@ static int psmouse_probe(struct psmouse *psmouse)
            param[0] != 0x04 && param[0] != 0xff)
                return -1;
 
-/*
- * Then we reset and disable the mouse so that it doesn't generate events.
- */
-
+       /*
+        * Then we reset and disable the mouse so that it doesn't generate
+        * events.
+        */
        if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
                psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
                             ps2dev->serio->phys);
@@ -1205,13 +1193,11 @@ static int psmouse_probe(struct psmouse *psmouse)
 /*
  * psmouse_initialize() initializes the mouse to a sane state.
  */
-
 static void psmouse_initialize(struct psmouse *psmouse)
 {
-/*
- * We set the mouse report rate, resolution and scaling.
- */
-
+       /*
+        * We set the mouse report rate, resolution and scaling.
+        */
        if (psmouse_max_proto != PSMOUSE_PS2) {
                psmouse->set_rate(psmouse, psmouse->rate);
                psmouse->set_resolution(psmouse, psmouse->resolution);
@@ -1222,7 +1208,6 @@ static void psmouse_initialize(struct psmouse *psmouse)
 /*
  * psmouse_activate() enables the mouse so that we get motion reports from it.
  */
-
 int psmouse_activate(struct psmouse *psmouse)
 {
        if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
@@ -1236,10 +1221,9 @@ int psmouse_activate(struct psmouse *psmouse)
 }
 
 /*
- * psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
- * reports from it unless we explicitly request it.
+ * psmouse_deactivate() puts the mouse into poll mode so that we don't get
+ * motion reports from it unless we explicitly request it.
  */
-
 int psmouse_deactivate(struct psmouse *psmouse)
 {
        if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
@@ -1252,11 +1236,9 @@ int psmouse_deactivate(struct psmouse *psmouse)
        return 0;
 }
 
-
 /*
  * psmouse_resync() attempts to re-validate current protocol.
  */
-
 static void psmouse_resync(struct work_struct *work)
 {
        struct psmouse *parent = NULL, *psmouse =
@@ -1276,16 +1258,16 @@ static void psmouse_resync(struct work_struct *work)
                psmouse_deactivate(parent);
        }
 
-/*
- * Some mice don't ACK commands sent while they are in the middle of
- * transmitting motion packet. To avoid delay we use ps2_sendbyte()
- * instead of ps2_command() which would wait for 200ms for an ACK
- * that may never come.
- * As an additional quirk ALPS touchpads may not only forget to ACK
- * disable command but will stop reporting taps, so if we see that
- * mouse at least once ACKs disable we will do full reconnect if ACK
- * is missing.
- */
+       /*
       * Some mice don't ACK commands sent while they are in the middle of
       * transmitting motion packet. To avoid delay we use ps2_sendbyte()
       * instead of ps2_command() which would wait for 200ms for an ACK
       * that may never come.
       * As an additional quirk ALPS touchpads may not only forget to ACK
       * disable command but will stop reporting taps, so if we see that
       * mouse at least once ACKs disable we will do full reconnect if ACK
       * is missing.
       */
        psmouse->num_resyncs++;
 
        if (ps2_sendbyte(&psmouse->ps2dev, PSMOUSE_CMD_DISABLE, 20)) {
@@ -1294,13 +1276,13 @@ static void psmouse_resync(struct work_struct *work)
        } else
                psmouse->acks_disable_command = true;
 
-/*
- * Poll the mouse. If it was reset the packet will be shorter than
- * psmouse->pktsize and ps2_command will fail. We do not expect and
- * do not handle scenario when mouse "upgrades" its protocol while
- * disconnected since it would require additional delay. If we ever
- * see a mouse that does it we'll adjust the code.
- */
+       /*
       * Poll the mouse. If it was reset the packet will be shorter than
       * psmouse->pktsize and ps2_command will fail. We do not expect and
       * do not handle scenario when mouse "upgrades" its protocol while
       * disconnected since it would require additional delay. If we ever
       * see a mouse that does it we'll adjust the code.
       */
        if (!failed) {
                if (psmouse->poll(psmouse))
                        failed = true;
@@ -1317,11 +1299,12 @@ static void psmouse_resync(struct work_struct *work)
                        psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
                }
        }
-/*
- * Now try to enable mouse. We try to do that even if poll failed and also
- * repeat our attempts 5 times, otherwise we may be left out with disabled
- * mouse.
- */
+
+       /*
+        * Now try to enable mouse. We try to do that even if poll failed
+        * and also repeat our attempts 5 times, otherwise we may be left
+        * out with disabled mouse.
+        */
        for (i = 0; i < 5; i++) {
                if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
                        enabled = true;
@@ -1353,7 +1336,6 @@ static void psmouse_resync(struct work_struct *work)
 /*
  * psmouse_cleanup() resets the mouse into power-on state.
  */
-
 static void psmouse_cleanup(struct serio *serio)
 {
        struct psmouse *psmouse = serio_get_drvdata(serio);
@@ -1378,15 +1360,15 @@ static void psmouse_cleanup(struct serio *serio)
        if (psmouse->cleanup)
                psmouse->cleanup(psmouse);
 
-/*
- * Reset the mouse to defaults (bare PS/2 protocol).
- */
+       /*
       * Reset the mouse to defaults (bare PS/2 protocol).
       */
        ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
 
-/*
- * Some boxes, such as HP nx7400, get terribly confused if mouse
- * is not fully enabled before suspending/shutting down.
- */
+       /*
       * Some boxes, such as HP nx7400, get terribly confused if mouse
       * is not fully enabled before suspending/shutting down.
       */
        ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
 
        if (parent) {
@@ -1402,7 +1384,6 @@ static void psmouse_cleanup(struct serio *serio)
 /*
  * psmouse_disconnect() closes and frees.
  */
-
 static void psmouse_disconnect(struct serio *serio)
 {
        struct psmouse *psmouse, *parent = NULL;
@@ -1602,7 +1583,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
        goto out;
 }
 
-
 static int psmouse_reconnect(struct serio *serio)
 {
        struct psmouse *psmouse = serio_get_drvdata(serio);
index c11556563ef0633f746edc9bc9e3ebca80879e8b..68f5f4a0f1e72f10b35e45243218e6cfbf3d586e 100644 (file)
@@ -257,6 +257,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
                },
        },
+       {
+               /* Fujitsu Lifebook U745 */
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
+               },
+       },
        {
                /* Fujitsu T70H */
                .matches = {
index ae33da7ab51ff7f5f84464366be1c4b9f8030527..53a97b379c9f0816d295fe8290af79d4b5732fad 100644 (file)
@@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX
          To compile this driver as a module, choose M here: the
          module will be called egalax_ts.
 
+config TOUCHSCREEN_EGALAX_SERIAL
+       tristate "EETI eGalax serial touchscreen"
+       select SERIO
+       help
+         Say Y here to enable support for serial connected EETI
+         eGalax touch panels.
+
+         To compile this driver as a module, choose M here: the
+         module will be called egalax_ts_serial.
+
 config TOUCHSCREEN_FT6236
        tristate "FT6236 I2C touchscreen"
        depends on I2C
@@ -324,6 +334,7 @@ config TOUCHSCREEN_FUJITSU
 config TOUCHSCREEN_GOODIX
        tristate "Goodix I2C touchscreen"
        depends on I2C
+       depends on GPIOLIB
        help
          Say Y here if you have the Goodix touchscreen (such as one
          installed in Onda v975w tablets) connected to your
@@ -927,6 +938,22 @@ config TOUCHSCREEN_TOUCHIT213
          To compile this driver as a module, choose M here: the
          module will be called touchit213.
 
+config TOUCHSCREEN_TS4800
+       tristate "TS-4800 touchscreen"
+       depends on HAS_IOMEM && OF
+       select MFD_SYSCON
+       select INPUT_POLLDEV
+       help
+         Say Y here if you have a touchscreen on a TS-4800 board.
+
+         On TS-4800, the touchscreen is not handled directly by Linux but by
+         a companion FPGA.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ts4800_ts.
+
 config TOUCHSCREEN_TSC_SERIO
        tristate "TSC-10/25/40 serial touchscreen support"
        select SERIO
index cbaa6abb08dae6c58ea1125b0fc0baa335dbc444..968ff12e31325bec720c38608864a7af58c75d8d 100644 (file)
@@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI)                += eeti_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ELAN)         += elants_i2c.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)          += elo.o
 obj-$(CONFIG_TOUCHSCREEN_EGALAX)       += egalax_ts.o
+obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL)        += egalax_ts_serial.o
 obj-$(CONFIG_TOUCHSCREEN_FT6236)       += ft6236.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)      += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_GOODIX)       += goodix.o
@@ -68,6 +69,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC)       += ti_am335x_tsc.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
+obj-$(CONFIG_TOUCHSCREEN_TS4800)       += ts4800-ts.o
 obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO)    += tsc40.o
 obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2004)      += tsc2004.o
diff --git a/drivers/input/touchscreen/egalax_ts_serial.c b/drivers/input/touchscreen/egalax_ts_serial.c
new file mode 100644 (file)
index 0000000..657bbae
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * EETI Egalax serial touchscreen driver
+ *
+ * Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu>
+ *
+ * based on the
+ *
+ * Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett)
+ */
+
+/*
+ * 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/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC    "EETI Egalax serial touchscreen driver"
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define EGALAX_FORMAT_MAX_LENGTH       6
+#define EGALAX_FORMAT_START_BIT                BIT(7)
+#define EGALAX_FORMAT_PRESSURE_BIT     BIT(6)
+#define EGALAX_FORMAT_TOUCH_BIT                BIT(0)
+#define EGALAX_FORMAT_RESOLUTION_MASK  0x06
+
+#define EGALAX_MIN_XC                  0
+#define EGALAX_MAX_XC                  0x4000
+#define EGALAX_MIN_YC                  0
+#define EGALAX_MAX_YC                  0x4000
+
+/*
+ * Per-touchscreen data.
+ */
+struct egalax {
+       struct input_dev *input;
+       struct serio *serio;
+       int idx;
+       u8 data[EGALAX_FORMAT_MAX_LENGTH];
+       char phys[32];
+};
+
+static void egalax_process_data(struct egalax *egalax)
+{
+       struct input_dev *dev = egalax->input;
+       u8 *data = egalax->data;
+       u16 x, y;
+       u8 shift;
+       u8 mask;
+
+       shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1);
+       mask = 0xff >> (shift + 1);
+
+       x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift;
+       y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift;
+
+       input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT);
+       input_report_abs(dev, ABS_X, x);
+       input_report_abs(dev, ABS_Y, y);
+       input_sync(dev);
+}
+
+static irqreturn_t egalax_interrupt(struct serio *serio,
+                                   unsigned char data, unsigned int flags)
+{
+       struct egalax *egalax = serio_get_drvdata(serio);
+       int pkt_len;
+
+       egalax->data[egalax->idx++] = data;
+
+       if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) {
+               pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5;
+               if (pkt_len == egalax->idx) {
+                       egalax_process_data(egalax);
+                       egalax->idx = 0;
+               }
+       } else {
+               dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
+                       egalax->data[0]);
+               egalax->idx = 0;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * egalax_connect() is the routine that is called when someone adds a
+ * new serio device that supports egalax protocol and registers it as
+ * an input device. This is usually accomplished using inputattach.
+ */
+static int egalax_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct egalax *egalax;
+       struct input_dev *input_dev;
+       int error;
+
+       egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!egalax || !input_dev) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       egalax->serio = serio;
+       egalax->input = input_dev;
+       snprintf(egalax->phys, sizeof(egalax->phys),
+                "%s/input0", serio->phys);
+
+       input_dev->name = "EETI eGalaxTouch Serial TouchScreen";
+       input_dev->phys = egalax->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_EGALAX;
+       input_dev->id.product = 0;
+       input_dev->id.version = 0x0001;
+       input_dev->dev.parent = &serio->dev;
+
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(input_dev, ABS_X,
+                            EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y,
+                            EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0);
+
+       serio_set_drvdata(serio, egalax);
+
+       error = serio_open(serio, drv);
+       if (error)
+               goto err_reset_drvdata;
+
+       error = input_register_device(input_dev);
+       if (error)
+               goto err_close_serio;
+
+       return 0;
+
+err_close_serio:
+       serio_close(serio);
+err_reset_drvdata:
+       serio_set_drvdata(serio, NULL);
+err_free_mem:
+       input_free_device(input_dev);
+       kfree(egalax);
+       return error;
+}
+
+static void egalax_disconnect(struct serio *serio)
+{
+       struct egalax *egalax = serio_get_drvdata(serio);
+
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_unregister_device(egalax->input);
+       kfree(egalax);
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static const struct serio_device_id egalax_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_EGALAX,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, egalax_serio_ids);
+
+static struct serio_driver egalax_drv = {
+       .driver         = {
+               .name   = "egalax",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = egalax_serio_ids,
+       .interrupt      = egalax_interrupt,
+       .connect        = egalax_connect,
+       .disconnect     = egalax_disconnect,
+};
+module_serio_driver(egalax_drv);
+
+MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
index 4d113c9e4b77103224924128d1d033af21b4e552..240b16f3ee9797c4961709ef8f9a50b874febb3f 100644 (file)
@@ -2,6 +2,7 @@
  *  Driver for Goodix Touchscreens
  *
  *  Copyright (c) 2014 Red Hat Inc.
+ *  Copyright (c) 2015 K. Merker <merker@debian.org>
  *
  *  This code is based on gt9xx.c authored by andrew@goodix.com:
  *
@@ -16,6 +17,8 @@
 
 #include <linux/kernel.h>
 #include <linux/dmi.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/input/mt.h>
@@ -33,11 +36,24 @@ struct goodix_ts_data {
        struct input_dev *input_dev;
        int abs_x_max;
        int abs_y_max;
+       bool swapped_x_y;
+       bool inverted_x;
+       bool inverted_y;
        unsigned int max_touch_num;
        unsigned int int_trigger_type;
-       bool rotated_screen;
+       int cfg_len;
+       struct gpio_desc *gpiod_int;
+       struct gpio_desc *gpiod_rst;
+       u16 id;
+       u16 version;
+       const char *cfg_name;
+       struct completion firmware_loading_complete;
+       unsigned long irq_flags;
 };
 
+#define GOODIX_GPIO_INT_NAME           "irq"
+#define GOODIX_GPIO_RST_NAME           "reset"
+
 #define GOODIX_MAX_HEIGHT              4096
 #define GOODIX_MAX_WIDTH               4096
 #define GOODIX_INT_TRIGGER             1
@@ -45,8 +61,13 @@ struct goodix_ts_data {
 #define GOODIX_MAX_CONTACTS            10
 
 #define GOODIX_CONFIG_MAX_LENGTH       240
+#define GOODIX_CONFIG_911_LENGTH       186
+#define GOODIX_CONFIG_967_LENGTH       228
 
 /* Register defines */
+#define GOODIX_REG_COMMAND             0x8040
+#define GOODIX_CMD_SCREEN_OFF          0x05
+
 #define GOODIX_READ_COOR_ADDR          0x814E
 #define GOODIX_REG_CONFIG_DATA         0x8047
 #define GOODIX_REG_ID                  0x8140
@@ -115,6 +136,63 @@ static int goodix_i2c_read(struct i2c_client *client,
        return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
 }
 
+/**
+ * goodix_i2c_write - write data to a register of the i2c slave device.
+ *
+ * @client: i2c device.
+ * @reg: the register to write to.
+ * @buf: raw data buffer to write.
+ * @len: length of the buffer to write
+ */
+static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
+                           unsigned len)
+{
+       u8 *addr_buf;
+       struct i2c_msg msg;
+       int ret;
+
+       addr_buf = kmalloc(len + 2, GFP_KERNEL);
+       if (!addr_buf)
+               return -ENOMEM;
+
+       addr_buf[0] = reg >> 8;
+       addr_buf[1] = reg & 0xFF;
+       memcpy(&addr_buf[2], buf, len);
+
+       msg.flags = 0;
+       msg.addr = client->addr;
+       msg.buf = addr_buf;
+       msg.len = len + 2;
+
+       ret = i2c_transfer(client->adapter, &msg, 1);
+       kfree(addr_buf);
+       return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
+}
+
+static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
+{
+       return goodix_i2c_write(client, reg, &value, sizeof(value));
+}
+
+static int goodix_get_cfg_len(u16 id)
+{
+       switch (id) {
+       case 911:
+       case 9271:
+       case 9110:
+       case 927:
+       case 928:
+               return GOODIX_CONFIG_911_LENGTH;
+
+       case 912:
+       case 967:
+               return GOODIX_CONFIG_967_LENGTH;
+
+       default:
+               return GOODIX_CONFIG_MAX_LENGTH;
+       }
+}
+
 static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
 {
        int touch_num;
@@ -155,10 +233,13 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
        int input_y = get_unaligned_le16(&coor_data[3]);
        int input_w = get_unaligned_le16(&coor_data[5]);
 
-       if (ts->rotated_screen) {
+       /* Inversions have to happen before axis swapping */
+       if (ts->inverted_x)
                input_x = ts->abs_x_max - input_x;
+       if (ts->inverted_y)
                input_y = ts->abs_y_max - input_y;
-       }
+       if (ts->swapped_x_y)
+               swap(input_x, input_y);
 
        input_mt_slot(ts->input_dev, id);
        input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
@@ -202,21 +283,195 @@ static void goodix_process_events(struct goodix_ts_data *ts)
  */
 static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
 {
-       static const u8 end_cmd[] = {
-               GOODIX_READ_COOR_ADDR >> 8,
-               GOODIX_READ_COOR_ADDR & 0xff,
-               0
-       };
        struct goodix_ts_data *ts = dev_id;
 
        goodix_process_events(ts);
 
-       if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
+       if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)
                dev_err(&ts->client->dev, "I2C write end_cmd error\n");
 
        return IRQ_HANDLED;
 }
 
+static void goodix_free_irq(struct goodix_ts_data *ts)
+{
+       devm_free_irq(&ts->client->dev, ts->client->irq, ts);
+}
+
+static int goodix_request_irq(struct goodix_ts_data *ts)
+{
+       return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
+                                        NULL, goodix_ts_irq_handler,
+                                        ts->irq_flags, ts->client->name, ts);
+}
+
+/**
+ * goodix_check_cfg - Checks if config fw is valid
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: firmware config data
+ */
+static int goodix_check_cfg(struct goodix_ts_data *ts,
+                           const struct firmware *cfg)
+{
+       int i, raw_cfg_len;
+       u8 check_sum = 0;
+
+       if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
+               dev_err(&ts->client->dev,
+                       "The length of the config fw is not correct");
+               return -EINVAL;
+       }
+
+       raw_cfg_len = cfg->size - 2;
+       for (i = 0; i < raw_cfg_len; i++)
+               check_sum += cfg->data[i];
+       check_sum = (~check_sum) + 1;
+       if (check_sum != cfg->data[raw_cfg_len]) {
+               dev_err(&ts->client->dev,
+                       "The checksum of the config fw is not correct");
+               return -EINVAL;
+       }
+
+       if (cfg->data[raw_cfg_len + 1] != 1) {
+               dev_err(&ts->client->dev,
+                       "Config fw must have Config_Fresh register set");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ * goodix_send_cfg - Write fw config to device
+ *
+ * @ts: goodix_ts_data pointer
+ * @cfg: config firmware to write to device
+ */
+static int goodix_send_cfg(struct goodix_ts_data *ts,
+                          const struct firmware *cfg)
+{
+       int error;
+
+       error = goodix_check_cfg(ts, cfg);
+       if (error)
+               return error;
+
+       error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data,
+                                cfg->size);
+       if (error) {
+               dev_err(&ts->client->dev, "Failed to write config data: %d",
+                       error);
+               return error;
+       }
+       dev_dbg(&ts->client->dev, "Config sent successfully.");
+
+       /* Let the firmware reconfigure itself, so sleep for 10ms */
+       usleep_range(10000, 11000);
+
+       return 0;
+}
+
+static int goodix_int_sync(struct goodix_ts_data *ts)
+{
+       int error;
+
+       error = gpiod_direction_output(ts->gpiod_int, 0);
+       if (error)
+               return error;
+
+       msleep(50);                             /* T5: 50ms */
+
+       error = gpiod_direction_input(ts->gpiod_int);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+/**
+ * goodix_reset - Reset device during power on
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_reset(struct goodix_ts_data *ts)
+{
+       int error;
+
+       /* begin select I2C slave addr */
+       error = gpiod_direction_output(ts->gpiod_rst, 0);
+       if (error)
+               return error;
+
+       msleep(20);                             /* T2: > 10ms */
+
+       /* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
+       error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
+       if (error)
+               return error;
+
+       usleep_range(100, 2000);                /* T3: > 100us */
+
+       error = gpiod_direction_output(ts->gpiod_rst, 1);
+       if (error)
+               return error;
+
+       usleep_range(6000, 10000);              /* T4: > 5ms */
+
+       /* end select I2C slave addr */
+       error = gpiod_direction_input(ts->gpiod_rst);
+       if (error)
+               return error;
+
+       error = goodix_int_sync(ts);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+/**
+ * goodix_get_gpio_config - Get GPIO config from ACPI/DT
+ *
+ * @ts: goodix_ts_data pointer
+ */
+static int goodix_get_gpio_config(struct goodix_ts_data *ts)
+{
+       int error;
+       struct device *dev;
+       struct gpio_desc *gpiod;
+
+       if (!ts->client)
+               return -EINVAL;
+       dev = &ts->client->dev;
+
+       /* Get the interrupt GPIO pin number */
+       gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
+       if (IS_ERR(gpiod)) {
+               error = PTR_ERR(gpiod);
+               if (error != -EPROBE_DEFER)
+                       dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+                               GOODIX_GPIO_INT_NAME, error);
+               return error;
+       }
+
+       ts->gpiod_int = gpiod;
+
+       /* Get the reset line GPIO pin number */
+       gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
+       if (IS_ERR(gpiod)) {
+               error = PTR_ERR(gpiod);
+               if (error != -EPROBE_DEFER)
+                       dev_dbg(dev, "Failed to get %s GPIO: %d\n",
+                               GOODIX_GPIO_RST_NAME, error);
+               return error;
+       }
+
+       ts->gpiod_rst = gpiod;
+
+       return 0;
+}
+
 /**
  * goodix_read_config - Read the embedded configuration of the panel
  *
@@ -230,14 +485,15 @@ static void goodix_read_config(struct goodix_ts_data *ts)
        int error;
 
        error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
-                               config,
-                               GOODIX_CONFIG_MAX_LENGTH);
+                               config, ts->cfg_len);
        if (error) {
                dev_warn(&ts->client->dev,
                         "Error reading config (%d), using defaults\n",
                         error);
                ts->abs_x_max = GOODIX_MAX_WIDTH;
                ts->abs_y_max = GOODIX_MAX_HEIGHT;
+               if (ts->swapped_x_y)
+                       swap(ts->abs_x_max, ts->abs_y_max);
                ts->int_trigger_type = GOODIX_INT_TRIGGER;
                ts->max_touch_num = GOODIX_MAX_CONTACTS;
                return;
@@ -245,6 +501,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
 
        ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
        ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
+       if (ts->swapped_x_y)
+               swap(ts->abs_x_max, ts->abs_y_max);
        ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
        ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
        if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
@@ -252,42 +510,45 @@ static void goodix_read_config(struct goodix_ts_data *ts)
                        "Invalid config, using defaults\n");
                ts->abs_x_max = GOODIX_MAX_WIDTH;
                ts->abs_y_max = GOODIX_MAX_HEIGHT;
+               if (ts->swapped_x_y)
+                       swap(ts->abs_x_max, ts->abs_y_max);
                ts->max_touch_num = GOODIX_MAX_CONTACTS;
        }
 
-       ts->rotated_screen = dmi_check_system(rotated_screen);
-       if (ts->rotated_screen)
+       if (dmi_check_system(rotated_screen)) {
+               ts->inverted_x = true;
+               ts->inverted_y = true;
                dev_dbg(&ts->client->dev,
                         "Applying '180 degrees rotated screen' quirk\n");
+       }
 }
 
 /**
  * goodix_read_version - Read goodix touchscreen version
  *
- * @client: the i2c client
- * @version: output buffer containing the version on success
- * @id: output buffer containing the id on success
+ * @ts: our goodix_ts_data pointer
  */
-static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
+static int goodix_read_version(struct goodix_ts_data *ts)
 {
        int error;
        u8 buf[6];
        char id_str[5];
 
-       error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
+       error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
        if (error) {
-               dev_err(&client->dev, "read version failed: %d\n", error);
+               dev_err(&ts->client->dev, "read version failed: %d\n", error);
                return error;
        }
 
        memcpy(id_str, buf, 4);
        id_str[4] = 0;
-       if (kstrtou16(id_str, 10, id))
-               *id = 0x1001;
+       if (kstrtou16(id_str, 10, &ts->id))
+               ts->id = 0x1001;
 
-       *version = get_unaligned_le16(&buf[4]);
+       ts->version = get_unaligned_le16(&buf[4]);
 
-       dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
+       dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
+                ts->version);
 
        return 0;
 }
@@ -321,13 +582,10 @@ static int goodix_i2c_test(struct i2c_client *client)
  * goodix_request_input_dev - Allocate, populate and register the input device
  *
  * @ts: our goodix_ts_data pointer
- * @version: device firmware version
- * @id: device ID
  *
  * Must be called during probe
  */
-static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
-                                   u16 id)
+static int goodix_request_input_dev(struct goodix_ts_data *ts)
 {
        int error;
 
@@ -351,8 +609,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
        ts->input_dev->phys = "input/ts";
        ts->input_dev->id.bustype = BUS_I2C;
        ts->input_dev->id.vendor = 0x0416;
-       ts->input_dev->id.product = id;
-       ts->input_dev->id.version = version;
+       ts->input_dev->id.product = ts->id;
+       ts->input_dev->id.version = ts->version;
 
        error = input_register_device(ts->input_dev);
        if (error) {
@@ -364,13 +622,75 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
        return 0;
 }
 
+/**
+ * goodix_configure_dev - Finish device initialization
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * Must be called from probe to finish initialization of the device.
+ * Contains the common initialization code for both devices that
+ * declare gpio pins and devices that do not. It is either called
+ * directly from probe or from request_firmware_wait callback.
+ */
+static int goodix_configure_dev(struct goodix_ts_data *ts)
+{
+       int error;
+
+       ts->swapped_x_y = device_property_read_bool(&ts->client->dev,
+                                                   "touchscreen-swapped-x-y");
+       ts->inverted_x = device_property_read_bool(&ts->client->dev,
+                                                  "touchscreen-inverted-x");
+       ts->inverted_y = device_property_read_bool(&ts->client->dev,
+                                                  "touchscreen-inverted-y");
+
+       goodix_read_config(ts);
+
+       error = goodix_request_input_dev(ts);
+       if (error)
+               return error;
+
+       ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
+       error = goodix_request_irq(ts);
+       if (error) {
+               dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+/**
+ * goodix_config_cb - Callback to finish device init
+ *
+ * @ts: our goodix_ts_data pointer
+ *
+ * request_firmware_wait callback that finishes
+ * initialization of the device.
+ */
+static void goodix_config_cb(const struct firmware *cfg, void *ctx)
+{
+       struct goodix_ts_data *ts = ctx;
+       int error;
+
+       if (cfg) {
+               /* send device configuration to the firmware */
+               error = goodix_send_cfg(ts, cfg);
+               if (error)
+                       goto err_release_cfg;
+       }
+
+       goodix_configure_dev(ts);
+
+err_release_cfg:
+       release_firmware(cfg);
+       complete_all(&ts->firmware_loading_complete);
+}
+
 static int goodix_ts_probe(struct i2c_client *client,
                           const struct i2c_device_id *id)
 {
        struct goodix_ts_data *ts;
-       unsigned long irq_flags;
        int error;
-       u16 version_info, id_info;
 
        dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
 
@@ -385,6 +705,20 @@ static int goodix_ts_probe(struct i2c_client *client,
 
        ts->client = client;
        i2c_set_clientdata(client, ts);
+       init_completion(&ts->firmware_loading_complete);
+
+       error = goodix_get_gpio_config(ts);
+       if (error)
+               return error;
+
+       if (ts->gpiod_int && ts->gpiod_rst) {
+               /* reset the controller */
+               error = goodix_reset(ts);
+               if (error) {
+                       dev_err(&client->dev, "Controller reset failed.\n");
+                       return error;
+               }
+       }
 
        error = goodix_i2c_test(client);
        if (error) {
@@ -392,30 +726,125 @@ static int goodix_ts_probe(struct i2c_client *client,
                return error;
        }
 
-       error = goodix_read_version(client, &version_info, &id_info);
+       error = goodix_read_version(ts);
        if (error) {
                dev_err(&client->dev, "Read version failed.\n");
                return error;
        }
 
-       goodix_read_config(ts);
+       ts->cfg_len = goodix_get_cfg_len(ts->id);
+
+       if (ts->gpiod_int && ts->gpiod_rst) {
+               /* update device config */
+               ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
+                                             "goodix_%d_cfg.bin", ts->id);
+               if (!ts->cfg_name)
+                       return -ENOMEM;
+
+               error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
+                                               &client->dev, GFP_KERNEL, ts,
+                                               goodix_config_cb);
+               if (error) {
+                       dev_err(&client->dev,
+                               "Failed to invoke firmware loader: %d\n",
+                               error);
+                       return error;
+               }
 
-       error = goodix_request_input_dev(ts, version_info, id_info);
-       if (error)
-               return error;
+               return 0;
+       } else {
+               error = goodix_configure_dev(ts);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+static int goodix_ts_remove(struct i2c_client *client)
+{
+       struct goodix_ts_data *ts = i2c_get_clientdata(client);
+
+       if (ts->gpiod_int && ts->gpiod_rst)
+               wait_for_completion(&ts->firmware_loading_complete);
+
+       return 0;
+}
+
+static int __maybe_unused goodix_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct goodix_ts_data *ts = i2c_get_clientdata(client);
+       int error;
 
-       irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
-       error = devm_request_threaded_irq(&ts->client->dev, client->irq,
-                                         NULL, goodix_ts_irq_handler,
-                                         irq_flags, client->name, ts);
+       /* We need gpio pins to suspend/resume */
+       if (!ts->gpiod_int || !ts->gpiod_rst)
+               return 0;
+
+       wait_for_completion(&ts->firmware_loading_complete);
+
+       /* Free IRQ as IRQ pin is used as output in the suspend sequence */
+       goodix_free_irq(ts);
+
+       /* Output LOW on the INT pin for 5 ms */
+       error = gpiod_direction_output(ts->gpiod_int, 0);
        if (error) {
-               dev_err(&client->dev, "request IRQ failed: %d\n", error);
+               goodix_request_irq(ts);
                return error;
        }
 
+       usleep_range(5000, 6000);
+
+       error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND,
+                                   GOODIX_CMD_SCREEN_OFF);
+       if (error) {
+               dev_err(&ts->client->dev, "Screen off command failed\n");
+               gpiod_direction_input(ts->gpiod_int);
+               goodix_request_irq(ts);
+               return -EAGAIN;
+       }
+
+       /*
+        * The datasheet specifies that the interval between sending screen-off
+        * command and wake-up should be longer than 58 ms. To avoid waking up
+        * sooner, delay 58ms here.
+        */
+       msleep(58);
+       return 0;
+}
+
+static int __maybe_unused goodix_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct goodix_ts_data *ts = i2c_get_clientdata(client);
+       int error;
+
+       if (!ts->gpiod_int || !ts->gpiod_rst)
+               return 0;
+
+       /*
+        * Exit sleep mode by outputting HIGH level to INT pin
+        * for 2ms~5ms.
+        */
+       error = gpiod_direction_output(ts->gpiod_int, 1);
+       if (error)
+               return error;
+
+       usleep_range(2000, 5000);
+
+       error = goodix_int_sync(ts);
+       if (error)
+               return error;
+
+       error = goodix_request_irq(ts);
+       if (error)
+               return error;
+
        return 0;
 }
 
+static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
+
 static const struct i2c_device_id goodix_ts_id[] = {
        { "GDIX1001:00", 0 },
        { }
@@ -446,11 +875,13 @@ MODULE_DEVICE_TABLE(of, goodix_of_match);
 
 static struct i2c_driver goodix_ts_driver = {
        .probe = goodix_ts_probe,
+       .remove = goodix_ts_remove,
        .id_table = goodix_ts_id,
        .driver = {
                .name = "Goodix-TS",
                .acpi_match_table = ACPI_PTR(goodix_acpi_match),
                .of_match_table = of_match_ptr(goodix_of_match),
+               .pm = &goodix_pm_ops,
        },
 };
 module_i2c_driver(goodix_ts_driver);
index 23a354a392aed7ab259bf3d8f0a53203dbab28b7..0e3fc419a3cf83472ee153b832de88feae01aaff 100644 (file)
@@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2])
 
 static void pcap_ts_work(struct work_struct *work)
 {
-       struct delayed_work *dw = container_of(work, struct delayed_work, work);
+       struct delayed_work *dw = to_delayed_work(work);
        struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
        u8 ch[2];
 
index 4b961ad9f0b51cb905e25c8f272b16378078d5c5..09523a3d3f232a78f4b338d362b2cf5f304f998b 100644 (file)
@@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data {
        struct input_dev *input;
        struct gpio_desc *gpio_attb;
        struct gpio_desc *gpio_reset;
+       struct gpio_desc *gpio_enable;
+       struct gpio_desc *gpio_wake;
        const struct pixcir_i2c_chip_data *chip;
        int max_fingers;        /* Max fingers supported in this instance */
        bool running;
@@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
        struct device *dev = &ts->client->dev;
        int ret;
 
+       if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) {
+               if (ts->gpio_wake)
+                       gpiod_set_value_cansleep(ts->gpio_wake, 1);
+       }
+
        ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
        if (ret < 0) {
                dev_err(dev, "%s: can't read reg 0x%x : %d\n",
@@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
                return ret;
        }
 
+       if (mode == PIXCIR_POWER_HALT) {
+               if (ts->gpio_wake)
+                       gpiod_set_value_cansleep(ts->gpio_wake, 0);
+       }
+
        return 0;
 }
 
@@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts)
        struct device *dev = &ts->client->dev;
        int error;
 
+       if (ts->gpio_enable) {
+               gpiod_set_value_cansleep(ts->gpio_enable, 1);
+               msleep(100);
+       }
+
        /* LEVEL_TOUCH interrupt with active low polarity */
        error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
        if (error) {
@@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
        /* Wait till running ISR is complete */
        synchronize_irq(ts->client->irq);
 
+       if (ts->gpio_enable)
+               gpiod_set_value_cansleep(ts->gpio_enable, 0);
+
        return 0;
 }
 
@@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
                return error;
        }
 
+       tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake",
+                                                   GPIOD_OUT_HIGH);
+       if (IS_ERR(tsdata->gpio_wake)) {
+               error = PTR_ERR(tsdata->gpio_wake);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get wake gpio: %d\n", error);
+               return error;
+       }
+
+       tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable",
+                                                     GPIOD_OUT_HIGH);
+       if (IS_ERR(tsdata->gpio_enable)) {
+               error = PTR_ERR(tsdata->gpio_enable);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get enable gpio: %d\n", error);
+               return error;
+       }
+
+       if (tsdata->gpio_enable)
+               msleep(100);
+
        error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
                                          IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                          client->name, tsdata);
index ba6024f934696c06934264c365f4f6bf930914f0..611156a2ef80d7a1381597d59444083807316bd9 100644 (file)
@@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client,
                        break;
 
                error = -EIO;
-       } while (++retry >= FIRMWARE_RETRY_MAX);
+       } while (++retry <= FIRMWARE_RETRY_MAX);
 
 out:
        error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
index 191a1b87895f69eec25f67369fb110a0cb5b295e..a21a07c3ab6d73d33fd4653f2c99e7766e00f3ce 100644 (file)
@@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev)
        status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
        if (status & IRQENB_HW_PEN) {
                ts_dev->pen_down = true;
-               titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
-               titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
                irqclr |= IRQENB_HW_PEN;
        }
 
diff --git a/drivers/input/touchscreen/ts4800-ts.c b/drivers/input/touchscreen/ts4800-ts.c
new file mode 100644 (file)
index 0000000..3c3dd78
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Touchscreen driver for the TS-4800 board
+ *
+ * Copyright (c) 2015 - Savoir-faire Linux
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/bitops.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* polling interval in ms */
+#define POLL_INTERVAL          3
+
+#define DEBOUNCE_COUNT         1
+
+/* sensor values are 12-bit wide */
+#define MAX_12BIT              ((1 << 12) - 1)
+
+#define PENDOWN_MASK           0x1
+
+#define X_OFFSET               0x0
+#define Y_OFFSET               0x2
+
+struct ts4800_ts {
+       struct input_polled_dev *poll_dev;
+       struct device           *dev;
+       char                    phys[32];
+
+       void __iomem            *base;
+       struct regmap           *regmap;
+       unsigned int            reg;
+       unsigned int            bit;
+
+       bool                    pendown;
+       int                     debounce;
+};
+
+static void ts4800_ts_open(struct input_polled_dev *dev)
+{
+       struct ts4800_ts *ts = dev->private;
+       int ret;
+
+       ts->pendown = false;
+       ts->debounce = DEBOUNCE_COUNT;
+
+       ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
+       if (ret)
+               dev_warn(ts->dev, "Failed to enable touchscreen\n");
+}
+
+static void ts4800_ts_close(struct input_polled_dev *dev)
+{
+       struct ts4800_ts *ts = dev->private;
+       int ret;
+
+       ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
+       if (ret)
+               dev_warn(ts->dev, "Failed to disable touchscreen\n");
+
+}
+
+static void ts4800_ts_poll(struct input_polled_dev *dev)
+{
+       struct input_dev *input_dev = dev->input;
+       struct ts4800_ts *ts = dev->private;
+       u16 last_x = readw(ts->base + X_OFFSET);
+       u16 last_y = readw(ts->base + Y_OFFSET);
+       bool pendown = last_x & PENDOWN_MASK;
+
+       if (pendown) {
+               if (ts->debounce) {
+                       ts->debounce--;
+                       return;
+               }
+
+               if (!ts->pendown) {
+                       input_report_key(input_dev, BTN_TOUCH, 1);
+                       ts->pendown = true;
+               }
+
+               last_x = ((~last_x) >> 4) & MAX_12BIT;
+               last_y = ((~last_y) >> 4) & MAX_12BIT;
+
+               input_report_abs(input_dev, ABS_X, last_x);
+               input_report_abs(input_dev, ABS_Y, last_y);
+               input_sync(input_dev);
+       } else if (ts->pendown) {
+               ts->pendown = false;
+               ts->debounce = DEBOUNCE_COUNT;
+               input_report_key(input_dev, BTN_TOUCH, 0);
+               input_sync(input_dev);
+       }
+}
+
+static int ts4800_parse_dt(struct platform_device *pdev,
+                          struct ts4800_ts *ts)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *syscon_np;
+       u32 reg, bit;
+       int error;
+
+       syscon_np = of_parse_phandle(np, "syscon", 0);
+       if (!syscon_np) {
+               dev_err(dev, "no syscon property\n");
+               return -ENODEV;
+       }
+
+       error = of_property_read_u32_index(np, "syscon", 1, &reg);
+       if (error < 0) {
+               dev_err(dev, "no offset in syscon\n");
+               return error;
+       }
+
+       ts->reg = reg;
+
+       error = of_property_read_u32_index(np, "syscon", 2, &bit);
+       if (error < 0) {
+               dev_err(dev, "no bit in syscon\n");
+               return error;
+       }
+
+       ts->bit = BIT(bit);
+
+       ts->regmap = syscon_node_to_regmap(syscon_np);
+       if (IS_ERR(ts->regmap)) {
+               dev_err(dev, "cannot get parent's regmap\n");
+               return PTR_ERR(ts->regmap);
+       }
+
+       return 0;
+}
+
+static int ts4800_ts_probe(struct platform_device *pdev)
+{
+       struct input_polled_dev *poll_dev;
+       struct ts4800_ts *ts;
+       struct resource *res;
+       int error;
+
+       ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       error = ts4800_parse_dt(pdev, ts);
+       if (error)
+               return error;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ts->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ts->base))
+               return PTR_ERR(ts->base);
+
+       poll_dev = devm_input_allocate_polled_device(&pdev->dev);
+       if (!poll_dev)
+               return -ENOMEM;
+
+       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
+       ts->poll_dev = poll_dev;
+       ts->dev = &pdev->dev;
+
+       poll_dev->private = ts;
+       poll_dev->poll_interval = POLL_INTERVAL;
+       poll_dev->open = ts4800_ts_open;
+       poll_dev->close = ts4800_ts_close;
+       poll_dev->poll = ts4800_ts_poll;
+
+       poll_dev->input->name = "TS-4800 Touchscreen";
+       poll_dev->input->phys = ts->phys;
+
+       input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH);
+       input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0);
+       input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+       error = input_register_polled_device(poll_dev);
+       if (error) {
+               dev_err(&pdev->dev,
+                       "Unabled to register polled input device (%d)\n",
+                       error);
+               return error;
+       }
+
+       return 0;
+}
+
+static const struct of_device_id ts4800_ts_of_match[] = {
+       { .compatible = "technologic,ts4800-ts", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
+
+static struct platform_driver ts4800_ts_driver = {
+       .driver = {
+               .name = "ts4800-ts",
+               .of_match_table = ts4800_ts_of_match,
+       },
+       .probe = ts4800_ts_probe,
+};
+module_platform_driver(ts4800_ts_driver);
+
+MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ts4800_ts");
index 2792ca397dd085eb1f8d30d0af2a12a233389a41..bab3c6acf6a228f13fcb44756b43374293bf030f 100644 (file)
@@ -80,7 +80,8 @@ struct w8001_touch_query {
  */
 
 struct w8001 {
-       struct input_dev *dev;
+       struct input_dev *pen_dev;
+       struct input_dev *touch_dev;
        struct serio *serio;
        struct completion cmd_done;
        int id;
@@ -95,7 +96,10 @@ struct w8001 {
        u16 max_touch_y;
        u16 max_pen_x;
        u16 max_pen_y;
-       char name[64];
+       char pen_name[64];
+       char touch_name[64];
+       int open_count;
+       struct mutex mutex;
 };
 
 static void parse_pen_data(u8 *data, struct w8001_coord *coord)
@@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001,
 
 static void parse_multi_touch(struct w8001 *w8001)
 {
-       struct input_dev *dev = w8001->dev;
+       struct input_dev *dev = w8001->touch_dev;
        unsigned char *data = w8001->data;
        unsigned int x, y;
        int i;
@@ -151,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001)
                bool touch = data[0] & (1 << i);
 
                input_mt_slot(dev, i);
-               input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
                if (touch) {
                        x = (data[6 * i + 1] << 7) | data[6 * i + 2];
                        y = (data[6 * i + 3] << 7) | data[6 * i + 4];
@@ -207,7 +210,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
 
 static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
 {
-       struct input_dev *dev = w8001->dev;
+       struct input_dev *dev = w8001->pen_dev;
 
        /*
         * We have 1 bit for proximity (rdy) and 3 bits for tip, side,
@@ -233,11 +236,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
                break;
 
        case BTN_TOOL_FINGER:
-               input_report_key(dev, BTN_TOUCH, 0);
-               input_report_key(dev, BTN_TOOL_FINGER, 0);
-               input_sync(dev);
-               /* fall through */
-
        case KEY_RESERVED:
                w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
                break;
@@ -261,7 +259,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
 
 static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
 {
-       struct input_dev *dev = w8001->dev;
+       struct input_dev *dev = w8001->touch_dev;
        unsigned int x = coord->x;
        unsigned int y = coord->y;
 
@@ -271,7 +269,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
        input_report_abs(dev, ABS_X, x);
        input_report_abs(dev, ABS_Y, y);
        input_report_key(dev, BTN_TOUCH, coord->tsw);
-       input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
 
        input_sync(dev);
 
@@ -369,22 +366,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,
 static int w8001_open(struct input_dev *dev)
 {
        struct w8001 *w8001 = input_get_drvdata(dev);
+       int err;
 
-       return w8001_command(w8001, W8001_CMD_START, false);
+       err = mutex_lock_interruptible(&w8001->mutex);
+       if (err)
+               return err;
+
+       if (w8001->open_count++ == 0) {
+               err = w8001_command(w8001, W8001_CMD_START, false);
+               if (err)
+                       w8001->open_count--;
+       }
+
+       mutex_unlock(&w8001->mutex);
+       return err;
 }
 
 static void w8001_close(struct input_dev *dev)
 {
        struct w8001 *w8001 = input_get_drvdata(dev);
 
-       w8001_command(w8001, W8001_CMD_STOP, false);
+       mutex_lock(&w8001->mutex);
+
+       if (--w8001->open_count == 0)
+               w8001_command(w8001, W8001_CMD_STOP, false);
+
+       mutex_unlock(&w8001->mutex);
 }
 
-static int w8001_setup(struct w8001 *w8001)
+static int w8001_detect(struct w8001 *w8001)
 {
-       struct input_dev *dev = w8001->dev;
-       struct w8001_coord coord;
-       struct w8001_touch_query touch;
        int error;
 
        error = w8001_command(w8001, W8001_CMD_STOP, false);
@@ -393,105 +404,145 @@ static int w8001_setup(struct w8001 *w8001)
 
        msleep(250);    /* wait 250ms before querying the device */
 
-       dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-       strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
+       return 0;
+}
 
-       __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+static int w8001_setup_pen(struct w8001 *w8001, char *basename,
+                          size_t basename_sz)
+{
+       struct input_dev *dev = w8001->pen_dev;
+       struct w8001_coord coord;
+       int error;
 
        /* penabled? */
        error = w8001_command(w8001, W8001_CMD_QUERY, true);
-       if (!error) {
-               __set_bit(BTN_TOUCH, dev->keybit);
-               __set_bit(BTN_TOOL_PEN, dev->keybit);
-               __set_bit(BTN_TOOL_RUBBER, dev->keybit);
-               __set_bit(BTN_STYLUS, dev->keybit);
-               __set_bit(BTN_STYLUS2, dev->keybit);
-
-               parse_pen_data(w8001->response, &coord);
-               w8001->max_pen_x = coord.x;
-               w8001->max_pen_y = coord.y;
-
-               input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
-               input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
-               input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
-               input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
-               input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
-               if (coord.tilt_x && coord.tilt_y) {
-                       input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
-                       input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
-               }
-               w8001->id = 0x90;
-               strlcat(w8001->name, " Penabled", sizeof(w8001->name));
+       if (error)
+               return error;
+
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(EV_ABS, dev->evbit);
+       __set_bit(BTN_TOUCH, dev->keybit);
+       __set_bit(BTN_TOOL_PEN, dev->keybit);
+       __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+       __set_bit(BTN_STYLUS, dev->keybit);
+       __set_bit(BTN_STYLUS2, dev->keybit);
+       __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+
+       parse_pen_data(w8001->response, &coord);
+       w8001->max_pen_x = coord.x;
+       w8001->max_pen_y = coord.y;
+
+       input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
+       input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
+       input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+       input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+       input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
+       if (coord.tilt_x && coord.tilt_y) {
+               input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
+               input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
        }
 
+       w8001->id = 0x90;
+       strlcat(basename, " Penabled", basename_sz);
+
+       return 0;
+}
+
+static int w8001_setup_touch(struct w8001 *w8001, char *basename,
+                            size_t basename_sz)
+{
+       struct input_dev *dev = w8001->touch_dev;
+       struct w8001_touch_query touch;
+       int error;
+
+
        /* Touch enabled? */
        error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
-
+       if (error)
+               return error;
        /*
         * Some non-touch devices may reply to the touch query. But their
         * second byte is empty, which indicates touch is not supported.
         */
-       if (!error && w8001->response[1]) {
-               __set_bit(BTN_TOUCH, dev->keybit);
-               __set_bit(BTN_TOOL_FINGER, dev->keybit);
-
-               parse_touchquery(w8001->response, &touch);
-               w8001->max_touch_x = touch.x;
-               w8001->max_touch_y = touch.y;
-
-               if (w8001->max_pen_x && w8001->max_pen_y) {
-                       /* if pen is supported scale to pen maximum */
-                       touch.x = w8001->max_pen_x;
-                       touch.y = w8001->max_pen_y;
-                       touch.panel_res = W8001_PEN_RESOLUTION;
-               }
+       if (!w8001->response[1])
+               return -ENXIO;
 
-               input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
-               input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
-               input_abs_set_res(dev, ABS_X, touch.panel_res);
-               input_abs_set_res(dev, ABS_Y, touch.panel_res);
-
-               switch (touch.sensor_id) {
-               case 0:
-               case 2:
-                       w8001->pktlen = W8001_PKTLEN_TOUCH93;
-                       w8001->id = 0x93;
-                       strlcat(w8001->name, " 1FG", sizeof(w8001->name));
-                       break;
+       __set_bit(EV_KEY, dev->evbit);
+       __set_bit(EV_ABS, dev->evbit);
+       __set_bit(BTN_TOUCH, dev->keybit);
+       __set_bit(INPUT_PROP_DIRECT, dev->propbit);
 
-               case 1:
-               case 3:
-               case 4:
-                       w8001->pktlen = W8001_PKTLEN_TOUCH9A;
-                       strlcat(w8001->name, " 1FG", sizeof(w8001->name));
-                       w8001->id = 0x9a;
-                       break;
+       parse_touchquery(w8001->response, &touch);
+       w8001->max_touch_x = touch.x;
+       w8001->max_touch_y = touch.y;
 
-               case 5:
-                       w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
-
-                       input_mt_init_slots(dev, 2, 0);
-                       input_set_abs_params(dev, ABS_MT_POSITION_X,
-                                               0, touch.x, 0, 0);
-                       input_set_abs_params(dev, ABS_MT_POSITION_Y,
-                                               0, touch.y, 0, 0);
-                       input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
-                                               0, MT_TOOL_MAX, 0, 0);
-
-                       strlcat(w8001->name, " 2FG", sizeof(w8001->name));
-                       if (w8001->max_pen_x && w8001->max_pen_y)
-                               w8001->id = 0xE3;
-                       else
-                               w8001->id = 0xE2;
-                       break;
-               }
+       if (w8001->max_pen_x && w8001->max_pen_y) {
+               /* if pen is supported scale to pen maximum */
+               touch.x = w8001->max_pen_x;
+               touch.y = w8001->max_pen_y;
+               touch.panel_res = W8001_PEN_RESOLUTION;
+       }
+
+       input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
+       input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
+       input_abs_set_res(dev, ABS_X, touch.panel_res);
+       input_abs_set_res(dev, ABS_Y, touch.panel_res);
+
+       switch (touch.sensor_id) {
+       case 0:
+       case 2:
+               w8001->pktlen = W8001_PKTLEN_TOUCH93;
+               w8001->id = 0x93;
+               strlcat(basename, " 1FG", basename_sz);
+               break;
+
+       case 1:
+       case 3:
+       case 4:
+               w8001->pktlen = W8001_PKTLEN_TOUCH9A;
+               strlcat(basename, " 1FG", basename_sz);
+               w8001->id = 0x9a;
+               break;
+
+       case 5:
+               w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
+
+               __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+               input_mt_init_slots(dev, 2, 0);
+               input_set_abs_params(dev, ABS_MT_POSITION_X,
+                                       0, touch.x, 0, 0);
+               input_set_abs_params(dev, ABS_MT_POSITION_Y,
+                                       0, touch.y, 0, 0);
+
+               strlcat(basename, " 2FG", basename_sz);
+               if (w8001->max_pen_x && w8001->max_pen_y)
+                       w8001->id = 0xE3;
+               else
+                       w8001->id = 0xE2;
+               break;
        }
 
-       strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
+       strlcat(basename, " Touchscreen", basename_sz);
 
        return 0;
 }
 
+static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001,
+                             struct serio *serio)
+{
+       dev->phys = w8001->phys;
+       dev->id.bustype = BUS_RS232;
+       dev->id.product = w8001->id;
+       dev->id.vendor = 0x056a;
+       dev->id.version = 0x0100;
+       dev->open = w8001_open;
+       dev->close = w8001_close;
+
+       dev->dev.parent = &serio->dev;
+
+       input_set_drvdata(dev, w8001);
+}
+
 /*
  * w8001_disconnect() is the opposite of w8001_connect()
  */
@@ -502,7 +553,10 @@ static void w8001_disconnect(struct serio *serio)
 
        serio_close(serio);
 
-       input_unregister_device(w8001->dev);
+       if (w8001->pen_dev)
+               input_unregister_device(w8001->pen_dev);
+       if (w8001->touch_dev)
+               input_unregister_device(w8001->touch_dev);
        kfree(w8001);
 
        serio_set_drvdata(serio, NULL);
@@ -517,18 +571,23 @@ static void w8001_disconnect(struct serio *serio)
 static int w8001_connect(struct serio *serio, struct serio_driver *drv)
 {
        struct w8001 *w8001;
-       struct input_dev *input_dev;
-       int err;
+       struct input_dev *input_dev_pen;
+       struct input_dev *input_dev_touch;
+       char basename[64];
+       int err, err_pen, err_touch;
 
        w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!w8001 || !input_dev) {
+       input_dev_pen = input_allocate_device();
+       input_dev_touch = input_allocate_device();
+       if (!w8001 || !input_dev_pen || !input_dev_touch) {
                err = -ENOMEM;
                goto fail1;
        }
 
        w8001->serio = serio;
-       w8001->dev = input_dev;
+       w8001->pen_dev = input_dev_pen;
+       w8001->touch_dev = input_dev_touch;
+       mutex_init(&w8001->mutex);
        init_completion(&w8001->cmd_done);
        snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
 
@@ -537,35 +596,67 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
        if (err)
                goto fail2;
 
-       err = w8001_setup(w8001);
+       err = w8001_detect(w8001);
        if (err)
                goto fail3;
 
-       input_dev->name = w8001->name;
-       input_dev->phys = w8001->phys;
-       input_dev->id.product = w8001->id;
-       input_dev->id.bustype = BUS_RS232;
-       input_dev->id.vendor = 0x056a;
-       input_dev->id.version = 0x0100;
-       input_dev->dev.parent = &serio->dev;
+       /* For backwards-compatibility we compose the basename based on
+        * capabilities and then just append the tool type
+        */
+       strlcpy(basename, "Wacom Serial", sizeof(basename));
+
+       err_pen = w8001_setup_pen(w8001, basename, sizeof(basename));
+       err_touch = w8001_setup_touch(w8001, basename, sizeof(basename));
+       if (err_pen && err_touch) {
+               err = -ENXIO;
+               goto fail3;
+       }
+
+       if (!err_pen) {
+               strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name));
+               strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name));
+               input_dev_pen->name = w8001->pen_name;
 
-       input_dev->open = w8001_open;
-       input_dev->close = w8001_close;
+               w8001_set_devdata(input_dev_pen, w8001, serio);
 
-       input_set_drvdata(input_dev, w8001);
+               err = input_register_device(w8001->pen_dev);
+               if (err)
+                       goto fail3;
+       } else {
+               input_free_device(input_dev_pen);
+               input_dev_pen = NULL;
+               w8001->pen_dev = NULL;
+       }
 
-       err = input_register_device(w8001->dev);
-       if (err)
-               goto fail3;
+       if (!err_touch) {
+               strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name));
+               strlcat(w8001->touch_name, " Finger",
+                       sizeof(w8001->touch_name));
+               input_dev_touch->name = w8001->touch_name;
+
+               w8001_set_devdata(input_dev_touch, w8001, serio);
+
+               err = input_register_device(w8001->touch_dev);
+               if (err)
+                       goto fail4;
+       } else {
+               input_free_device(input_dev_touch);
+               input_dev_touch = NULL;
+               w8001->touch_dev = NULL;
+       }
 
        return 0;
 
+fail4:
+       if (w8001->pen_dev)
+               input_unregister_device(w8001->pen_dev);
 fail3:
        serio_close(serio);
 fail2:
        serio_set_drvdata(serio, NULL);
 fail1:
-       input_free_device(input_dev);
+       input_free_device(input_dev_pen);
+       input_free_device(input_dev_touch);
        kfree(w8001);
        return err;
 }
index 0994c0d01a09295eefc672f6d31a0f34d69bc9ad..75de43da23015c027a350f4ab4b97548305cbf87 100644 (file)
  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
  *
  * Changes/Revisions:
+ *     0.5     08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
+ *                         Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ *             - add UI_DEV_SETUP ioctl
+ *             - add UI_ABS_SETUP ioctl
+ *             - add UI_GET_VERSION ioctl
  *     0.4     01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
  *             - add UI_GET_SYSNAME ioctl
  *     0.3     24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
index becdd78295cc6866fe23c56ba70bf53560a63450..c2ea1698257f65d9656e395ffb0e850ed00540aa 100644 (file)
@@ -77,5 +77,6 @@
 #define SERIO_PS2MULT  0x3c
 #define SERIO_TSC40    0x3d
 #define SERIO_WACOM_IV 0x3e
+#define SERIO_EGALAX   0x3f
 
 #endif /* _UAPI_SERIO_H */
index 013c9d8db3720cc1c606323def3e47692702cfa2..dc652e224b67ce5373a977b68ee46c3855fe0085 100644 (file)
  * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
  *
  * Changes/Revisions:
+ *     0.5     08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
+ *                         Benjamin Tissoires <benjamin.tissoires@redhat.com>)
+ *             - add UI_DEV_SETUP ioctl
+ *             - add UI_ABS_SETUP ioctl
+ *             - add UI_GET_VERSION ioctl
  *     0.4     01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
  *             - add UI_GET_SYSNAME ioctl
  *     0.3     24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
@@ -37,8 +42,8 @@
 #include <linux/types.h>
 #include <linux/input.h>
 
-#define UINPUT_VERSION         4
-
+#define UINPUT_VERSION         5
+#define UINPUT_MAX_NAME_SIZE   80
 
 struct uinput_ff_upload {
        __u32                   request_id;
@@ -58,6 +63,76 @@ struct uinput_ff_erase {
 #define UI_DEV_CREATE          _IO(UINPUT_IOCTL_BASE, 1)
 #define UI_DEV_DESTROY         _IO(UINPUT_IOCTL_BASE, 2)
 
+struct uinput_setup {
+       struct input_id id;
+       char name[UINPUT_MAX_NAME_SIZE];
+       __u32 ff_effects_max;
+};
+
+/**
+ * UI_DEV_SETUP - Set device parameters for setup
+ *
+ * This ioctl sets parameters for the input device to be created.  It
+ * supersedes the old "struct uinput_user_dev" method, which wrote this data
+ * via write(). To actually set the absolute axes UI_ABS_SETUP should be
+ * used.
+ *
+ * The ioctl takes a "struct uinput_setup" object as argument. The fields of
+ * this object are as follows:
+ *              id: See the description of "struct input_id". This field is
+ *                  copied unchanged into the new device.
+ *            name: This is used unchanged as name for the new device.
+ *  ff_effects_max: This limits the maximum numbers of force-feedback effects.
+ *                  See below for a description of FF with uinput.
+ *
+ * This ioctl can be called multiple times and will overwrite previous values.
+ * If this ioctl fails with -EINVAL, it is recommended to use the old
+ * "uinput_user_dev" method via write() as a fallback, in case you run on an
+ * old kernel that does not support this ioctl.
+ *
+ * This ioctl may fail with -EINVAL if it is not supported or if you passed
+ * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
+ * passed uinput_setup object cannot be read/written.
+ * If this call fails, partial data may have already been applied to the
+ * internal device.
+ */
+#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
+
+struct uinput_abs_setup {
+       __u16  code; /* axis code */
+       /* __u16 filler; */
+       struct input_absinfo absinfo;
+};
+
+/**
+ * UI_ABS_SETUP - Set absolute axis information for the device to setup
+ *
+ * This ioctl sets one absolute axis information for the input device to be
+ * created. It supersedes the old "struct uinput_user_dev" method, which wrote
+ * part of this data and the content of UI_DEV_SETUP via write().
+ *
+ * The ioctl takes a "struct uinput_abs_setup" object as argument. The fields
+ * of this object are as follows:
+ *            code: The corresponding input code associated with this axis
+ *                  (ABS_X, ABS_Y, etc...)
+ *         absinfo: See "struct input_absinfo" for a description of this field.
+ *                  This field is copied unchanged into the kernel for the
+ *                  specified axis. If the axis is not enabled via
+ *                  UI_SET_ABSBIT, this ioctl will enable it.
+ *
+ * This ioctl can be called multiple times and will overwrite previous values.
+ * If this ioctl fails with -EINVAL, it is recommended to use the old
+ * "uinput_user_dev" method via write() as a fallback, in case you run on an
+ * old kernel that does not support this ioctl.
+ *
+ * This ioctl may fail with -EINVAL if it is not supported or if you passed
+ * incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
+ * passed uinput_setup object cannot be read/written.
+ * If this call fails, partial data may have already been applied to the
+ * internal device.
+ */
+#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup)
+
 #define UI_SET_EVBIT           _IOW(UINPUT_IOCTL_BASE, 100, int)
 #define UI_SET_KEYBIT          _IOW(UINPUT_IOCTL_BASE, 101, int)
 #define UI_SET_RELBIT          _IOW(UINPUT_IOCTL_BASE, 102, int)
@@ -144,7 +219,6 @@ struct uinput_ff_erase {
 #define UI_FF_UPLOAD           1
 #define UI_FF_ERASE            2
 
-#define UINPUT_MAX_NAME_SIZE   80
 struct uinput_user_dev {
        char name[UINPUT_MAX_NAME_SIZE];
        struct input_id id;