Merge tag 'v4.11-rc5' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 3 Apr 2017 19:01:20 +0000 (12:01 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Mon, 3 Apr 2017 19:01:20 +0000 (12:01 -0700)
Sync up with mainline to bring in changes to input subsystem merged
through other trees.

50 files changed:
Documentation/devicetree/bindings/input/gpio-matrix-keypad.txt
Documentation/devicetree/bindings/input/pwm-beeper.txt
Documentation/devicetree/bindings/input/touchscreen/ad7879.txt
Documentation/devicetree/bindings/input/touchscreen/ads7846.txt [moved from Documentation/devicetree/bindings/input/ads7846.txt with 100% similarity]
Documentation/devicetree/bindings/input/touchscreen/max11801-ts.txt [new file with mode: 0644]
Documentation/devicetree/bindings/input/touchscreen/silead_gsl1680.txt
drivers/input/gameport/gameport.c
drivers/input/joystick/gamecon.c
drivers/input/keyboard/cros_ec_keyb.c
drivers/input/keyboard/matrix_keypad.c
drivers/input/keyboard/qt1070.c
drivers/input/keyboard/tca8418_keypad.c
drivers/input/misc/apanel.c
drivers/input/misc/axp20x-pek.c
drivers/input/misc/bma150.c
drivers/input/misc/dm355evm_keys.c
drivers/input/misc/drv260x.c
drivers/input/misc/pwm-beeper.c
drivers/input/misc/soc_button_array.c
drivers/input/misc/wistron_btns.c
drivers/input/misc/xen-kbdfront.c
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/alps.c
drivers/input/mouse/alps.h
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/psmouse-smbus.c [new file with mode: 0644]
drivers/input/mouse/psmouse.h
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/mouse/synaptics_i2c.c
drivers/input/serio/serio.c
drivers/input/sparse-keymap.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7879-i2c.c
drivers/input/touchscreen/ad7879-spi.c
drivers/input/touchscreen/ad7879.c
drivers/input/touchscreen/ad7879.h
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/max11801_ts.c
drivers/input/touchscreen/melfas_mip4.c
drivers/input/touchscreen/silead.c
drivers/input/touchscreen/tps6507x-ts.c
drivers/input/touchscreen/tsc2007.h [new file with mode: 0644]
drivers/input/touchscreen/tsc2007_core.c [moved from drivers/input/touchscreen/tsc2007.c with 83% similarity]
drivers/input/touchscreen/tsc2007_iio.c [new file with mode: 0644]
include/linux/input/matrix_keypad.h
include/linux/mfd/cros_ec_commands.h
include/linux/serio.h

index d0ea09b..570dc10 100644 (file)
@@ -24,6 +24,8 @@ Optional Properties:
 - debounce-delay-ms:   debounce interval in milliseconds
 - col-scan-delay-us:   delay, measured in microseconds, that is needed
                        before we can scan keypad after activating column gpio
+- drive-inactive-cols: drive inactive columns during scan,
+                       default is to turn inactive columns into inputs.
 
 Example:
        matrix-keypad {
index 529408b..8fc0e48 100644 (file)
@@ -8,6 +8,7 @@ Required properties:
 
 Optional properties:
 - amp-supply: phandle to a regulator that acts as an amplifier for the beeper
+- beeper-hz:  bell frequency in Hz
 
 Example:
 
index e3f22d2..3c8614c 100644 (file)
@@ -35,6 +35,7 @@ Optional properties:
 - adi,conversion-interval:     : 0    : convert one time only
                                  1-255: 515us + val * 35us (up to 9.440ms)
                                  This property has to be a '/bits/ 8' value
+- gpio-controller              : Switch AUX/VBAT/GPIO pin to GPIO mode
 
 Example:
 
@@ -51,3 +52,21 @@ Example:
                adi,averaging = /bits/ 8 <1>;
                adi,conversion-interval = /bits/ 8 <255>;
        };
+
+       ad7879@1 {
+               compatible = "adi,ad7879";
+               spi-max-frequency = <5000000>;
+               reg = <1>;
+               spi-cpol;
+               spi-cpha;
+               gpio-controller;
+               interrupt-parent = <&gpio1>;
+               interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
+               touchscreen-max-pressure = <4096>;
+               adi,resistance-plate-x = <120>;
+               adi,first-conversion-delay = /bits/ 8 <3>;
+               adi,acquisition-time = /bits/ 8 <1>;
+               adi,median-filter-size = /bits/ 8 <2>;
+               adi,averaging = /bits/ 8 <1>;
+               adi,conversion-interval = /bits/ 8 <255>;
+       };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/max11801-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/max11801-ts.txt
new file mode 100644 (file)
index 0000000..40ac0fe
--- /dev/null
@@ -0,0 +1,18 @@
+* MAXI MAX11801 Resistive touch screen controller with i2c interface
+
+Required properties:
+- compatible: must be "maxim,max11801"
+- reg: i2c slave address
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: touch controller interrupt
+
+Example:
+
+&i2c1 {
+       max11801: touchscreen@48 {
+               compatible = "maxim,max11801";
+               reg = <0x48>;
+               interrupt-parent = <&gpio3>;
+               interrupts = <31 IRQ_TYPE_EDGE_FALLING>;
+       };
+};
index ce85ee5..6aa625e 100644 (file)
@@ -1,7 +1,12 @@
 * GSL 1680 touchscreen controller
 
 Required properties:
-- compatible             : "silead,gsl1680"
+- compatible             : Must be one of the following, depending on the model:
+                           "silead,gsl1680"
+                           "silead,gsl1688"
+                           "silead,gsl3670"
+                           "silead,gsl3675"
+                           "silead,gsl3692"
 - reg                    : I2C slave address of the chip (0x40)
 - interrupt-parent       : a phandle pointing to the interrupt controller
                            serving the interrupt for this chip
index 4a2a9e3..092cc41 100644 (file)
@@ -385,8 +385,8 @@ static int gameport_queue_event(void *object, struct module *owner,
        }
 
        if (!try_module_get(owner)) {
-               pr_warning("Can't get module reference, dropping event %d\n",
-                          event_type);
+               pr_warn("Can't get module reference, dropping event %d\n",
+                       event_type);
                kfree(event);
                retval = -EINVAL;
                goto out;
index eae14d5..c43f087 100644 (file)
@@ -870,7 +870,8 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
 
                err = gc_n64_init_ff(input_dev, idx);
                if (err) {
-                       pr_warning("Failed to initiate rumble for N64 device %d\n", idx);
+                       pr_warn("Failed to initiate rumble for N64 device %d\n",
+                               idx);
                        goto err_free_dev;
                }
 
index 6a250d6..c7a8120 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/notifier.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
+#include <linux/sysrq.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/mfd/cros_ec.h>
 #include <linux/mfd/cros_ec_commands.h>
@@ -260,6 +261,12 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
                                     ckdev->ec->event_size);
                break;
 
+       case EC_MKBP_EVENT_SYSRQ:
+               val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq);
+               dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val);
+               handle_sysrq(val);
+               break;
+
        case EC_MKBP_EVENT_BUTTON:
        case EC_MKBP_EVENT_SWITCH:
                /*
index 18839cd..1f316d6 100644 (file)
@@ -42,9 +42,10 @@ struct matrix_keypad {
 };
 
 /*
- * NOTE: normally the GPIO has to be put into HiZ when de-activated to cause
- * minmal side effect when scanning other columns, here it is configured to
- * be input, and it should work on most platforms.
+ * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into
+ * HiZ when de-activated to cause minmal side effect when scanning other
+ * columns. In that case it is configured here to be input, otherwise it is
+ * driven with the inactive value.
  */
 static void __activate_col(const struct matrix_keypad_platform_data *pdata,
                           int col, bool on)
@@ -55,7 +56,8 @@ static void __activate_col(const struct matrix_keypad_platform_data *pdata,
                gpio_direction_output(pdata->col_gpios[col], level_on);
        } else {
                gpio_set_value_cansleep(pdata->col_gpios[col], !level_on);
-               gpio_direction_input(pdata->col_gpios[col]);
+               if (!pdata->drive_inactive_cols)
+                       gpio_direction_input(pdata->col_gpios[col]);
        }
 }
 
@@ -432,6 +434,9 @@ matrix_keypad_parse_dt(struct device *dev)
        if (of_get_property(np, "gpio-activelow", NULL))
                pdata->active_low = true;
 
+       pdata->drive_inactive_cols =
+               of_property_read_bool(np, "drive-inactive-cols");
+
        of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms);
        of_property_read_u32(np, "col-scan-delay-us",
                                                &pdata->col_scan_delay_us);
index 5a57787..76bb513 100644 (file)
@@ -274,9 +274,18 @@ static const struct i2c_device_id qt1070_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, qt1070_id);
 
+#ifdef CONFIG_OF
+static const struct of_device_id qt1070_of_match[] = {
+       { .compatible = "qt1070", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, qt1070_of_match);
+#endif
+
 static struct i2c_driver qt1070_driver = {
        .driver = {
                .name   = "qt1070",
+               .of_match_table = of_match_ptr(qt1070_of_match),
                .pm     = &qt1070_pm_ops,
        },
        .id_table       = qt1070_id,
index 44dd768..e37e335 100644 (file)
@@ -188,8 +188,6 @@ static void tca8418_read_keypad(struct tca8418_keypad *keypad_data)
                input_event(input, EV_MSC, MSC_SCAN, code);
                input_report_key(input, keymap[code], state);
 
-               /* Read for next loop */
-               error = tca8418_read_byte(keypad_data, REG_KEY_EVENT_A, &reg);
        } while (1);
 
        input_sync(input);
index 53630af..aad1df0 100644 (file)
@@ -314,7 +314,8 @@ static int __init apanel_init(void)
                if (devno >= APANEL_DEV_MAX)
                        pr_notice(APANEL ": unknown device %u found\n", devno);
                else if (device_chip[devno] != CHIP_NONE)
-                       pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
+                       pr_warn(APANEL ": duplicate entry for devno %u\n",
+                               devno);
 
                else if (method != 1 && method != 2 && method != 4) {
                        pr_notice(APANEL ": unknown method %u for devno %u\n",
index 1ac898d..f11807d 100644 (file)
@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/acpi.h>
 #include <linux/errno.h>
 #include <linux/irq.h>
 #include <linux/init.h>
@@ -188,21 +189,13 @@ static void axp20x_remove_sysfs_group(void *_data)
        sysfs_remove_group(&dev->kobj, &axp20x_attribute_group);
 }
 
-static int axp20x_pek_probe(struct platform_device *pdev)
+static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
+                                        struct platform_device *pdev)
 {
-       struct axp20x_pek *axp20x_pek;
-       struct axp20x_dev *axp20x;
+       struct axp20x_dev *axp20x = axp20x_pek->axp20x;
        struct input_dev *idev;
        int error;
 
-       axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
-                                 GFP_KERNEL);
-       if (!axp20x_pek)
-               return -ENOMEM;
-
-       axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
-       axp20x = axp20x_pek->axp20x;
-
        axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
        if (axp20x_pek->irq_dbr < 0) {
                dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
@@ -239,7 +232,7 @@ static int axp20x_pek_probe(struct platform_device *pdev)
                                             axp20x_pek_irq, 0,
                                             "axp20x-pek-dbr", idev);
        if (error < 0) {
-               dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+               dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n",
                        axp20x_pek->irq_dbr, error);
                return error;
        }
@@ -248,30 +241,57 @@ static int axp20x_pek_probe(struct platform_device *pdev)
                                          axp20x_pek_irq, 0,
                                          "axp20x-pek-dbf", idev);
        if (error < 0) {
-               dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+               dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n",
                        axp20x_pek->irq_dbf, error);
                return error;
        }
 
-       error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
+       error = input_register_device(idev);
        if (error) {
-               dev_err(axp20x->dev, "Failed to create sysfs attributes: %d\n",
+               dev_err(&pdev->dev, "Can't register input device: %d\n",
                        error);
                return error;
        }
 
-       error = devm_add_action(&pdev->dev,
-                               axp20x_remove_sysfs_group, &pdev->dev);
+       return 0;
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+       struct axp20x_pek *axp20x_pek;
+       int error;
+
+       axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+                                 GFP_KERNEL);
+       if (!axp20x_pek)
+               return -ENOMEM;
+
+       axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+
+       /*
+        * Do not register the input device if there is an "INTCFD9"
+        * gpio button ACPI device, that handles the power button too,
+        * and otherwise we end up reporting all presses twice.
+        */
+       if (!acpi_dev_found("INTCFD9") ||
+           !IS_ENABLED(CONFIG_INPUT_SOC_BUTTON_ARRAY)) {
+               error = axp20x_pek_probe_input_device(axp20x_pek, pdev);
+               if (error)
+                       return error;
+       }
+
+       error = sysfs_create_group(&pdev->dev.kobj, &axp20x_attribute_group);
        if (error) {
-               axp20x_remove_sysfs_group(&pdev->dev);
-               dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
+               dev_err(&pdev->dev, "Failed to create sysfs attributes: %d\n",
                        error);
                return error;
        }
 
-       error = input_register_device(idev);
+       error = devm_add_action(&pdev->dev,
+                               axp20x_remove_sysfs_group, &pdev->dev);
        if (error) {
-               dev_err(axp20x->dev, "Can't register input device: %d\n",
+               axp20x_remove_sysfs_group(&pdev->dev);
+               dev_err(&pdev->dev, "Failed to add sysfs cleanup action: %d\n",
                        error);
                return error;
        }
index 1fa8537..1efcfdf 100644 (file)
@@ -70,7 +70,6 @@
 #define BMA150_CFG_5_REG       0x11
 
 #define BMA150_CHIP_ID         2
-#define BMA180_CHIP_ID         3
 #define BMA150_CHIP_ID_REG     BMA150_DATA_0_REG
 
 #define BMA150_ACC_X_LSB_REG   BMA150_DATA_2_REG
@@ -538,13 +537,8 @@ static int bma150_probe(struct i2c_client *client,
                return -EIO;
        }
 
-       /*
-        * Note if the IIO CONFIG_BMA180 driver is enabled we want to fail
-        * the probe for the bma180 as the iio driver is preferred.
-        */
        chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
-       if (chip_id != BMA150_CHIP_ID &&
-           (IS_ENABLED(CONFIG_BMA180) || chip_id != BMA180_CHIP_ID)) {
+       if (chip_id != BMA150_CHIP_ID) {
                dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
                return -EINVAL;
        }
@@ -648,9 +642,6 @@ static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
 
 static const struct i2c_device_id bma150_id[] = {
        { "bma150", 0 },
-#if !IS_ENABLED(CONFIG_BMA180)
-       { "bma180", 0 },
-#endif
        { "smb380", 0 },
        { "bma023", 0 },
        { }
index 82e272e..bab256e 100644 (file)
@@ -32,7 +32,6 @@
 struct dm355evm_keys {
        struct input_dev        *input;
        struct device           *dev;
-       int                     irq;
 };
 
 /* These initial keycodes can be remapped */
@@ -176,74 +175,49 @@ static int dm355evm_keys_probe(struct platform_device *pdev)
 {
        struct dm355evm_keys    *keys;
        struct input_dev        *input;
-       int                     status;
+       int                     irq;
+       int                     error;
 
-       /* allocate instance struct and input dev */
-       keys = kzalloc(sizeof *keys, GFP_KERNEL);
-       input = input_allocate_device();
-       if (!keys || !input) {
-               status = -ENOMEM;
-               goto fail1;
-       }
+       keys = devm_kzalloc(&pdev->dev, sizeof (*keys), GFP_KERNEL);
+       if (!keys)
+               return -ENOMEM;
+
+       input = devm_input_allocate_device(&pdev->dev);
+       if (!input)
+               return -ENOMEM;
 
        keys->dev = &pdev->dev;
        keys->input = input;
 
-       /* set up "threaded IRQ handler" */
-       status = platform_get_irq(pdev, 0);
-       if (status < 0)
-               goto fail1;
-       keys->irq = status;
-
        input->name = "DM355 EVM Controls";
        input->phys = "dm355evm/input0";
-       input->dev.parent = &pdev->dev;
 
        input->id.bustype = BUS_I2C;
        input->id.product = 0x0355;
        input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
 
-       status = sparse_keymap_setup(input, dm355evm_keys, NULL);
-       if (status)
-               goto fail1;
+       error = sparse_keymap_setup(input, dm355evm_keys, NULL);
+       if (error)
+               return error;
 
        /* REVISIT:  flush the event queue? */
 
-       status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
-                                     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                     dev_name(&pdev->dev), keys);
-       if (status < 0)
-               goto fail2;
-
-       /* register */
-       status = input_register_device(input);
-       if (status < 0)
-               goto fail3;
-
-       platform_set_drvdata(pdev, keys);
-
-       return 0;
-
-fail3:
-       free_irq(keys->irq, keys);
-fail2:
-       sparse_keymap_free(input);
-fail1:
-       input_free_device(input);
-       kfree(keys);
-       dev_err(&pdev->dev, "can't register, err %d\n", status);
-
-       return status;
-}
+       /* set up "threaded IRQ handler" */
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
 
-static int dm355evm_keys_remove(struct platform_device *pdev)
-{
-       struct dm355evm_keys    *keys = platform_get_drvdata(pdev);
+       error = devm_request_threaded_irq(&pdev->dev, irq,
+                                         NULL, dm355evm_keys_irq,
+                                         IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                         dev_name(&pdev->dev), keys);
+       if (error)
+               return error;
 
-       free_irq(keys->irq, keys);
-       sparse_keymap_free(keys->input);
-       input_unregister_device(keys->input);
-       kfree(keys);
+       /* register */
+       error = input_register_device(input);
+       if (error)
+               return error;
 
        return 0;
 }
@@ -259,7 +233,6 @@ static int dm355evm_keys_remove(struct platform_device *pdev)
  */
 static struct platform_driver dm355evm_keys_driver = {
        .probe          = dm355evm_keys_probe,
-       .remove         = dm355evm_keys_remove,
        .driver         = {
                .name   = "dm355evm_keys",
        },
index fb089d3..17eb84a 100644 (file)
@@ -652,7 +652,6 @@ static const struct i2c_device_id drv260x_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, drv260x_id);
 
-#ifdef CONFIG_OF
 static const struct of_device_id drv260x_of_match[] = {
        { .compatible = "ti,drv2604", },
        { .compatible = "ti,drv2604l", },
@@ -661,13 +660,12 @@ static const struct of_device_id drv260x_of_match[] = {
        { }
 };
 MODULE_DEVICE_TABLE(of, drv260x_of_match);
-#endif
 
 static struct i2c_driver drv260x_driver = {
        .probe          = drv260x_probe,
        .driver         = {
                .name   = "drv260x-haptics",
-               .of_match_table = of_match_ptr(drv260x_of_match),
+               .of_match_table = drv260x_of_match,
                .pm     = &drv260x_pm_ops,
        },
        .id_table = drv260x_id,
index e53801d..edca0d7 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/pwm.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
@@ -29,6 +30,7 @@ struct pwm_beeper {
        struct regulator *amplifier;
        struct work_struct work;
        unsigned long period;
+       unsigned int bell_frequency;
        bool suspended;
        bool amplifier_on;
 };
@@ -94,7 +96,7 @@ static int pwm_beeper_event(struct input_dev *input,
 
        switch (code) {
        case SND_BELL:
-               value = value ? 1000 : 0;
+               value = value ? beeper->bell_frequency : 0;
                break;
        case SND_TONE:
                break;
@@ -131,6 +133,7 @@ static int pwm_beeper_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct pwm_beeper *beeper;
        struct pwm_state state;
+       u32 bell_frequency;
        int error;
 
        beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
@@ -167,6 +170,16 @@ static int pwm_beeper_probe(struct platform_device *pdev)
 
        INIT_WORK(&beeper->work, pwm_beeper_work);
 
+       error = device_property_read_u32(dev, "beeper-hz", &bell_frequency);
+       if (error) {
+               bell_frequency = 1000;
+               dev_dbg(dev,
+                       "failed to parse 'beeper-hz' property, using default: %uHz\n",
+                       bell_frequency);
+       }
+
+       beeper->bell_frequency = bell_frequency;
+
        beeper->input = devm_input_allocate_device(dev);
        if (!beeper->input) {
                dev_err(dev, "Failed to allocate input device\n");
index ddb2f22..95b787a 100644 (file)
 #include <linux/gpio.h>
 #include <linux/platform_device.h>
 
-/*
- * Definition of buttons on the tablet. The ACPI index of each button
- * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
- * Platforms"
- */
-#define MAX_NBUTTONS   5
-
 struct soc_button_info {
        const char *name;
        int acpi_index;
@@ -55,7 +48,7 @@ static int soc_button_lookup_gpio(struct device *dev, int acpi_index)
        struct gpio_desc *desc;
        int gpio;
 
-       desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index, GPIOD_ASIS);
+       desc = gpiod_get_index(dev, NULL, acpi_index, GPIOD_ASIS);
        if (IS_ERR(desc))
                return PTR_ERR(desc);
 
@@ -79,14 +72,19 @@ soc_button_device_create(struct platform_device *pdev,
        int gpio;
        int error;
 
+       for (info = button_info; info->name; info++)
+               if (info->autorepeat == autorepeat)
+                       n_buttons++;
+
        gpio_keys_pdata = devm_kzalloc(&pdev->dev,
                                       sizeof(*gpio_keys_pdata) +
-                                       sizeof(*gpio_keys) * MAX_NBUTTONS,
+                                       sizeof(*gpio_keys) * n_buttons,
                                       GFP_KERNEL);
        if (!gpio_keys_pdata)
                return ERR_PTR(-ENOMEM);
 
        gpio_keys = (void *)(gpio_keys_pdata + 1);
+       n_buttons = 0;
 
        for (info = button_info; info->name; info++) {
                if (info->autorepeat != autorepeat)
@@ -140,6 +138,153 @@ err_free_mem:
        return ERR_PTR(error);
 }
 
+static int soc_button_get_acpi_object_int(const union acpi_object *obj)
+{
+       if (obj->type != ACPI_TYPE_INTEGER)
+               return -1;
+
+       return obj->integer.value;
+}
+
+/* Parse a single ACPI0011 _DSD button descriptor */
+static int soc_button_parse_btn_desc(struct device *dev,
+                                    const union acpi_object *desc,
+                                    int collection_uid,
+                                    struct soc_button_info *info)
+{
+       int upage, usage;
+
+       if (desc->type != ACPI_TYPE_PACKAGE ||
+           desc->package.count != 5 ||
+           /* First byte should be 1 (control) */
+           soc_button_get_acpi_object_int(&desc->package.elements[0]) != 1 ||
+           /* Third byte should be collection uid */
+           soc_button_get_acpi_object_int(&desc->package.elements[2]) !=
+                                                           collection_uid) {
+               dev_err(dev, "Invalid ACPI Button Descriptor\n");
+               return -ENODEV;
+       }
+
+       info->event_type = EV_KEY;
+       info->acpi_index =
+               soc_button_get_acpi_object_int(&desc->package.elements[1]);
+       upage = soc_button_get_acpi_object_int(&desc->package.elements[3]);
+       usage = soc_button_get_acpi_object_int(&desc->package.elements[4]);
+
+       /*
+        * The UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e descriptors use HID
+        * usage page and usage codes, but otherwise the device is not HID
+        * compliant: it uses one irq per button instead of generating HID
+        * input reports and some buttons should generate wakeups where as
+        * others should not, so we cannot use the HID subsystem.
+        *
+        * Luckily all devices only use a few usage page + usage combinations,
+        * so we can simply check for the known combinations here.
+        */
+       if (upage == 0x01 && usage == 0x81) {
+               info->name = "power";
+               info->event_code = KEY_POWER;
+               info->wakeup = true;
+       } else if (upage == 0x07 && usage == 0xe3) {
+               info->name = "home";
+               info->event_code = KEY_HOMEPAGE;
+               info->wakeup = true;
+       } else if (upage == 0x0c && usage == 0xe9) {
+               info->name = "volume_up";
+               info->event_code = KEY_VOLUMEUP;
+               info->autorepeat = true;
+       } else if (upage == 0x0c && usage == 0xea) {
+               info->name = "volume_down";
+               info->event_code = KEY_VOLUMEDOWN;
+               info->autorepeat = true;
+       } else {
+               dev_warn(dev, "Unknown button index %d upage %02x usage %02x, ignoring\n",
+                        info->acpi_index, upage, usage);
+               info->name = "unknown";
+               info->event_code = KEY_RESERVED;
+       }
+
+       return 0;
+}
+
+/* ACPI0011 _DSD btns descriptors UUID: fa6bd625-9ce8-470d-a2c7-b3ca36c4282e */
+static const u8 btns_desc_uuid[16] = {
+       0x25, 0xd6, 0x6b, 0xfa, 0xe8, 0x9c, 0x0d, 0x47,
+       0xa2, 0xc7, 0xb3, 0xca, 0x36, 0xc4, 0x28, 0x2e
+};
+
+/* Parse ACPI0011 _DSD button descriptors */
+static struct soc_button_info *soc_button_get_button_info(struct device *dev)
+{
+       struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
+       const union acpi_object *desc, *el0, *uuid, *btns_desc = NULL;
+       struct soc_button_info *button_info;
+       acpi_status status;
+       int i, btn, collection_uid = -1;
+
+       status = acpi_evaluate_object_typed(ACPI_HANDLE(dev), "_DSD", NULL,
+                                           &buf, ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status)) {
+               dev_err(dev, "ACPI _DSD object not found\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       /* Look for the Button Descriptors UUID */
+       desc = buf.pointer;
+       for (i = 0; (i + 1) < desc->package.count; i += 2) {
+               uuid = &desc->package.elements[i];
+
+               if (uuid->type != ACPI_TYPE_BUFFER ||
+                   uuid->buffer.length != 16 ||
+                   desc->package.elements[i + 1].type != ACPI_TYPE_PACKAGE) {
+                       break;
+               }
+
+               if (memcmp(uuid->buffer.pointer, btns_desc_uuid, 16) == 0) {
+                       btns_desc = &desc->package.elements[i + 1];
+                       break;
+               }
+       }
+
+       if (!btns_desc) {
+               dev_err(dev, "ACPI Button Descriptors not found\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       /* The first package describes the collection */
+       el0 = &btns_desc->package.elements[0];
+       if (el0->type == ACPI_TYPE_PACKAGE &&
+           el0->package.count == 5 &&
+           /* First byte should be 0 (collection) */
+           soc_button_get_acpi_object_int(&el0->package.elements[0]) == 0 &&
+           /* Third byte should be 0 (top level collection) */
+           soc_button_get_acpi_object_int(&el0->package.elements[2]) == 0) {
+               collection_uid = soc_button_get_acpi_object_int(
+                                               &el0->package.elements[1]);
+       }
+       if (collection_uid == -1) {
+               dev_err(dev, "Invalid Button Collection Descriptor\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       /* There are package.count - 1 buttons + 1 terminating empty entry */
+       button_info = devm_kcalloc(dev, btns_desc->package.count,
+                                  sizeof(*button_info), GFP_KERNEL);
+       if (!button_info)
+               return ERR_PTR(-ENOMEM);
+
+       /* Parse the button descriptors */
+       for (i = 1, btn = 0; i < btns_desc->package.count; i++, btn++) {
+               if (soc_button_parse_btn_desc(dev,
+                                             &btns_desc->package.elements[i],
+                                             collection_uid,
+                                             &button_info[btn]))
+                       return ERR_PTR(-ENODEV);
+       }
+
+       return button_info;
+}
+
 static int soc_button_remove(struct platform_device *pdev)
 {
        struct soc_button_data *priv = platform_get_drvdata(pdev);
@@ -167,9 +312,15 @@ static int soc_button_probe(struct platform_device *pdev)
        if (!id)
                return -ENODEV;
 
-       button_info = (struct soc_button_info *)id->driver_data;
+       if (!id->driver_data) {
+               button_info = soc_button_get_button_info(dev);
+               if (IS_ERR(button_info))
+                       return PTR_ERR(button_info);
+       } else {
+               button_info = (struct soc_button_info *)id->driver_data;
+       }
 
-       if (gpiod_count(dev, KBUILD_MODNAME) <= 0) {
+       if (gpiod_count(dev, NULL) <= 0) {
                dev_dbg(dev, "no GPIO attached, ignoring...\n");
                return -ENODEV;
        }
@@ -197,9 +348,17 @@ static int soc_button_probe(struct platform_device *pdev)
        if (!priv->children[0] && !priv->children[1])
                return -ENODEV;
 
+       if (!id->driver_data)
+               devm_kfree(dev, button_info);
+
        return 0;
 }
 
+/*
+ * Definition of buttons on the tablet. The ACPI index of each button
+ * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC
+ * Platforms"
+ */
 static struct soc_button_info soc_button_PNP0C40[] = {
        { "power", 0, EV_KEY, KEY_POWER, false, true },
        { "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
@@ -211,6 +370,7 @@ static struct soc_button_info soc_button_PNP0C40[] = {
 
 static const struct acpi_device_id soc_button_acpi_match[] = {
        { "PNP0C40", (unsigned long)soc_button_PNP0C40 },
+       { "ACPI0011", 0 },
        { }
 };
 
index e25f87b..43e67f5 100644 (file)
@@ -1243,12 +1243,10 @@ static int setup_input_dev(void)
 
        error = input_register_polled_device(wistron_idev);
        if (error)
-               goto err_free_keymap;
+               goto err_free_dev;
 
        return 0;
 
- err_free_keymap:
-       sparse_keymap_free(input_dev);
  err_free_dev:
        input_free_polled_device(wistron_idev);
        return error;
@@ -1300,7 +1298,6 @@ static int wistron_remove(struct platform_device *dev)
 {
        wistron_led_remove();
        input_unregister_polled_device(wistron_idev);
-       sparse_keymap_free(wistron_idev->input);
        input_free_polled_device(wistron_idev);
        bios_detach();
 
index 3900875..1fd911d 100644 (file)
@@ -84,8 +84,8 @@ static irqreturn_t input_handler(int rq, void *dev_id)
                                input_report_key(dev, event->key.keycode,
                                                 event->key.pressed);
                        else
-                               pr_warning("unhandled keycode 0x%x\n",
-                                          event->key.keycode);
+                               pr_warn("unhandled keycode 0x%x\n",
+                                       event->key.keycode);
                        break;
                case XENKBD_TYPE_POS:
                        input_report_abs(dev, ABS_X, event->pos.abs_x);
@@ -133,7 +133,7 @@ static int xenkbd_probe(struct xenbus_device *dev,
                ret = xenbus_write(XBT_NIL, dev->nodename,
                                   "request-abs-pointer", "1");
                if (ret) {
-                       pr_warning("xenkbd: can't request abs-pointer");
+                       pr_warn("xenkbd: can't request abs-pointer\n");
                        abs = 0;
                }
        }
@@ -327,7 +327,7 @@ InitWait:
                        ret = xenbus_write(XBT_NIL, info->xbdev->nodename,
                                           "request-abs-pointer", "1");
                        if (ret)
-                               pr_warning("xenkbd: can't request abs-pointer");
+                               pr_warn("xenkbd: can't request abs-pointer\n");
                }
 
                xenbus_switch_state(dev, XenbusStateConnected);
index 096abb4..89ebb8f 100644 (file)
@@ -78,6 +78,18 @@ config MOUSE_PS2_SYNAPTICS
 
          If unsure, say Y.
 
+config MOUSE_PS2_SYNAPTICS_SMBUS
+       bool "Synaptics PS/2 SMbus companion" if EXPERT
+       default y
+       depends on MOUSE_PS2
+       depends on I2C=y || I2C=MOUSE_PS2
+       select MOUSE_PS2_SMBUS
+       help
+         Say Y here if you have a Synaptics RMI4 touchpad connected to
+         to an SMBus, but enumerated through PS/2.
+
+         If unsure, say Y.
+
 config MOUSE_PS2_CYPRESS
        bool "Cypress PS/2 mouse protocol extension" if EXPERT
        default y
@@ -171,6 +183,10 @@ config MOUSE_PS2_VMMOUSE
 
          If unsure, say N.
 
+config MOUSE_PS2_SMBUS
+       bool
+       depends on MOUSE_PS2
+
 config MOUSE_SERIAL
        tristate "Serial mouse"
        select SERIO
index 6168b13..56bf0ad 100644 (file)
@@ -39,6 +39,8 @@ psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT)  += touchkit_ps2.o
 psmouse-$(CONFIG_MOUSE_PS2_CYPRESS)    += cypress_ps2.o
 psmouse-$(CONFIG_MOUSE_PS2_VMMOUSE)    += vmmouse.o
 
+psmouse-$(CONFIG_MOUSE_PS2_SMBUS)      += psmouse-smbus.o
+
 elan_i2c-objs := elan_i2c_core.o
 elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_I2C)  += elan_i2c_i2c.o
 elan_i2c-$(CONFIG_MOUSE_ELAN_I2C_SMBUS)        += elan_i2c_smbus.o
index f210e19..262d105 100644 (file)
@@ -106,39 +106,36 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
 #define ALPS_DUALPOINT_WITH_PRESSURE   0x400   /* device can report trackpoint pressure */
 
 static const struct alps_model_info alps_model_data[] = {
-       { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },      /* Toshiba Salellite Pro M10 */
-       { { 0x33, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } },                               /* UMAX-530T */
-       { { 0x53, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
-       { { 0x53, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
-       { { 0x60, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },                               /* HP ze1115 */
-       { { 0x63, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
-       { { 0x63, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
-       { { 0x63, 0x02, 0x28 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } },            /* Fujitsu Siemens S6010 */
-       { { 0x63, 0x02, 0x3c }, 0x00, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } },              /* Toshiba Satellite S2400-103 */
-       { { 0x63, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } },            /* NEC Versa L320 */
-       { { 0x63, 0x02, 0x64 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
-       { { 0x63, 0x03, 0xc8 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },      /* Dell Latitude D800 */
-       { { 0x73, 0x00, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } },          /* ThinkPad R61 8918-5QG */
-       { { 0x73, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
-       { { 0x73, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } },            /* Ahtec Laptop */
-
        /*
         * XXX This entry is suspicious. First byte has zero lower nibble,
         * which is what a normal mouse would report. Also, the value 0x0e
         * isn't valid per PS/2 spec.
         */
-       { { 0x20, 0x02, 0x0e }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
-
-       { { 0x22, 0x02, 0x0a }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
-       { { 0x22, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } },      /* Dell Latitude D600 */
-       /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
-       { { 0x62, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf,
-               ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },
-       { { 0x73, 0x00, 0x14 }, 0x00, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } },          /* Dell XT2 */
-       { { 0x73, 0x02, 0x50 }, 0x00, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } },       /* Dell Vostro 1400 */
-       { { 0x52, 0x01, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xff, 0xff,
+       { { 0x20, 0x02, 0x0e }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+
+       { { 0x22, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },
+       { { 0x22, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT } },    /* Dell Latitude D600 */
+       { { 0x32, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },    /* Toshiba Salellite Pro M10 */
+       { { 0x33, 0x02, 0x0a }, { ALPS_PROTO_V1, 0x88, 0xf8, 0 } },                             /* UMAX-530T */
+       { { 0x52, 0x01, 0x14 }, { ALPS_PROTO_V2, 0xff, 0xff,
                ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },                          /* Toshiba Tecra A11-11L */
-       { { 0x73, 0x02, 0x64 }, 0x8a, { ALPS_PROTO_V4, 0x8f, 0x8f, 0 } },
+       { { 0x53, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+       { { 0x53, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+       { { 0x60, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },                             /* HP ze1115 */
+       { { 0x62, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xcf, 0xcf,
+               ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED } },                          /* Dell Latitude E5500, E6400, E6500, Precision M4400 */
+       { { 0x63, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+       { { 0x63, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+       { { 0x63, 0x02, 0x28 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } },                  /* Fujitsu Siemens S6010 */
+       { { 0x63, 0x02, 0x3c }, { ALPS_PROTO_V2, 0x8f, 0x8f, ALPS_WHEEL } },                    /* Toshiba Satellite S2400-103 */
+       { { 0x63, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xef, 0xef, ALPS_FW_BK_1 } },                  /* NEC Versa L320 */
+       { { 0x63, 0x02, 0x64 }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+       { { 0x63, 0x03, 0xc8 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } },    /* Dell Latitude D800 */
+       { { 0x73, 0x00, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_DUALPOINT } },                /* ThinkPad R61 8918-5QG */
+       { { 0x73, 0x00, 0x14 }, { ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT } },                /* Dell XT2 */
+       { { 0x73, 0x02, 0x0a }, { ALPS_PROTO_V2, 0xf8, 0xf8, 0 } },
+       { { 0x73, 0x02, 0x14 }, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_FW_BK_2 } },                  /* Ahtec Laptop */
+       { { 0x73, 0x02, 0x50 }, { ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS } },             /* Dell Vostro 1400 */
 };
 
 static const struct alps_protocol_info alps_v3_protocol_data = {
@@ -149,6 +146,10 @@ static const struct alps_protocol_info alps_v3_rushmore_data = {
        ALPS_PROTO_V3_RUSHMORE, 0x8f, 0x8f, ALPS_DUALPOINT
 };
 
+static const struct alps_protocol_info alps_v4_protocol_data = {
+       ALPS_PROTO_V4, 0x8f, 0x8f, 0
+};
+
 static const struct alps_protocol_info alps_v5_protocol_data = {
        ALPS_PROTO_V5, 0xc8, 0xd8, 0
 };
@@ -161,6 +162,10 @@ static const struct alps_protocol_info alps_v8_protocol_data = {
        ALPS_PROTO_V8, 0x18, 0x18, 0
 };
 
+static const struct alps_protocol_info alps_v9_protocol_data = {
+       ALPS_PROTO_V9, 0xc8, 0xc8, 0
+};
+
 /*
  * Some v2 models report the stick buttons in separate bits
  */
@@ -2806,12 +2811,8 @@ static const struct alps_protocol_info *alps_match_table(unsigned char *e7,
        for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) {
                model = &alps_model_data[i];
 
-               if (!memcmp(e7, model->signature, sizeof(model->signature)) &&
-                   (!model->command_mode_resp ||
-                    model->command_mode_resp == ec[2])) {
-
+               if (!memcmp(e7, model->signature, sizeof(model->signature)))
                        return &model->protocol_info;
-               }
        }
 
        return NULL;
@@ -2849,7 +2850,10 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
 
        protocol = alps_match_table(e7, ec);
        if (!protocol) {
-               if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
+               if (e7[0] == 0x73 && e7[1] == 0x02 && e7[2] == 0x64 &&
+                          ec[2] == 0x8a) {
+                       protocol = &alps_v4_protocol_data;
+               } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
                           ec[0] == 0x73 && (ec[1] == 0x01 || ec[1] == 0x02)) {
                        protocol = &alps_v5_protocol_data;
                } else if (ec[0] == 0x88 &&
@@ -2863,6 +2867,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
                } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
                           (e7[2] == 0x14 || e7[2] == 0x28)) {
                        protocol = &alps_v8_protocol_data;
+               } else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0xc8) {
+                       protocol = &alps_v9_protocol_data;
+                       psmouse_warn(psmouse,
+                                    "Unsupported ALPS V9 touchpad: E7=%3ph, EC=%3ph\n",
+                                    e7, ec);
+                       return -EINVAL;
                } else {
                        psmouse_dbg(psmouse,
                                    "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
index 4334f28..ed2d687 100644 (file)
@@ -23,6 +23,7 @@
 #define ALPS_PROTO_V6          0x600
 #define ALPS_PROTO_V7          0x700   /* t3btl t4s */
 #define ALPS_PROTO_V8          0x800   /* SS4btl SS4s */
+#define ALPS_PROTO_V9          0x900   /* ss3btl */
 
 #define MAX_TOUCHES    4
 
@@ -172,10 +173,6 @@ struct alps_protocol_info {
 /**
  * struct alps_model_info - touchpad ID table
  * @signature: E7 response string to match.
- * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response
- *   (aka command mode response) identifies the firmware minor version.  This
- *   can be used to distinguish different hardware models which are not
- *   uniquely identifiable through their E7 responses.
  * @protocol_info: information about protocol used by the device.
  *
  * Many (but not all) ALPS touchpads can be identified by looking at the
@@ -184,7 +181,6 @@ struct alps_protocol_info {
  */
 struct alps_model_info {
        u8 signature[3];
-       u8 command_mode_resp;
        struct alps_protocol_info protocol_info;
 };
 
index a598b72..f73b47b 100644 (file)
@@ -116,17 +116,6 @@ static DEFINE_MUTEX(psmouse_mutex);
 
 static struct workqueue_struct *kpsmoused_wq;
 
-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);
-       int (*init)(struct psmouse *);
-};
-
 static void psmouse_report_standard_buttons(struct input_dev *dev, u8 buttons)
 {
        input_report_key(dev, BTN_LEFT,   buttons & BIT(0));
@@ -148,7 +137,7 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
 
        /* Full packet accumulated, process it */
 
-       switch (psmouse->type) {
+       switch (psmouse->protocol->type) {
        case PSMOUSE_IMPS:
                /* IntelliMouse has scroll wheel */
                input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
@@ -325,7 +314,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                goto out;
 
        if (unlikely((flags & SERIO_TIMEOUT) ||
-                    ((flags & SERIO_PARITY) && !psmouse->ignore_parity))) {
+                    ((flags & SERIO_PARITY) &&
+                     !psmouse->protocol->ignore_parity))) {
 
                if (psmouse->state == PSMOUSE_ACTIVATED)
                        psmouse_warn(psmouse,
@@ -372,7 +362,7 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
                }
 
                if (psmouse->packet[1] == PSMOUSE_RET_ID ||
-                   (psmouse->type == PSMOUSE_HGPK &&
+                   (psmouse->protocol->type == PSMOUSE_HGPK &&
                     psmouse->packet[1] == PSMOUSE_RET_BAT)) {
                        __psmouse_set_state(psmouse, PSMOUSE_IGNORE);
                        serio_reconnect(serio);
@@ -783,7 +773,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .name           = "SynPS/2",
                .alias          = "synaptics",
                .detect         = synaptics_detect,
-               .init           = synaptics_init,
+               .init           = synaptics_init_absolute,
        },
        {
                .type           = PSMOUSE_SYNAPTICS_RELATIVE,
@@ -793,6 +783,16 @@ static const struct psmouse_protocol psmouse_protocols[] = {
                .init           = synaptics_init_relative,
        },
 #endif
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
+       {
+               .type           = PSMOUSE_SYNAPTICS_SMBUS,
+               .name           = "SynSMBus",
+               .alias          = "synaptics-smbus",
+               .detect         = synaptics_detect,
+               .init           = synaptics_init_smbus,
+               .smbus_companion = true,
+       },
+#endif
 #ifdef CONFIG_MOUSE_PS2_ALPS
        {
                .type           = PSMOUSE_ALPS,
@@ -959,6 +959,8 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
 
        __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
 
+       psmouse->protocol = &psmouse_protocols[0];
+
        psmouse->set_rate = psmouse_set_rate;
        psmouse->set_resolution = psmouse_set_resolution;
        psmouse->set_scale = psmouse_set_scale;
@@ -966,6 +968,7 @@ static void psmouse_apply_defaults(struct psmouse *psmouse)
        psmouse->protocol_handler = psmouse_process_byte;
        psmouse->pktsize = 3;
        psmouse->reconnect = NULL;
+       psmouse->fast_reconnect = NULL;
        psmouse->disconnect = NULL;
        psmouse->cleanup = NULL;
        psmouse->pt_activate = NULL;
@@ -1018,6 +1021,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
                              unsigned int max_proto, bool set_properties)
 {
        bool synaptics_hardware = false;
+       int ret;
 
        /*
         * Always check for focaltech, this is safe as it uses pnp-id
@@ -1080,9 +1084,14 @@ static int psmouse_extensions(struct psmouse *psmouse,
                         * 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;
+                       if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) ||
+                           IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)) {
+                               if (!set_properties)
+                                       return PSMOUSE_SYNAPTICS;
+
+                               ret = synaptics_init(psmouse);
+                               if (ret >= 0)
+                                       return ret;
                        }
 
                        /*
@@ -1431,9 +1440,8 @@ static void psmouse_cleanup(struct serio *serio)
  */
 static void psmouse_disconnect(struct serio *serio)
 {
-       struct psmouse *psmouse, *parent = NULL;
-
-       psmouse = serio_get_drvdata(serio);
+       struct psmouse *psmouse = serio_get_drvdata(serio);
+       struct psmouse *parent = NULL;
 
        sysfs_remove_group(&serio->dev.kobj, &psmouse_attribute_group);
 
@@ -1461,7 +1469,10 @@ static void psmouse_disconnect(struct serio *serio)
 
        serio_close(serio);
        serio_set_drvdata(serio, NULL);
-       input_unregister_device(psmouse->dev);
+
+       if (psmouse->dev)
+               input_unregister_device(psmouse->dev);
+
        kfree(psmouse);
 
        if (parent)
@@ -1475,6 +1486,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
 {
        const struct psmouse_protocol *selected_proto;
        struct input_dev *input_dev = psmouse->dev;
+       enum psmouse_type type;
 
        input_dev->dev.parent = &psmouse->ps2dev.serio->dev;
 
@@ -1487,15 +1499,13 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
                if (proto->init && proto->init(psmouse) < 0)
                        return -1;
 
-               psmouse->type = proto->type;
                selected_proto = proto;
        } else {
-               psmouse->type = psmouse_extensions(psmouse,
-                                                  psmouse_max_proto, true);
-               selected_proto = psmouse_protocol_by_type(psmouse->type);
+               type = psmouse_extensions(psmouse, psmouse_max_proto, true);
+               selected_proto = psmouse_protocol_by_type(type);
        }
 
-       psmouse->ignore_parity = selected_proto->ignore_parity;
+       psmouse->protocol = selected_proto;
 
        /*
         * If mouse's packet size is 3 there is no point in polling the
@@ -1521,7 +1531,7 @@ static int psmouse_switch_protocol(struct psmouse *psmouse,
        input_dev->phys = psmouse->phys;
        input_dev->id.bustype = BUS_I8042;
        input_dev->id.vendor = 0x0002;
-       input_dev->id.product = psmouse->type;
+       input_dev->id.product = psmouse->protocol->type;
        input_dev->id.version = psmouse->model;
 
        return 0;
@@ -1583,12 +1593,18 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
 
        psmouse_switch_protocol(psmouse, NULL);
 
-       psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
-       psmouse_initialize(psmouse);
+       if (!psmouse->protocol->smbus_companion) {
+               psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+               psmouse_initialize(psmouse);
 
-       error = input_register_device(psmouse->dev);
-       if (error)
-               goto err_protocol_disconnect;
+               error = input_register_device(input_dev);
+               if (error)
+                       goto err_protocol_disconnect;
+       } else {
+               /* Smbus companion will be reporting events, not us. */
+               input_free_device(input_dev);
+               psmouse->dev = input_dev = NULL;
+       }
 
        if (parent && parent->pt_activate)
                parent->pt_activate(parent);
@@ -1597,7 +1613,12 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
        if (error)
                goto err_pt_deactivate;
 
-       psmouse_activate(psmouse);
+       /*
+        * PS/2 devices having SMBus companions should stay disabled
+        * on PS/2 side, in order to have SMBus part operable.
+        */
+       if (!psmouse->protocol->smbus_companion)
+               psmouse_activate(psmouse);
 
  out:
        /* If this is a pass-through port the parent needs to be re-activated */
@@ -1610,8 +1631,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
  err_pt_deactivate:
        if (parent && parent->pt_deactivate)
                parent->pt_deactivate(parent);
-       input_unregister_device(psmouse->dev);
-       input_dev = NULL; /* so we don't try to free it below */
+       if (input_dev) {
+               input_unregister_device(input_dev);
+               input_dev = NULL; /* so we don't try to free it below */
+       }
  err_protocol_disconnect:
        if (psmouse->disconnect)
                psmouse->disconnect(psmouse);
@@ -1628,15 +1651,26 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
        goto out;
 }
 
-static int psmouse_reconnect(struct serio *serio)
+static int __psmouse_reconnect(struct serio *serio, bool fast_reconnect)
 {
        struct psmouse *psmouse = serio_get_drvdata(serio);
        struct psmouse *parent = NULL;
-       unsigned char type;
+       int (*reconnect_handler)(struct psmouse *);
+       enum psmouse_type type;
        int rc = -1;
 
        mutex_lock(&psmouse_mutex);
 
+       if (fast_reconnect) {
+               reconnect_handler = psmouse->fast_reconnect;
+               if (!reconnect_handler) {
+                       rc = -ENOENT;
+                       goto out_unlock;
+               }
+       } else {
+               reconnect_handler = psmouse->reconnect;
+       }
+
        if (serio->parent && serio->id.type == SERIO_PS_PSTHRU) {
                parent = serio_get_drvdata(serio->parent);
                psmouse_deactivate(parent);
@@ -1644,8 +1678,8 @@ static int psmouse_reconnect(struct serio *serio)
 
        psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
 
-       if (psmouse->reconnect) {
-               if (psmouse->reconnect(psmouse))
+       if (reconnect_handler) {
+               if (reconnect_handler(psmouse))
                        goto out;
        } else {
                psmouse_reset(psmouse);
@@ -1654,7 +1688,7 @@ static int psmouse_reconnect(struct serio *serio)
                        goto out;
 
                type = psmouse_extensions(psmouse, psmouse_max_proto, false);
-               if (psmouse->type != type)
+               if (psmouse->protocol->type != type)
                        goto out;
        }
 
@@ -1662,14 +1696,21 @@ static int psmouse_reconnect(struct serio *serio)
         * OK, the device type (and capabilities) match the old one,
         * we can continue using it, complete initialization
         */
-       psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
-
-       psmouse_initialize(psmouse);
+       if (!psmouse->protocol->smbus_companion) {
+               psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+               psmouse_initialize(psmouse);
+       }
 
        if (parent && parent->pt_activate)
                parent->pt_activate(parent);
 
-       psmouse_activate(psmouse);
+       /*
+        * PS/2 devices having SMBus companions should stay disabled
+        * on PS/2 side, in order to have SMBus part operable.
+        */
+       if (!psmouse->protocol->smbus_companion)
+               psmouse_activate(psmouse);
+
        rc = 0;
 
 out:
@@ -1677,10 +1718,21 @@ out:
        if (parent)
                psmouse_activate(parent);
 
+out_unlock:
        mutex_unlock(&psmouse_mutex);
        return rc;
 }
 
+static int psmouse_reconnect(struct serio *serio)
+{
+       return __psmouse_reconnect(serio, false);
+}
+
+static int psmouse_fast_reconnect(struct serio *serio)
+{
+       return __psmouse_reconnect(serio, true);
+}
+
 static struct serio_device_id psmouse_serio_ids[] = {
        {
                .type   = SERIO_8042,
@@ -1708,6 +1760,7 @@ static struct serio_driver psmouse_drv = {
        .interrupt      = psmouse_interrupt,
        .connect        = psmouse_connect,
        .reconnect      = psmouse_reconnect,
+       .fast_reconnect = psmouse_fast_reconnect,
        .disconnect     = psmouse_disconnect,
        .cleanup        = psmouse_cleanup,
 };
@@ -1717,9 +1770,11 @@ ssize_t psmouse_attr_show_helper(struct device *dev, struct device_attribute *de
 {
        struct serio *serio = to_serio_port(dev);
        struct psmouse_attribute *attr = to_psmouse_attr(devattr);
-       struct psmouse *psmouse;
+       struct psmouse *psmouse = serio_get_drvdata(serio);
 
-       psmouse = serio_get_drvdata(serio);
+       if (psmouse->protocol->smbus_companion &&
+                       devattr != &psmouse_attr_protocol.dattr)
+               return -ENOENT;
 
        return attr->show(psmouse, attr->data, buf);
 }
@@ -1738,6 +1793,12 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
 
        psmouse = serio_get_drvdata(serio);
 
+       if (psmouse->protocol->smbus_companion &&
+                       devattr != &psmouse_attr_protocol.dattr) {
+               retval = -ENOENT;
+               goto out_unlock;
+       }
+
        if (attr->protect) {
                if (psmouse->state == PSMOUSE_IGNORE) {
                        retval = -ENODEV;
@@ -1749,13 +1810,14 @@ ssize_t psmouse_attr_set_helper(struct device *dev, struct device_attribute *dev
                        psmouse_deactivate(parent);
                }
 
-               psmouse_deactivate(psmouse);
+               if (!psmouse->protocol->smbus_companion)
+                       psmouse_deactivate(psmouse);
        }
 
        retval = attr->set(psmouse, attr->data, buf, count);
 
        if (attr->protect) {
-               if (retval != -ENODEV)
+               if (retval != -ENODEV && !psmouse->protocol->smbus_companion)
                        psmouse_activate(psmouse);
 
                if (parent)
@@ -1792,7 +1854,7 @@ static ssize_t psmouse_set_int_attr(struct psmouse *psmouse, void *offset, const
 
 static ssize_t psmouse_attr_show_protocol(struct psmouse *psmouse, void *data, char *buf)
 {
-       return sprintf(buf, "%s\n", psmouse_protocol_by_type(psmouse->type)->name);
+       return sprintf(buf, "%s\n", psmouse->protocol->name);
 }
 
 static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, const char *buf, size_t count)
@@ -1808,7 +1870,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
        if (!proto)
                return -EINVAL;
 
-       if (psmouse->type == proto->type)
+       if (psmouse->protocol == proto)
                return count;
 
        new_dev = input_allocate_device();
@@ -1832,7 +1894,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
                        return -ENODEV;
                }
 
-               if (psmouse->type == proto->type) {
+               if (psmouse->protocol == proto) {
                        input_free_device(new_dev);
                        return count; /* switched by other thread */
                }
@@ -1845,7 +1907,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
        }
 
        old_dev = psmouse->dev;
-       old_proto = psmouse_protocol_by_type(psmouse->type);
+       old_proto = psmouse->protocol;
 
        if (psmouse->disconnect)
                psmouse->disconnect(psmouse);
@@ -1864,23 +1926,29 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
        psmouse_initialize(psmouse);
        psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 
-       error = input_register_device(psmouse->dev);
-       if (error) {
-               if (psmouse->disconnect)
-                       psmouse->disconnect(psmouse);
+       if (psmouse->protocol->smbus_companion) {
+               input_free_device(psmouse->dev);
+               psmouse->dev = NULL;
+       } else {
+               error = input_register_device(psmouse->dev);
+               if (error) {
+                       if (psmouse->disconnect)
+                               psmouse->disconnect(psmouse);
 
-               psmouse_set_state(psmouse, PSMOUSE_IGNORE);
-               input_free_device(new_dev);
-               psmouse->dev = old_dev;
-               psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
-               psmouse_switch_protocol(psmouse, old_proto);
-               psmouse_initialize(psmouse);
-               psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+                       psmouse_set_state(psmouse, PSMOUSE_IGNORE);
+                       input_free_device(new_dev);
+                       psmouse->dev = old_dev;
+                       psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
+                       psmouse_switch_protocol(psmouse, old_proto);
+                       psmouse_initialize(psmouse);
+                       psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
 
-               return error;
+                       return error;
+               }
        }
 
-       input_unregister_device(old_dev);
+       if (old_dev)
+               input_unregister_device(old_dev);
 
        if (parent && parent->pt_activate)
                parent->pt_activate(parent);
@@ -1947,16 +2015,27 @@ static int __init psmouse_init(void)
        synaptics_module_init();
        hgpk_module_init();
 
+       err = psmouse_smbus_module_init();
+       if (err)
+               return err;
+
        kpsmoused_wq = alloc_ordered_workqueue("kpsmoused", 0);
        if (!kpsmoused_wq) {
                pr_err("failed to create kpsmoused workqueue\n");
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto err_smbus_exit;
        }
 
        err = serio_register_driver(&psmouse_drv);
        if (err)
-               destroy_workqueue(kpsmoused_wq);
+               goto err_destroy_wq;
+
+       return 0;
 
+err_destroy_wq:
+       destroy_workqueue(kpsmoused_wq);
+err_smbus_exit:
+       psmouse_smbus_module_exit();
        return err;
 }
 
@@ -1964,6 +2043,7 @@ static void __exit psmouse_exit(void)
 {
        serio_unregister_driver(&psmouse_drv);
        destroy_workqueue(kpsmoused_wq);
+       psmouse_smbus_module_exit();
 }
 
 module_init(psmouse_init);
diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
new file mode 100644 (file)
index 0000000..d2b1ea3
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2017 Red Hat, Inc
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt)            KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/libps2.h>
+#include <linux/i2c.h>
+#include <linux/serio.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include "psmouse.h"
+
+struct psmouse_smbus_dev {
+       struct i2c_board_info board;
+       struct psmouse *psmouse;
+       struct i2c_client *client;
+       struct list_head node;
+       bool dead;
+};
+
+static LIST_HEAD(psmouse_smbus_list);
+static DEFINE_MUTEX(psmouse_smbus_mutex);
+
+static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter)
+{
+       struct psmouse_smbus_dev *smbdev;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
+               return;
+
+       mutex_lock(&psmouse_smbus_mutex);
+
+       list_for_each_entry(smbdev, &psmouse_smbus_list, node) {
+               if (smbdev->dead)
+                       continue;
+
+               if (smbdev->client)
+                       continue;
+
+               /*
+                * Here would be a good place to check if device is actually
+                * present, but it seems that SMBus will not respond unless we
+                * fully reset PS/2 connection.  So cross our fingers, and try
+                * to switch over, hopefully our system will not have too many
+                * "host notify" I2C adapters.
+                */
+               psmouse_dbg(smbdev->psmouse,
+                           "SMBus candidate adapter appeared, triggering rescan\n");
+               serio_rescan(smbdev->psmouse->ps2dev.serio);
+       }
+
+       mutex_unlock(&psmouse_smbus_mutex);
+}
+
+static void psmouse_smbus_detach_i2c_client(struct i2c_client *client)
+{
+       struct psmouse_smbus_dev *smbdev, *tmp;
+
+       mutex_lock(&psmouse_smbus_mutex);
+
+       list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+               if (smbdev->client != client)
+                       continue;
+
+               kfree(client->dev.platform_data);
+               client->dev.platform_data = NULL;
+
+               if (!smbdev->dead) {
+                       psmouse_dbg(smbdev->psmouse,
+                                   "Marking SMBus companion %s as gone\n",
+                                   dev_name(&smbdev->client->dev));
+                       smbdev->dead = true;
+                       serio_rescan(smbdev->psmouse->ps2dev.serio);
+               } else {
+                       list_del(&smbdev->node);
+                       kfree(smbdev);
+               }
+       }
+
+       mutex_unlock(&psmouse_smbus_mutex);
+}
+
+static int psmouse_smbus_notifier_call(struct notifier_block *nb,
+                                      unsigned long action, void *data)
+{
+       struct device *dev = data;
+
+       switch (action) {
+       case BUS_NOTIFY_ADD_DEVICE:
+               if (dev->type == &i2c_adapter_type)
+                       psmouse_smbus_check_adapter(to_i2c_adapter(dev));
+               break;
+
+       case BUS_NOTIFY_REMOVED_DEVICE:
+               if (dev->type != &i2c_adapter_type)
+                       psmouse_smbus_detach_i2c_client(to_i2c_client(dev));
+               break;
+       }
+
+       return 0;
+}
+
+static struct notifier_block psmouse_smbus_notifier = {
+       .notifier_call = psmouse_smbus_notifier_call,
+};
+
+static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
+{
+       return PSMOUSE_FULL_PACKET;
+}
+
+static int psmouse_smbus_reconnect(struct psmouse *psmouse)
+{
+       psmouse_deactivate(psmouse);
+
+       return 0;
+}
+
+struct psmouse_smbus_removal_work {
+       struct work_struct work;
+       struct i2c_client *client;
+};
+
+static void psmouse_smbus_remove_i2c_device(struct work_struct *work)
+{
+       struct psmouse_smbus_removal_work *rwork =
+               container_of(work, struct psmouse_smbus_removal_work, work);
+
+       dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n");
+       i2c_unregister_device(rwork->client);
+
+       kfree(rwork);
+}
+
+/*
+ * This schedules removal of SMBus companion device. We have to do
+ * it in a separate tread to avoid deadlocking on psmouse_mutex in
+ * case the device has a trackstick (which is also driven by psmouse).
+ *
+ * Note that this may be racing with i2c adapter removal, but we
+ * can't do anything about that: i2c automatically destroys clients
+ * attached to an adapter that is being removed. This has to be
+ * fixed in i2c core.
+ */
+static void psmouse_smbus_schedule_remove(struct i2c_client *client)
+{
+       struct psmouse_smbus_removal_work *rwork;
+
+       rwork = kzalloc(sizeof(*rwork), GFP_KERNEL);
+       if (rwork) {
+               INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device);
+               rwork->client = client;
+
+               schedule_work(&rwork->work);
+       }
+}
+
+static void psmouse_smbus_disconnect(struct psmouse *psmouse)
+{
+       struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+       mutex_lock(&psmouse_smbus_mutex);
+
+       if (smbdev->dead) {
+               list_del(&smbdev->node);
+               kfree(smbdev);
+       } else {
+               smbdev->dead = true;
+               psmouse_dbg(smbdev->psmouse,
+                           "posting removal request for SMBus companion %s\n",
+                           dev_name(&smbdev->client->dev));
+               psmouse_smbus_schedule_remove(smbdev->client);
+       }
+
+       mutex_unlock(&psmouse_smbus_mutex);
+
+       psmouse->private = NULL;
+}
+
+static int psmouse_smbus_create_companion(struct device *dev, void *data)
+{
+       struct psmouse_smbus_dev *smbdev = data;
+       unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END };
+       struct i2c_adapter *adapter;
+
+       adapter = i2c_verify_adapter(dev);
+       if (!adapter)
+               return 0;
+
+       if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HOST_NOTIFY))
+               return 0;
+
+       smbdev->client = i2c_new_probed_device(adapter, &smbdev->board,
+                                              addr_list, NULL);
+       if (!smbdev->client)
+               return 0;
+
+       /* We have our(?) device, stop iterating i2c bus. */
+       return 1;
+}
+
+void psmouse_smbus_cleanup(struct psmouse *psmouse)
+{
+       struct psmouse_smbus_dev *smbdev, *tmp;
+
+       mutex_lock(&psmouse_smbus_mutex);
+
+       list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) {
+               if (psmouse == smbdev->psmouse) {
+                       list_del(&smbdev->node);
+                       kfree(smbdev);
+               }
+       }
+
+       mutex_unlock(&psmouse_smbus_mutex);
+}
+
+int psmouse_smbus_init(struct psmouse *psmouse,
+                      const struct i2c_board_info *board,
+                      const void *pdata, size_t pdata_size,
+                      bool leave_breadcrumbs)
+{
+       struct psmouse_smbus_dev *smbdev;
+       int error;
+
+       smbdev = kzalloc(sizeof(*smbdev), GFP_KERNEL);
+       if (!smbdev)
+               return -ENOMEM;
+
+       smbdev->psmouse = psmouse;
+       smbdev->board = *board;
+
+       smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
+       if (!smbdev->board.platform_data) {
+               kfree(smbdev);
+               return -ENOMEM;
+       }
+
+       psmouse->private = smbdev;
+       psmouse->protocol_handler = psmouse_smbus_process_byte;
+       psmouse->reconnect = psmouse_smbus_reconnect;
+       psmouse->fast_reconnect = psmouse_smbus_reconnect;
+       psmouse->disconnect = psmouse_smbus_disconnect;
+       psmouse->resync_time = 0;
+
+       psmouse_deactivate(psmouse);
+
+       mutex_lock(&psmouse_smbus_mutex);
+       list_add_tail(&smbdev->node, &psmouse_smbus_list);
+       mutex_unlock(&psmouse_smbus_mutex);
+
+       /* Bind to already existing adapters right away */
+       error = i2c_for_each_dev(smbdev, psmouse_smbus_create_companion);
+
+       if (smbdev->client) {
+               /* We have our companion device */
+               return 0;
+       }
+
+       /*
+        * If we did not create i2c device we will not need platform
+        * data even if we are leaving breadcrumbs.
+        */
+       kfree(smbdev->board.platform_data);
+       smbdev->board.platform_data = NULL;
+
+       if (error < 0 || !leave_breadcrumbs) {
+               mutex_lock(&psmouse_smbus_mutex);
+               list_del(&smbdev->node);
+               mutex_unlock(&psmouse_smbus_mutex);
+
+               kfree(smbdev);
+       }
+
+       return error < 0 ? error : -EAGAIN;
+}
+
+int __init psmouse_smbus_module_init(void)
+{
+       int error;
+
+       error = bus_register_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
+       if (error) {
+               pr_err("failed to register i2c bus notifier: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
+
+void psmouse_smbus_module_exit(void)
+{
+       bus_unregister_notifier(&i2c_bus_type, &psmouse_smbus_notifier);
+       flush_scheduled_work();
+}
index 8c83b8e..38855e4 100644 (file)
@@ -44,21 +44,60 @@ enum psmouse_scale {
        PSMOUSE_SCALE21
 };
 
+enum psmouse_type {
+       PSMOUSE_NONE,
+       PSMOUSE_PS2,
+       PSMOUSE_PS2PP,
+       PSMOUSE_THINKPS,
+       PSMOUSE_GENPS,
+       PSMOUSE_IMPS,
+       PSMOUSE_IMEX,
+       PSMOUSE_SYNAPTICS,
+       PSMOUSE_ALPS,
+       PSMOUSE_LIFEBOOK,
+       PSMOUSE_TRACKPOINT,
+       PSMOUSE_TOUCHKIT_PS2,
+       PSMOUSE_CORTRON,
+       PSMOUSE_HGPK,
+       PSMOUSE_ELANTECH,
+       PSMOUSE_FSP,
+       PSMOUSE_SYNAPTICS_RELATIVE,
+       PSMOUSE_CYPRESS,
+       PSMOUSE_FOCALTECH,
+       PSMOUSE_VMMOUSE,
+       PSMOUSE_BYD,
+       PSMOUSE_SYNAPTICS_SMBUS,
+       PSMOUSE_AUTO            /* This one should always be last */
+};
+
+struct psmouse;
+
+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 */
+       bool smbus_companion; /* "Protocol" is a stub, device is on SMBus */
+       const char *name;
+       const char *alias;
+       int (*detect)(struct psmouse *, bool);
+       int (*init)(struct psmouse *);
+};
+
 struct psmouse {
        void *private;
        struct input_dev *dev;
        struct ps2dev ps2dev;
        struct delayed_work resync_work;
-       char *vendor;
-       char *name;
+       const char *vendor;
+       const char *name;
+       const struct psmouse_protocol *protocol;
        unsigned char packet[8];
        unsigned char badbyte;
        unsigned char pktcnt;
        unsigned char pktsize;
-       unsigned char type;
        unsigned char oob_data_type;
        unsigned char extra_buttons;
-       bool ignore_parity;
        bool acks_disable_command;
        unsigned int model;
        unsigned long last;
@@ -80,6 +119,7 @@ struct psmouse {
        void (*set_scale)(struct psmouse *psmouse, enum psmouse_scale scale);
 
        int (*reconnect)(struct psmouse *psmouse);
+       int (*fast_reconnect)(struct psmouse *psmouse);
        void (*disconnect)(struct psmouse *psmouse);
        void (*cleanup)(struct psmouse *psmouse);
        int (*poll)(struct psmouse *psmouse);
@@ -88,31 +128,6 @@ struct psmouse {
        void (*pt_deactivate)(struct psmouse *psmouse);
 };
 
-enum psmouse_type {
-       PSMOUSE_NONE,
-       PSMOUSE_PS2,
-       PSMOUSE_PS2PP,
-       PSMOUSE_THINKPS,
-       PSMOUSE_GENPS,
-       PSMOUSE_IMPS,
-       PSMOUSE_IMEX,
-       PSMOUSE_SYNAPTICS,
-       PSMOUSE_ALPS,
-       PSMOUSE_LIFEBOOK,
-       PSMOUSE_TRACKPOINT,
-       PSMOUSE_TOUCHKIT_PS2,
-       PSMOUSE_CORTRON,
-       PSMOUSE_HGPK,
-       PSMOUSE_ELANTECH,
-       PSMOUSE_FSP,
-       PSMOUSE_SYNAPTICS_RELATIVE,
-       PSMOUSE_CYPRESS,
-       PSMOUSE_FOCALTECH,
-       PSMOUSE_VMMOUSE,
-       PSMOUSE_BYD,
-       PSMOUSE_AUTO            /* This one should always be last */
-};
-
 void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
                unsigned long delay);
 int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command);
@@ -195,5 +210,34 @@ static struct psmouse_attribute psmouse_attr_##_name = {                   \
                   &(psmouse)->ps2dev.serio->dev,       \
                   psmouse_fmt(format), ##__VA_ARGS__)
 
+#ifdef CONFIG_MOUSE_PS2_SMBUS
+
+int psmouse_smbus_module_init(void);
+void psmouse_smbus_module_exit(void);
+
+struct i2c_board_info;
+
+int psmouse_smbus_init(struct psmouse *psmouse,
+                      const struct i2c_board_info *board,
+                      const void *pdata, size_t pdata_size,
+                      bool leave_breadcrumbs);
+void psmouse_smbus_cleanup(struct psmouse *psmouse);
+
+#else /* !CONFIG_MOUSE_PS2_SMBUS */
+
+static inline int psmouse_smbus_module_init(void)
+{
+       return 0;
+}
+
+static inline void psmouse_smbus_module_exit(void)
+{
+}
+
+static inline void psmouse_smbus_cleanup(struct psmouse *psmouse)
+{
+}
+
+#endif /* CONFIG_MOUSE_PS2_SMBUS */
 
 #endif /* _PSMOUSE_H */
index 597ee4b..d494c6c 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/input/mt.h>
 #include <linux/serio.h>
 #include <linux/libps2.h>
+#include <linux/rmi.h>
+#include <linux/i2c.h>
 #include <linux/slab.h>
 #include "psmouse.h"
 #include "synaptics.h"
@@ -119,59 +121,8 @@ void synaptics_reset(struct psmouse *psmouse)
        synaptics_mode_cmd(psmouse, 0);
 }
 
-#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
-
-static bool cr48_profile_sensor;
-
-#define ANY_BOARD_ID 0
-struct min_max_quirk {
-       const char * const *pnp_ids;
-       struct {
-               unsigned long int min, max;
-       } board_id;
-       int x_min, x_max, y_min, y_max;
-};
-
-static const struct min_max_quirk min_max_pnpid_table[] = {
-       {
-               (const char * const []){"LEN0033", NULL},
-               {ANY_BOARD_ID, ANY_BOARD_ID},
-               1024, 5052, 2258, 4832
-       },
-       {
-               (const char * const []){"LEN0042", NULL},
-               {ANY_BOARD_ID, ANY_BOARD_ID},
-               1232, 5710, 1156, 4696
-       },
-       {
-               (const char * const []){"LEN0034", "LEN0036", "LEN0037",
-                                       "LEN0039", "LEN2002", "LEN2004",
-                                       NULL},
-               {ANY_BOARD_ID, 2961},
-               1024, 5112, 2024, 4832
-       },
-       {
-               (const char * const []){"LEN2000", NULL},
-               {ANY_BOARD_ID, ANY_BOARD_ID},
-               1024, 5113, 2021, 4832
-       },
-       {
-               (const char * const []){"LEN2001", NULL},
-               {ANY_BOARD_ID, ANY_BOARD_ID},
-               1024, 5022, 2508, 4832
-       },
-       {
-               (const char * const []){"LEN2006", NULL},
-               {2691, 2691},
-               1024, 5045, 2457, 4832
-       },
-       {
-               (const char * const []){"LEN2006", NULL},
-               {ANY_BOARD_ID, ANY_BOARD_ID},
-               1264, 5675, 1171, 4688
-       },
-       { }
-};
+#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
+    defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
 
 /* This list has been kindly provided by Synaptics. */
 static const char * const topbuttonpad_pnp_ids[] = {
@@ -211,64 +162,98 @@ static const char * const topbuttonpad_pnp_ids[] = {
        NULL
 };
 
-/* This list has been kindly provided by Synaptics. */
-static const char * const forcepad_pnp_ids[] = {
-       "SYN300D",
-       "SYN3014",
+static const char * const smbus_pnp_ids[] = {
+       /* all of the topbuttonpad_pnp_ids are valid, we just add some extras */
+       "LEN0048", /* X1 Carbon 3 */
+       "LEN0046", /* X250 */
+       "LEN004a", /* W541 */
+       "LEN200f", /* T450s */
        NULL
 };
 
-/*****************************************************************************
- *     Synaptics communications functions
- ****************************************************************************/
-
 /*
- * Synaptics touchpads report the y coordinate from bottom to top, which is
- * opposite from what userspace expects.
- * This function is used to invert y before reporting.
+ * Send a command to the synpatics touchpad by special commands
  */
-static int synaptics_invert_y(int y)
+static int synaptics_send_cmd(struct psmouse *psmouse,
+                             unsigned char c, unsigned char *param)
 {
-       return YMAX_NOMINAL + YMIN_NOMINAL - y;
+       int error;
+
+       error = psmouse_sliced_command(psmouse, c);
+       if (error)
+               return error;
+
+       error = ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO);
+       if (error)
+               return error;
+
+       return 0;
 }
 
 /*
- * Send a command to the synpatics touchpad by special commands
+ * Identify Touchpad
+ * See also the SYN_ID_* macros
  */
-static int synaptics_send_cmd(struct psmouse *psmouse, unsigned char c, unsigned char *param)
+static int synaptics_identify(struct psmouse *psmouse,
+                             struct synaptics_device_info *info)
 {
-       if (psmouse_sliced_command(psmouse, c))
-               return -1;
-       if (ps2_command(&psmouse->ps2dev, param, PSMOUSE_CMD_GETINFO))
-               return -1;
-       return 0;
+       unsigned char id[3];
+       int error;
+
+       error = synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id);
+       if (error)
+               return error;
+
+       info->identity = (id[0] << 16) | (id[1] << 8) | id[2];
+       return SYN_ID_IS_SYNAPTICS(info->identity) ? 0 : -ENXIO;
 }
 
 /*
  * Read the model-id bytes from the touchpad
  * see also SYN_MODEL_* macros
  */
-static int synaptics_model_id(struct psmouse *psmouse)
+static int synaptics_model_id(struct psmouse *psmouse,
+                             struct synaptics_device_info *info)
 {
-       struct synaptics_data *priv = psmouse->private;
        unsigned char mi[3];
+       int error;
 
-       if (synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi))
-               return -1;
-       priv->model_id = (mi[0]<<16) | (mi[1]<<8) | mi[2];
+       error = synaptics_send_cmd(psmouse, SYN_QUE_MODEL, mi);
+       if (error)
+               return error;
+
+       info->model_id = (mi[0] << 16) | (mi[1] << 8) | mi[2];
        return 0;
 }
 
-static int synaptics_more_extended_queries(struct psmouse *psmouse)
+/*
+ * Read the firmware id from the touchpad
+ */
+static int synaptics_firmware_id(struct psmouse *psmouse,
+                                struct synaptics_device_info *info)
 {
-       struct synaptics_data *priv = psmouse->private;
-       unsigned char buf[3];
+       unsigned char fwid[3];
+       int error;
 
-       if (synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf))
-               return -1;
+       error = synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid);
+       if (error)
+               return error;
+
+       info->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
+       return 0;
+}
+
+static int synaptics_more_extended_queries(struct psmouse *psmouse,
+                                          struct synaptics_device_info *info)
+{
+       unsigned char buf[3];
+       int error;
 
-       priv->ext_cap_10 = (buf[0]<<16) | (buf[1]<<8) | buf[2];
+       error = synaptics_send_cmd(psmouse, SYN_QUE_MEXT_CAPAB_10, buf);
+       if (error)
+               return error;
 
+       info->ext_cap_10 = (buf[0] << 16) | (buf[1] << 8) | buf[2];
        return 0;
 }
 
@@ -276,36 +261,25 @@ static int synaptics_more_extended_queries(struct psmouse *psmouse)
  * Read the board id and the "More Extended Queries" from the touchpad
  * The board id is encoded in the "QUERY MODES" response
  */
-static int synaptics_query_modes(struct psmouse *psmouse)
+static int synaptics_query_modes(struct psmouse *psmouse,
+                                struct synaptics_device_info *info)
 {
-       struct synaptics_data *priv = psmouse->private;
        unsigned char bid[3];
+       int error;
 
        /* firmwares prior 7.5 have no board_id encoded */
-       if (SYN_ID_FULL(priv->identity) < 0x705)
+       if (SYN_ID_FULL(info->identity) < 0x705)
                return 0;
 
-       if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
-               return -1;
-       priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
+       error = synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid);
+       if (error)
+               return error;
 
-       if (SYN_MEXT_CAP_BIT(bid[0]))
-               return synaptics_more_extended_queries(psmouse);
+       info->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
 
-       return 0;
-}
-
-/*
- * Read the firmware id from the touchpad
- */
-static int synaptics_firmware_id(struct psmouse *psmouse)
-{
-       struct synaptics_data *priv = psmouse->private;
-       unsigned char fwid[3];
+       if (SYN_MEXT_CAP_BIT(bid[0]))
+               return synaptics_more_extended_queries(psmouse, info);
 
-       if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid))
-               return -1;
-       priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
        return 0;
 }
 
@@ -313,139 +287,239 @@ static int synaptics_firmware_id(struct psmouse *psmouse)
  * Read the capability-bits from the touchpad
  * see also the SYN_CAP_* macros
  */
-static int synaptics_capability(struct psmouse *psmouse)
+static int synaptics_capability(struct psmouse *psmouse,
+                               struct synaptics_device_info *info)
 {
-       struct synaptics_data *priv = psmouse->private;
        unsigned char cap[3];
+       int error;
 
-       if (synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap))
-               return -1;
-       priv->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
-       priv->ext_cap = priv->ext_cap_0c = 0;
+       error = synaptics_send_cmd(psmouse, SYN_QUE_CAPABILITIES, cap);
+       if (error)
+               return error;
+
+       info->capabilities = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+       info->ext_cap = info->ext_cap_0c = 0;
 
        /*
         * Older firmwares had submodel ID fixed to 0x47
         */
-       if (SYN_ID_FULL(priv->identity) < 0x705 &&
-           SYN_CAP_SUBMODEL_ID(priv->capabilities) != 0x47) {
-               return -1;
+       if (SYN_ID_FULL(info->identity) < 0x705 &&
+           SYN_CAP_SUBMODEL_ID(info->capabilities) != 0x47) {
+               return -ENXIO;
        }
 
        /*
         * Unless capExtended is set the rest of the flags should be ignored
         */
-       if (!SYN_CAP_EXTENDED(priv->capabilities))
-               priv->capabilities = 0;
+       if (!SYN_CAP_EXTENDED(info->capabilities))
+               info->capabilities = 0;
 
-       if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 1) {
+       if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 1) {
                if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB, cap)) {
                        psmouse_warn(psmouse,
                                     "device claims to have extended capabilities, but I'm not able to read them.\n");
                } else {
-                       priv->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
+                       info->ext_cap = (cap[0] << 16) | (cap[1] << 8) | cap[2];
 
                        /*
                         * if nExtBtn is greater than 8 it should be considered
                         * invalid and treated as 0
                         */
-                       if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 8)
-                               priv->ext_cap &= 0xff0fff;
+                       if (SYN_CAP_MULTI_BUTTON_NO(info->ext_cap) > 8)
+                               info->ext_cap &= 0xff0fff;
                }
        }
 
-       if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 4) {
-               if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap)) {
+       if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 4) {
+               error = synaptics_send_cmd(psmouse, SYN_QUE_EXT_CAPAB_0C, cap);
+               if (error)
                        psmouse_warn(psmouse,
                                     "device claims to have extended capability 0x0c, but I'm not able to read it.\n");
-               } else {
-                       priv->ext_cap_0c = (cap[0] << 16) | (cap[1] << 8) | cap[2];
-               }
+               else
+                       info->ext_cap_0c =
+                               (cap[0] << 16) | (cap[1] << 8) | cap[2];
        }
 
        return 0;
 }
 
 /*
- * Identify Touchpad
- * See also the SYN_ID_* macros
- */
-static int synaptics_identify(struct psmouse *psmouse)
-{
-       struct synaptics_data *priv = psmouse->private;
-       unsigned char id[3];
-
-       if (synaptics_send_cmd(psmouse, SYN_QUE_IDENTIFY, id))
-               return -1;
-       priv->identity = (id[0]<<16) | (id[1]<<8) | id[2];
-       if (SYN_ID_IS_SYNAPTICS(priv->identity))
-               return 0;
-       return -1;
-}
-
-/*
  * Read touchpad resolution and maximum reported coordinates
  * Resolution is left zero if touchpad does not support the query
  */
-
-static int synaptics_resolution(struct psmouse *psmouse)
+static int synaptics_resolution(struct psmouse *psmouse,
+                               struct synaptics_device_info *info)
 {
-       struct synaptics_data *priv = psmouse->private;
        unsigned char resp[3];
+       int error;
 
-       if (SYN_ID_MAJOR(priv->identity) < 4)
+       if (SYN_ID_MAJOR(info->identity) < 4)
                return 0;
 
-       if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp) == 0) {
+       error = synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, resp);
+       if (!error) {
                if (resp[0] != 0 && (resp[1] & 0x80) && resp[2] != 0) {
-                       priv->x_res = resp[0]; /* x resolution in units/mm */
-                       priv->y_res = resp[2]; /* y resolution in units/mm */
+                       info->x_res = resp[0]; /* x resolution in units/mm */
+                       info->y_res = resp[2]; /* y resolution in units/mm */
                }
        }
 
-       if (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 5 &&
-           SYN_CAP_MAX_DIMENSIONS(priv->ext_cap_0c)) {
-               if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MAX_COORDS, resp)) {
+       if (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 5 &&
+           SYN_CAP_MAX_DIMENSIONS(info->ext_cap_0c)) {
+               error = synaptics_send_cmd(psmouse,
+                                          SYN_QUE_EXT_MAX_COORDS, resp);
+               if (error) {
                        psmouse_warn(psmouse,
                                     "device claims to have max coordinates query, but I'm not able to read it.\n");
                } else {
-                       priv->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
-                       priv->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+                       info->x_max = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+                       info->y_max = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
                        psmouse_info(psmouse,
                                     "queried max coordinates: x [..%d], y [..%d]\n",
-                                    priv->x_max, priv->y_max);
+                                    info->x_max, info->y_max);
                }
        }
 
-       if (SYN_CAP_MIN_DIMENSIONS(priv->ext_cap_0c) &&
-           (SYN_EXT_CAP_REQUESTS(priv->capabilities) >= 7 ||
+       if (SYN_CAP_MIN_DIMENSIONS(info->ext_cap_0c) &&
+           (SYN_EXT_CAP_REQUESTS(info->capabilities) >= 7 ||
             /*
              * Firmware v8.1 does not report proper number of extended
              * capabilities, but has been proven to report correct min
              * coordinates.
              */
-            SYN_ID_FULL(priv->identity) == 0x801)) {
-               if (synaptics_send_cmd(psmouse, SYN_QUE_EXT_MIN_COORDS, resp)) {
+            SYN_ID_FULL(info->identity) == 0x801)) {
+               error = synaptics_send_cmd(psmouse,
+                                          SYN_QUE_EXT_MIN_COORDS, resp);
+               if (error) {
                        psmouse_warn(psmouse,
                                     "device claims to have min coordinates query, but I'm not able to read it.\n");
                } else {
-                       priv->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
-                       priv->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
+                       info->x_min = (resp[0] << 5) | ((resp[1] & 0x0f) << 1);
+                       info->y_min = (resp[2] << 5) | ((resp[1] & 0xf0) >> 3);
                        psmouse_info(psmouse,
                                     "queried min coordinates: x [%d..], y [%d..]\n",
-                                    priv->x_min, priv->y_min);
+                                    info->x_min, info->y_min);
                }
        }
 
        return 0;
 }
 
+static int synaptics_query_hardware(struct psmouse *psmouse,
+                                   struct synaptics_device_info *info)
+{
+       int error;
+
+       error = synaptics_identify(psmouse, info);
+       if (error)
+               return error;
+
+       error = synaptics_model_id(psmouse, info);
+       if (error)
+               return error;
+
+       error = synaptics_firmware_id(psmouse, info);
+       if (error)
+               return error;
+
+       error = synaptics_query_modes(psmouse, info);
+       if (error)
+               return error;
+
+       error = synaptics_capability(psmouse, info);
+       if (error)
+               return error;
+
+       error = synaptics_resolution(psmouse, info);
+       if (error)
+               return error;
+
+       return 0;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
+
+static bool cr48_profile_sensor;
+
+#define ANY_BOARD_ID 0
+struct min_max_quirk {
+       const char * const *pnp_ids;
+       struct {
+               u32 min, max;
+       } board_id;
+       u32 x_min, x_max, y_min, y_max;
+};
+
+static const struct min_max_quirk min_max_pnpid_table[] = {
+       {
+               (const char * const []){"LEN0033", NULL},
+               {ANY_BOARD_ID, ANY_BOARD_ID},
+               1024, 5052, 2258, 4832
+       },
+       {
+               (const char * const []){"LEN0042", NULL},
+               {ANY_BOARD_ID, ANY_BOARD_ID},
+               1232, 5710, 1156, 4696
+       },
+       {
+               (const char * const []){"LEN0034", "LEN0036", "LEN0037",
+                                       "LEN0039", "LEN2002", "LEN2004",
+                                       NULL},
+               {ANY_BOARD_ID, 2961},
+               1024, 5112, 2024, 4832
+       },
+       {
+               (const char * const []){"LEN2000", NULL},
+               {ANY_BOARD_ID, ANY_BOARD_ID},
+               1024, 5113, 2021, 4832
+       },
+       {
+               (const char * const []){"LEN2001", NULL},
+               {ANY_BOARD_ID, ANY_BOARD_ID},
+               1024, 5022, 2508, 4832
+       },
+       {
+               (const char * const []){"LEN2006", NULL},
+               {2691, 2691},
+               1024, 5045, 2457, 4832
+       },
+       {
+               (const char * const []){"LEN2006", NULL},
+               {ANY_BOARD_ID, ANY_BOARD_ID},
+               1264, 5675, 1171, 4688
+       },
+       { }
+};
+
+/* This list has been kindly provided by Synaptics. */
+static const char * const forcepad_pnp_ids[] = {
+       "SYN300D",
+       "SYN3014",
+       NULL
+};
+
+/*****************************************************************************
+ *     Synaptics communications functions
+ ****************************************************************************/
+
+/*
+ * Synaptics touchpads report the y coordinate from bottom to top, which is
+ * opposite from what userspace expects.
+ * This function is used to invert y before reporting.
+ */
+static int synaptics_invert_y(int y)
+{
+       return YMAX_NOMINAL + YMIN_NOMINAL - y;
+}
+
 /*
  * Apply quirk(s) if the hardware matches
  */
-
-static void synaptics_apply_quirks(struct psmouse *psmouse)
+static void synaptics_apply_quirks(struct psmouse *psmouse,
+                                  struct synaptics_device_info *info)
 {
-       struct synaptics_data *priv = psmouse->private;
        int i;
 
        for (i = 0; min_max_pnpid_table[i].pnp_ids; i++) {
@@ -454,52 +528,32 @@ static void synaptics_apply_quirks(struct psmouse *psmouse)
                        continue;
 
                if (min_max_pnpid_table[i].board_id.min != ANY_BOARD_ID &&
-                   priv->board_id < min_max_pnpid_table[i].board_id.min)
+                   info->board_id < min_max_pnpid_table[i].board_id.min)
                        continue;
 
                if (min_max_pnpid_table[i].board_id.max != ANY_BOARD_ID &&
-                   priv->board_id > min_max_pnpid_table[i].board_id.max)
+                   info->board_id > min_max_pnpid_table[i].board_id.max)
                        continue;
 
-               priv->x_min = min_max_pnpid_table[i].x_min;
-               priv->x_max = min_max_pnpid_table[i].x_max;
-               priv->y_min = min_max_pnpid_table[i].y_min;
-               priv->y_max = min_max_pnpid_table[i].y_max;
+               info->x_min = min_max_pnpid_table[i].x_min;
+               info->x_max = min_max_pnpid_table[i].x_max;
+               info->y_min = min_max_pnpid_table[i].y_min;
+               info->y_max = min_max_pnpid_table[i].y_max;
                psmouse_info(psmouse,
                             "quirked min/max coordinates: x [%d..%d], y [%d..%d]\n",
-                            priv->x_min, priv->x_max,
-                            priv->y_min, priv->y_max);
+                            info->x_min, info->x_max,
+                            info->y_min, info->y_max);
                break;
        }
 }
 
-static int synaptics_query_hardware(struct psmouse *psmouse)
-{
-       if (synaptics_identify(psmouse))
-               return -1;
-       if (synaptics_model_id(psmouse))
-               return -1;
-       if (synaptics_firmware_id(psmouse))
-               return -1;
-       if (synaptics_query_modes(psmouse))
-               return -1;
-       if (synaptics_capability(psmouse))
-               return -1;
-       if (synaptics_resolution(psmouse))
-               return -1;
-
-       synaptics_apply_quirks(psmouse);
-
-       return 0;
-}
-
 static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
 {
        static unsigned char param = 0xc8;
        struct synaptics_data *priv = psmouse->private;
 
-       if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
-             SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
+       if (!(SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
+             SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)))
                return 0;
 
        if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
@@ -509,7 +563,7 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
                return -1;
 
        /* Advanced gesture mode also sends multi finger data */
-       priv->capabilities |= BIT(1);
+       priv->info.capabilities |= BIT(1);
 
        return 0;
 }
@@ -525,7 +579,7 @@ static int synaptics_set_mode(struct psmouse *psmouse)
                priv->mode |= SYN_BIT_DISABLE_GESTURE;
        if (psmouse->rate >= 80)
                priv->mode |= SYN_BIT_HIGH_RATE;
-       if (SYN_CAP_EXTENDED(priv->capabilities))
+       if (SYN_CAP_EXTENDED(priv->info.capabilities))
                priv->mode |= SYN_BIT_W_MODE;
 
        if (synaptics_mode_cmd(psmouse, priv->mode))
@@ -693,7 +747,7 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[],
                                        struct synaptics_hw_state *hw)
 {
        unsigned int ext_bits =
-               (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+               (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
        unsigned int ext_mask = GENMASK(ext_bits - 1, 0);
 
        hw->ext_buttons = buf[4] & ext_mask;
@@ -706,13 +760,13 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
 {
        memset(hw, 0, sizeof(struct synaptics_hw_state));
 
-       if (SYN_MODEL_NEWABS(priv->model_id)) {
+       if (SYN_MODEL_NEWABS(priv->info.model_id)) {
                hw->w = (((buf[0] & 0x30) >> 2) |
                         ((buf[0] & 0x04) >> 1) |
                         ((buf[3] & 0x04) >> 2));
 
-               if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
-                       SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+               if ((SYN_CAP_ADV_GESTURE(priv->info.ext_cap_0c) ||
+                       SYN_CAP_IMAGE_SENSOR(priv->info.ext_cap_0c)) &&
                    hw->w == 2) {
                        synaptics_parse_agm(buf, priv, hw);
                        return 1;
@@ -765,7 +819,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
 
                        hw->left = priv->report_press;
 
-               } else if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+               } else if (SYN_CAP_CLICKPAD(priv->info.ext_cap_0c)) {
                        /*
                         * Clickpad's button is transmitted as middle button,
                         * however, since it is primary button, we will report
@@ -773,18 +827,18 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
                         */
                        hw->left = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
 
-               } else if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+               } else if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities)) {
                        hw->middle = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
                        if (hw->w == 2)
                                hw->scroll = (signed char)(buf[1]);
                }
 
-               if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+               if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
                        hw->up   = ((buf[0] ^ buf[3]) & 0x01) ? 1 : 0;
                        hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
                }
 
-               if (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) > 0 &&
+               if (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) > 0 &&
                    ((buf[0] ^ buf[3]) & 0x02)) {
                        synaptics_parse_ext_buttons(buf, priv, hw);
                }
@@ -853,19 +907,19 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
 {
        struct input_dev *dev = psmouse->dev;
        struct synaptics_data *priv = psmouse->private;
-       int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap) + 1) >> 1;
+       int ext_bits = (SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap) + 1) >> 1;
        int i;
 
-       if (!SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap))
+       if (!SYN_CAP_MULTI_BUTTON_NO(priv->info.ext_cap))
                return;
 
        /* Bug in FW 8.1 & 8.2, buttons are reported only when ExtBit is 1 */
-       if ((SYN_ID_FULL(priv->identity) == 0x801 ||
-            SYN_ID_FULL(priv->identity) == 0x802) &&
+       if ((SYN_ID_FULL(priv->info.identity) == 0x801 ||
+            SYN_ID_FULL(priv->info.identity) == 0x802) &&
            !((psmouse->packet[0] ^ psmouse->packet[3]) & 0x02))
                return;
 
-       if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10)) {
+       if (!SYN_CAP_EXT_BUTTONS_STICK(priv->info.ext_cap_10)) {
                for (i = 0; i < ext_bits; i++) {
                        input_report_key(dev, BTN_0 + 2 * i,
                                hw->ext_buttons & (1 << i));
@@ -884,9 +938,9 @@ static void synaptics_report_ext_buttons(struct psmouse *psmouse,
                u8 pt_buttons;
 
                /* The trackstick expects at most 3 buttons */
-               pt_buttons = SYN_CAP_EXT_BUTTON_STICK_L(hw->ext_buttons)      |
-                            SYN_CAP_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
-                            SYN_CAP_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
+               pt_buttons = SYN_EXT_BUTTON_STICK_L(hw->ext_buttons)      |
+                            SYN_EXT_BUTTON_STICK_R(hw->ext_buttons) << 1 |
+                            SYN_EXT_BUTTON_STICK_M(hw->ext_buttons) << 2;
 
                serio_interrupt(priv->pt_port,
                                PSMOUSE_OOB_EXTRA_BTNS, SERIO_OOB_DATA);
@@ -903,10 +957,10 @@ static void synaptics_report_buttons(struct psmouse *psmouse,
        input_report_key(dev, BTN_LEFT, hw->left);
        input_report_key(dev, BTN_RIGHT, hw->right);
 
-       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+       if (SYN_CAP_MIDDLE_BUTTON(priv->info.capabilities))
                input_report_key(dev, BTN_MIDDLE, hw->middle);
 
-       if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+       if (SYN_CAP_FOUR_BUTTON(priv->info.capabilities)) {
                input_report_key(dev, BTN_FORWARD, hw->up);
                input_report_key(dev, BTN_BACK, hw->down);
        }
@@ -931,7 +985,7 @@ static void synaptics_report_mt_data(struct psmouse *psmouse,
                pos[i].y = synaptics_invert_y(hw[i]->y);
        }
 
-       input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->x_res);
+       input_mt_assign_slots(dev, slot, pos, nsemi, DMAX * priv->info.x_res);
 
        for (i = 0; i < nsemi; i++) {
                input_mt_slot(dev, slot[i]);
@@ -985,6 +1039,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
 {
        struct input_dev *dev = psmouse->dev;
        struct synaptics_data *priv = psmouse->private;
+       struct synaptics_device_info *info = &priv->info;
        struct synaptics_hw_state hw;
        int num_fingers;
        int finger_width;
@@ -992,7 +1047,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
                return;
 
-       if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+       if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
                synaptics_image_sensor_process(psmouse, &hw);
                return;
        }
@@ -1020,18 +1075,18 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        if (hw.z > 0 && hw.x > 1) {
                num_fingers = 1;
                finger_width = 5;
-               if (SYN_CAP_EXTENDED(priv->capabilities)) {
+               if (SYN_CAP_EXTENDED(info->capabilities)) {
                        switch (hw.w) {
                        case 0 ... 1:
-                               if (SYN_CAP_MULTIFINGER(priv->capabilities))
+                               if (SYN_CAP_MULTIFINGER(info->capabilities))
                                        num_fingers = hw.w + 2;
                                break;
                        case 2:
-                               if (SYN_MODEL_PEN(priv->model_id))
+                               if (SYN_MODEL_PEN(info->model_id))
                                        ;   /* Nothing, treat a pen as a single finger */
                                break;
                        case 4 ... 15:
-                               if (SYN_CAP_PALMDETECT(priv->capabilities))
+                               if (SYN_CAP_PALMDETECT(info->capabilities))
                                        finger_width = hw.w;
                                break;
                        }
@@ -1046,7 +1101,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
                return;
        }
 
-       if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+       if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c))
                synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
                                              num_fingers);
 
@@ -1063,11 +1118,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        }
        input_report_abs(dev, ABS_PRESSURE, hw.z);
 
-       if (SYN_CAP_PALMDETECT(priv->capabilities))
+       if (SYN_CAP_PALMDETECT(info->capabilities))
                input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
 
        input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
-       if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+       if (SYN_CAP_MULTIFINGER(info->capabilities)) {
                input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
                input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
        }
@@ -1129,7 +1184,7 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
                if (unlikely(priv->pkt_type == SYN_NEWABS))
                        priv->pkt_type = synaptics_detect_pkt_type(psmouse);
 
-               if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+               if (SYN_CAP_PASS_THROUGH(priv->info.capabilities) &&
                    synaptics_is_pt_packet(psmouse->packet)) {
                        if (priv->pt_port)
                                synaptics_pass_pt_packet(priv->pt_port,
@@ -1148,26 +1203,27 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
  *     Driver initialization/cleanup functions
  ****************************************************************************/
 static void set_abs_position_params(struct input_dev *dev,
-                                   struct synaptics_data *priv, int x_code,
-                                   int y_code)
+                                   struct synaptics_device_info *info,
+                                   int x_code, int y_code)
 {
-       int x_min = priv->x_min ?: XMIN_NOMINAL;
-       int x_max = priv->x_max ?: XMAX_NOMINAL;
-       int y_min = priv->y_min ?: YMIN_NOMINAL;
-       int y_max = priv->y_max ?: YMAX_NOMINAL;
-       int fuzz = SYN_CAP_REDUCED_FILTERING(priv->ext_cap_0c) ?
+       int x_min = info->x_min ?: XMIN_NOMINAL;
+       int x_max = info->x_max ?: XMAX_NOMINAL;
+       int y_min = info->y_min ?: YMIN_NOMINAL;
+       int y_max = info->y_max ?: YMAX_NOMINAL;
+       int fuzz = SYN_CAP_REDUCED_FILTERING(info->ext_cap_0c) ?
                        SYN_REDUCED_FILTER_FUZZ : 0;
 
        input_set_abs_params(dev, x_code, x_min, x_max, fuzz, 0);
        input_set_abs_params(dev, y_code, y_min, y_max, fuzz, 0);
-       input_abs_set_res(dev, x_code, priv->x_res);
-       input_abs_set_res(dev, y_code, priv->y_res);
+       input_abs_set_res(dev, x_code, info->x_res);
+       input_abs_set_res(dev, y_code, info->y_res);
 }
 
 static void set_input_params(struct psmouse *psmouse,
                             struct synaptics_data *priv)
 {
        struct input_dev *dev = psmouse->dev;
+       struct synaptics_device_info *info = &priv->info;
        int i;
 
        /* Things that apply to both modes */
@@ -1176,7 +1232,7 @@ static void set_input_params(struct psmouse *psmouse,
        __set_bit(BTN_LEFT, dev->keybit);
        __set_bit(BTN_RIGHT, dev->keybit);
 
-       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+       if (SYN_CAP_MIDDLE_BUTTON(info->capabilities))
                __set_bit(BTN_MIDDLE, dev->keybit);
 
        if (!priv->absolute_mode) {
@@ -1189,15 +1245,15 @@ static void set_input_params(struct psmouse *psmouse,
 
        /* Absolute mode */
        __set_bit(EV_ABS, dev->evbit);
-       set_abs_position_params(dev, priv, ABS_X, ABS_Y);
+       set_abs_position_params(dev, &priv->info, ABS_X, ABS_Y);
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
 
        if (cr48_profile_sensor)
                input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
 
-       if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
-               set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
-                                       ABS_MT_POSITION_Y);
+       if (SYN_CAP_IMAGE_SENSOR(info->ext_cap_0c)) {
+               set_abs_position_params(dev, info,
+                                       ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
                /* Image sensors can report per-contact pressure */
                input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
                input_mt_init_slots(dev, 2, INPUT_MT_POINTER | INPUT_MT_TRACK);
@@ -1205,9 +1261,9 @@ static void set_input_params(struct psmouse *psmouse,
                /* Image sensors can signal 4 and 5 finger clicks */
                __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
                __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
-       } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
-               set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
-                                       ABS_MT_POSITION_Y);
+       } else if (SYN_CAP_ADV_GESTURE(info->ext_cap_0c)) {
+               set_abs_position_params(dev, info,
+                                       ABS_MT_POSITION_X, ABS_MT_POSITION_Y);
                /*
                 * Profile sensor in CR-48 tracks contacts reasonably well,
                 * other non-image sensors with AGM use semi-mt.
@@ -1218,35 +1274,35 @@ static void set_input_params(struct psmouse *psmouse,
                                        INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
        }
 
-       if (SYN_CAP_PALMDETECT(priv->capabilities))
+       if (SYN_CAP_PALMDETECT(info->capabilities))
                input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
 
        __set_bit(BTN_TOUCH, dev->keybit);
        __set_bit(BTN_TOOL_FINGER, dev->keybit);
 
-       if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
+       if (SYN_CAP_MULTIFINGER(info->capabilities)) {
                __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
                __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
        }
 
-       if (SYN_CAP_FOUR_BUTTON(priv->capabilities) ||
-           SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) {
+       if (SYN_CAP_FOUR_BUTTON(info->capabilities) ||
+           SYN_CAP_MIDDLE_BUTTON(info->capabilities)) {
                __set_bit(BTN_FORWARD, dev->keybit);
                __set_bit(BTN_BACK, dev->keybit);
        }
 
-       if (!SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
-               for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+       if (!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
+               for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(info->ext_cap); i++)
                        __set_bit(BTN_0 + i, dev->keybit);
 
        __clear_bit(EV_REL, dev->evbit);
        __clear_bit(REL_X, dev->relbit);
        __clear_bit(REL_Y, dev->relbit);
 
-       if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
+       if (SYN_CAP_CLICKPAD(info->ext_cap_0c)) {
                __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
                if (psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
-                   !SYN_CAP_EXT_BUTTONS_STICK(priv->ext_cap_10))
+                   !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10))
                        __set_bit(INPUT_PROP_TOPBUTTONPAD, dev->propbit);
                /* Clickpads report only left button */
                __clear_bit(BTN_RIGHT, dev->keybit);
@@ -1300,7 +1356,14 @@ static void synaptics_disconnect(struct psmouse *psmouse)
 {
        struct synaptics_data *priv = psmouse->private;
 
-       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity))
+       /*
+        * We might have left a breadcrumb when trying to
+        * set up SMbus companion.
+        */
+       psmouse_smbus_cleanup(psmouse);
+
+       if (!priv->absolute_mode &&
+                       SYN_ID_DISGEST_SUPPORTED(priv->info.identity))
                device_remove_file(&psmouse->ps2dev.serio->dev,
                                   &psmouse_attr_disable_gesture.dattr);
 
@@ -1312,7 +1375,7 @@ static void synaptics_disconnect(struct psmouse *psmouse)
 static int synaptics_reconnect(struct psmouse *psmouse)
 {
        struct synaptics_data *priv = psmouse->private;
-       struct synaptics_data old_priv = *priv;
+       struct synaptics_device_info info;
        unsigned char param[2];
        int retry = 0;
        int error;
@@ -1339,7 +1402,7 @@ static int synaptics_reconnect(struct psmouse *psmouse)
        if (retry > 1)
                psmouse_dbg(psmouse, "reconnected after %d tries\n", retry);
 
-       if (synaptics_query_hardware(psmouse)) {
+       if (synaptics_query_hardware(psmouse, &info)) {
                psmouse_err(psmouse, "Unable to query device.\n");
                return -1;
        }
@@ -1349,16 +1412,16 @@ static int synaptics_reconnect(struct psmouse *psmouse)
                return -1;
        }
 
-       if (old_priv.identity != priv->identity ||
-           old_priv.model_id != priv->model_id ||
-           old_priv.capabilities != priv->capabilities ||
-           old_priv.ext_cap != priv->ext_cap) {
+       if (info.identity != priv->info.identity ||
+           info.model_id != priv->info.model_id ||
+           info.capabilities != priv->info.capabilities ||
+           info.ext_cap != priv->info.ext_cap) {
                psmouse_err(psmouse,
-                           "hardware appears to be different: id(%ld-%ld), model(%ld-%ld), caps(%lx-%lx), ext(%lx-%lx).\n",
-                           old_priv.identity, priv->identity,
-                           old_priv.model_id, priv->model_id,
-                           old_priv.capabilities, priv->capabilities,
-                           old_priv.ext_cap, priv->ext_cap);
+                           "hardware appears to be different: id(%u-%u), model(%u-%u), caps(%x-%x), ext(%x-%x).\n",
+                           priv->info.identity, info.identity,
+                           priv->info.model_id, info.model_id,
+                           priv->info.capabilities, info.capabilities,
+                           priv->info.ext_cap, info.ext_cap);
                return -1;
        }
 
@@ -1439,36 +1502,22 @@ void __init synaptics_module_init(void)
        cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
 }
 
-static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+static int synaptics_init_ps2(struct psmouse *psmouse,
+                             struct synaptics_device_info *info,
+                             bool absolute_mode)
 {
        struct synaptics_data *priv;
-       int err = -1;
+       int err;
 
-       /*
-        * The OLPC XO has issues with Synaptics' absolute mode; the constant
-        * packet spew overloads the EC such that key presses on the keyboard
-        * are missed.  Given that, don't even attempt to use Absolute mode.
-        * Relative mode seems to work just fine.
-        */
-       if (absolute_mode && broken_olpc_ec) {
-               psmouse_info(psmouse,
-                            "OLPC XO detected, not enabling Synaptics protocol.\n");
-               return -ENODEV;
-       }
+       synaptics_apply_quirks(psmouse, info);
 
        psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       psmouse_reset(psmouse);
-
-       if (synaptics_query_hardware(psmouse)) {
-               psmouse_err(psmouse, "Unable to query device.\n");
-               goto init_fail;
-       }
-
+       priv->info = *info;
        priv->absolute_mode = absolute_mode;
-       if (SYN_ID_DISGEST_SUPPORTED(priv->identity))
+       if (SYN_ID_DISGEST_SUPPORTED(info->identity))
                priv->disable_gesture = true;
 
        /*
@@ -1477,20 +1526,22 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
         */
        priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids);
 
-       if (synaptics_set_mode(psmouse)) {
+       err = synaptics_set_mode(psmouse);
+       if (err) {
                psmouse_err(psmouse, "Unable to initialize device.\n");
                goto init_fail;
        }
 
-       priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
+       priv->pkt_type = SYN_MODEL_NEWABS(info->model_id) ?
+                                       SYN_NEWABS : SYN_OLDABS;
 
        psmouse_info(psmouse,
-                    "Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n",
-                    SYN_ID_MODEL(priv->identity),
-                    SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
-                    priv->model_id,
-                    priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
-                    priv->ext_cap_10, priv->board_id, priv->firmware_id);
+                    "Touchpad model: %u, fw: %u.%u, id: %#x, caps: %#x/%#x/%#x/%#x, board id: %u, fw id: %u\n",
+                    SYN_ID_MODEL(info->identity),
+                    SYN_ID_MAJOR(info->identity), SYN_ID_MINOR(info->identity),
+                    info->model_id,
+                    info->capabilities, info->ext_cap, info->ext_cap_0c,
+                    info->ext_cap_10, info->board_id, info->firmware_id);
 
        set_input_params(psmouse, priv);
 
@@ -1501,8 +1552,8 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
         * Hardware info bits seem to be good candidates as they
         * are documented to be for Synaptics corp. internal use.
         */
-       psmouse->model = ((priv->model_id & 0x00ff0000) >> 8) |
-                         (priv->model_id & 0x000000ff);
+       psmouse->model = ((info->model_id & 0x00ff0000) >> 8) |
+                         (info->model_id & 0x000000ff);
 
        if (absolute_mode) {
                psmouse->protocol_handler = synaptics_process_byte;
@@ -1520,7 +1571,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
        /* Synaptics can usually stay in sync without extra help */
        psmouse->resync_time = 0;
 
-       if (SYN_CAP_PASS_THROUGH(priv->capabilities))
+       if (SYN_CAP_PASS_THROUGH(info->capabilities))
                synaptics_pt_create(psmouse);
 
        /*
@@ -1535,7 +1586,7 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
                psmouse->rate = 40;
        }
 
-       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(priv->identity)) {
+       if (!priv->absolute_mode && SYN_ID_DISGEST_SUPPORTED(info->identity)) {
                err = device_create_file(&psmouse->ps2dev.serio->dev,
                                         &psmouse_attr_disable_gesture.dattr);
                if (err) {
@@ -1553,7 +1604,23 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
        return err;
 }
 
-int synaptics_init(struct psmouse *psmouse)
+static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
+{
+       struct synaptics_device_info info;
+       int error;
+
+       psmouse_reset(psmouse);
+
+       error = synaptics_query_hardware(psmouse, &info);
+       if (error) {
+               psmouse_err(psmouse, "Unable to query device: %d\n", error);
+               return error;
+       }
+
+       return synaptics_init_ps2(psmouse, &info, absolute_mode);
+}
+
+int synaptics_init_absolute(struct psmouse *psmouse)
 {
        return __synaptics_init(psmouse, true);
 }
@@ -1563,15 +1630,204 @@ int synaptics_init_relative(struct psmouse *psmouse)
        return __synaptics_init(psmouse, false);
 }
 
+static int synaptics_setup_ps2(struct psmouse *psmouse,
+                              struct synaptics_device_info *info)
+{
+       bool absolute_mode = true;
+       int error;
+
+       /*
+        * The OLPC XO has issues with Synaptics' absolute mode; the constant
+        * packet spew overloads the EC such that key presses on the keyboard
+        * are missed.  Given that, don't even attempt to use Absolute mode.
+        * Relative mode seems to work just fine.
+        */
+       if (broken_olpc_ec) {
+               psmouse_info(psmouse,
+                            "OLPC XO detected, forcing relative protocol.\n");
+               absolute_mode = false;
+       }
+
+       error = synaptics_init_ps2(psmouse, info, absolute_mode);
+       if (error)
+               return error;
+
+       return absolute_mode ? PSMOUSE_SYNAPTICS : PSMOUSE_SYNAPTICS_RELATIVE;
+}
+
 #else /* CONFIG_MOUSE_PS2_SYNAPTICS */
 
 void __init synaptics_module_init(void)
 {
 }
 
-int synaptics_init(struct psmouse *psmouse)
+static int __maybe_unused
+synaptics_setup_ps2(struct psmouse *psmouse,
+                   struct synaptics_device_info *info)
 {
        return -ENOSYS;
 }
 
 #endif /* CONFIG_MOUSE_PS2_SYNAPTICS */
+
+#ifdef CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS
+
+/*
+ * The newest Synaptics device can use a secondary bus (called InterTouch) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+       SYNAPTICS_INTERTOUCH_NOT_SET = -1,
+       SYNAPTICS_INTERTOUCH_OFF,
+       SYNAPTICS_INTERTOUCH_ON,
+};
+
+static int synaptics_intertouch = SYNAPTICS_INTERTOUCH_NOT_SET;
+module_param_named(synaptics_intertouch, synaptics_intertouch, int, 0644);
+MODULE_PARM_DESC(synaptics_intertouch, "Use a secondary bus for the Synaptics device.");
+
+static int synaptics_create_intertouch(struct psmouse *psmouse,
+                                      struct synaptics_device_info *info,
+                                      bool leave_breadcrumbs)
+{
+       bool topbuttonpad =
+               psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+               !SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10);
+       const struct rmi_device_platform_data pdata = {
+               .sensor_pdata = {
+                       .sensor_type = rmi_sensor_touchpad,
+                       .axis_align.flip_y = true,
+                       /* to prevent cursors jumps: */
+                       .kernel_tracking = true,
+                       .topbuttonpad = topbuttonpad,
+               },
+               .f30_data = {
+                       .buttonpad = SYN_CAP_CLICKPAD(info->ext_cap_0c),
+                       .trackstick_buttons =
+                               !!SYN_CAP_EXT_BUTTONS_STICK(info->ext_cap_10),
+               },
+       };
+       const struct i2c_board_info intertouch_board = {
+               I2C_BOARD_INFO("rmi4_smbus", 0x2c),
+               .flags = I2C_CLIENT_HOST_NOTIFY,
+       };
+
+       return psmouse_smbus_init(psmouse, &intertouch_board,
+                                 &pdata, sizeof(pdata),
+                                 leave_breadcrumbs);
+}
+
+/**
+ * synaptics_setup_intertouch - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static int synaptics_setup_intertouch(struct psmouse *psmouse,
+                                     struct synaptics_device_info *info,
+                                     bool leave_breadcrumbs)
+{
+       int error;
+
+       if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_OFF)
+               return -ENXIO;
+
+       if (synaptics_intertouch == SYNAPTICS_INTERTOUCH_NOT_SET) {
+               if (!psmouse_matches_pnp_id(psmouse, topbuttonpad_pnp_ids) &&
+                   !psmouse_matches_pnp_id(psmouse, smbus_pnp_ids))
+                       return -ENXIO;
+       }
+
+       psmouse_info(psmouse, "Trying to set up SMBus access\n");
+
+       error = synaptics_create_intertouch(psmouse, info, leave_breadcrumbs);
+       if (error) {
+               if (error == -EAGAIN)
+                       psmouse_info(psmouse, "SMbus companion is not ready yet\n");
+               else
+                       psmouse_err(psmouse, "unable to create intertouch device\n");
+
+               return error;
+       }
+
+       return 0;
+}
+
+int synaptics_init_smbus(struct psmouse *psmouse)
+{
+       struct synaptics_device_info info;
+       int error;
+
+       psmouse_reset(psmouse);
+
+       error = synaptics_query_hardware(psmouse, &info);
+       if (error) {
+               psmouse_err(psmouse, "Unable to query device: %d\n", error);
+               return error;
+       }
+
+       if (!SYN_CAP_INTERTOUCH(info.ext_cap_0c))
+               return -ENXIO;
+
+       return synaptics_create_intertouch(psmouse, &info, false);
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+static int __maybe_unused
+synaptics_setup_intertouch(struct psmouse *psmouse,
+                          struct synaptics_device_info *info,
+                          bool leave_breadcrumbs)
+{
+       return -ENOSYS;
+}
+
+int synaptics_init_smbus(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+#if defined(CONFIG_MOUSE_PS2_SYNAPTICS) || \
+    defined(CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS)
+
+int synaptics_init(struct psmouse *psmouse)
+{
+       struct synaptics_device_info info;
+       int error;
+       int retval;
+
+       psmouse_reset(psmouse);
+
+       error = synaptics_query_hardware(psmouse, &info);
+       if (error) {
+               psmouse_err(psmouse, "Unable to query device: %d\n", error);
+               return error;
+       }
+
+       if (SYN_CAP_INTERTOUCH(info.ext_cap_0c)) {
+               error = synaptics_setup_intertouch(psmouse, &info, true);
+               if (!error)
+                       return PSMOUSE_SYNAPTICS_SMBUS;
+       }
+
+       retval = synaptics_setup_ps2(psmouse, &info);
+       if (retval < 0) {
+               /*
+                * Not using any flavor of Synaptics support, so clean up
+                * SMbus breadcrumbs, if any.
+                */
+               psmouse_smbus_cleanup(psmouse);
+       }
+
+       return retval;
+}
+
+#else /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
+
+int synaptics_init(struct psmouse *psmouse)
+{
+       return -ENOSYS;
+}
+
+#endif /* CONFIG_MOUSE_PS2_SYNAPTICS || CONFIG_MOUSE_PS2_SYNAPTICS_SMBUS */
index 116ae25..31652d9 100644 (file)
@@ -90,6 +90,7 @@
 #define SYN_CAP_ADV_GESTURE(ex0c)      ((ex0c) & 0x080000)
 #define SYN_CAP_REDUCED_FILTERING(ex0c)        ((ex0c) & 0x000400)
 #define SYN_CAP_IMAGE_SENSOR(ex0c)     ((ex0c) & 0x000800)
+#define SYN_CAP_INTERTOUCH(ex0c)       ((ex0c) & 0x004000)
 
 /*
  * The following descibes response for the 0x10 query.
 #define SYN_CAP_EXT_BUTTONS_STICK(ex10)        ((ex10) & 0x010000)
 #define SYN_CAP_SECUREPAD(ex10)                ((ex10) & 0x020000)
 
-#define SYN_CAP_EXT_BUTTON_STICK_L(eb) (!!((eb) & 0x01))
-#define SYN_CAP_EXT_BUTTON_STICK_M(eb) (!!((eb) & 0x02))
-#define SYN_CAP_EXT_BUTTON_STICK_R(eb) (!!((eb) & 0x04))
+#define SYN_EXT_BUTTON_STICK_L(eb)     (((eb) & BIT(0)) >> 0)
+#define SYN_EXT_BUTTON_STICK_M(eb)     (((eb) & BIT(1)) >> 1)
+#define SYN_EXT_BUTTON_STICK_R(eb)     (((eb) & BIT(2)) >> 2)
 
 /* synaptics modes query bits */
 #define SYN_MODE_ABSOLUTE(m)           ((m) & (1 << 7))
@@ -161,19 +162,23 @@ struct synaptics_hw_state {
        signed char scroll;
 };
 
+/* Data read from the touchpad */
+struct synaptics_device_info {
+       u32 model_id;           /* Model-ID */
+       u32 firmware_id;        /* Firmware-ID */
+       u32 board_id;           /* Board-ID */
+       u32 capabilities;       /* Capabilities */
+       u32 ext_cap;            /* Extended Capabilities */
+       u32 ext_cap_0c;         /* Ext Caps from 0x0c query */
+       u32 ext_cap_10;         /* Ext Caps from 0x10 query */
+       u32 identity;           /* Identification */
+       u32 x_res, y_res;       /* X/Y resolution in units/mm */
+       u32 x_max, y_max;       /* Max coordinates (from FW) */
+       u32 x_min, y_min;       /* Min coordinates (from FW) */
+};
+
 struct synaptics_data {
-       /* Data read from the touchpad */
-       unsigned long int model_id;             /* Model-ID */
-       unsigned long int firmware_id;          /* Firmware-ID */
-       unsigned long int board_id;             /* Board-ID */
-       unsigned long int capabilities;         /* Capabilities */
-       unsigned long int ext_cap;              /* Extended Capabilities */
-       unsigned long int ext_cap_0c;           /* Ext Caps from 0x0c query */
-       unsigned long int ext_cap_10;           /* Ext Caps from 0x10 query */
-       unsigned long int identity;             /* Identification */
-       unsigned int x_res, y_res;              /* X/Y resolution in units/mm */
-       unsigned int x_max, y_max;              /* Max coordinates (from FW) */
-       unsigned int x_min, y_min;              /* Min coordinates (from FW) */
+       struct synaptics_device_info info;
 
        unsigned char pkt_type;                 /* packet type - old, new, etc */
        unsigned char mode;                     /* current mode byte */
@@ -200,8 +205,10 @@ struct synaptics_data {
 
 void synaptics_module_init(void);
 int synaptics_detect(struct psmouse *psmouse, bool set_properties);
-int synaptics_init(struct psmouse *psmouse);
+int synaptics_init_absolute(struct psmouse *psmouse);
 int synaptics_init_relative(struct psmouse *psmouse);
+int synaptics_init_smbus(struct psmouse *psmouse);
+int synaptics_init(struct psmouse *psmouse);
 void synaptics_reset(struct psmouse *psmouse);
 
 #endif /* _SYNAPTICS_H */
index cb2bf20..8538318 100644 (file)
@@ -652,9 +652,18 @@ static const struct i2c_device_id synaptics_i2c_id_table[] = {
 };
 MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
 
+#ifdef CONFIG_OF
+static const struct of_device_id synaptics_i2c_of_match[] = {
+       { .compatible = "synaptics,synaptics_i2c", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, synaptics_i2c_of_match);
+#endif
+
 static struct i2c_driver synaptics_i2c_driver = {
        .driver = {
                .name   = DRIVER_NAME,
+               .of_match_table = of_match_ptr(synaptics_i2c_of_match),
                .pm     = &synaptics_i2c_pm,
        },
 
index 1ca7f55..30d6230 100644 (file)
@@ -285,8 +285,8 @@ static int serio_queue_event(void *object, struct module *owner,
        }
 
        if (!try_module_get(owner)) {
-               pr_warning("Can't get module reference, dropping event %d\n",
-                          event_type);
+               pr_warn("Can't get module reference, dropping event %d\n",
+                       event_type);
                kfree(event);
                retval = -EINVAL;
                goto out;
@@ -823,8 +823,8 @@ static void serio_attach_driver(struct serio_driver *drv)
 
        error = driver_attach(&drv->driver);
        if (error)
-               pr_warning("driver_attach() failed for %s with error %d\n",
-                          drv->driver.name, error);
+               pr_warn("driver_attach() failed for %s with error %d\n",
+                       drv->driver.name, error);
 }
 
 int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
@@ -953,12 +953,24 @@ static int serio_suspend(struct device *dev)
 static int serio_resume(struct device *dev)
 {
        struct serio *serio = to_serio_port(dev);
+       int error = -ENOENT;
 
-       /*
-        * Driver reconnect can take a while, so better let kseriod
-        * deal with it.
-        */
-       serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+       mutex_lock(&serio->drv_mutex);
+       if (serio->drv && serio->drv->fast_reconnect) {
+               error = serio->drv->fast_reconnect(serio);
+               if (error && error != -ENOENT)
+                       dev_warn(dev, "fast reconnect failed with error %d\n",
+                                error);
+       }
+       mutex_unlock(&serio->drv_mutex);
+
+       if (error) {
+               /*
+                * Driver reconnect can take a while, so better let
+                * kseriod deal with it.
+                */
+               serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
+       }
 
        return 0;
 }
index e7409c4..12a3ad8 100644 (file)
@@ -160,12 +160,12 @@ static int sparse_keymap_setkeycode(struct input_dev *dev,
  * @keymap: Keymap in form of array of &key_entry structures ending
  *     with %KE_END type entry
  * @setup: Function that can be used to adjust keymap entries
- *     depending on device's deeds, may be %NULL
+ *     depending on device's needs, may be %NULL
  *
  * The function calculates size and allocates copy of the original
  * keymap after which sets up input device event bits appropriately.
- * Before destroying input device allocated keymap should be freed
- * with a call to sparse_keymap_free().
+ * The allocated copy of the keymap is automatically freed when it
+ * is no longer needed.
  */
 int sparse_keymap_setup(struct input_dev *dev,
                        const struct key_entry *keymap,
@@ -180,19 +180,18 @@ int sparse_keymap_setup(struct input_dev *dev,
        for (e = keymap; e->type != KE_END; e++)
                map_size++;
 
-       map = kcalloc(map_size, sizeof(struct key_entry), GFP_KERNEL);
+       map = devm_kmemdup(&dev->dev, keymap, map_size * sizeof(*map),
+                          GFP_KERNEL);
        if (!map)
                return -ENOMEM;
 
-       memcpy(map, keymap, map_size * sizeof(struct key_entry));
-
        for (i = 0; i < map_size; i++) {
                entry = &map[i];
 
                if (setup) {
                        error = setup(dev, entry);
                        if (error)
-                               goto err_out;
+                               return error;
                }
 
                switch (entry->type) {
@@ -221,10 +220,6 @@ int sparse_keymap_setup(struct input_dev *dev,
        dev->setkeycode = sparse_keymap_setkeycode;
 
        return 0;
-
- err_out:
-       kfree(map);
-       return error;
 }
 EXPORT_SYMBOL(sparse_keymap_setup);
 
@@ -232,29 +227,13 @@ EXPORT_SYMBOL(sparse_keymap_setup);
  * sparse_keymap_free - free memory allocated for sparse keymap
  * @dev: Input device using sparse keymap
  *
- * This function is used to free memory allocated by sparse keymap
+ * This function used to free memory allocated by sparse keymap
  * in an input device that was set up by sparse_keymap_setup().
- * NOTE: It is safe to cal this function while input device is
- * still registered (however the drivers should care not to try to
- * use freed keymap and thus have to shut off interrupts/polling
- * before freeing the keymap).
+ * Since sparse_keymap_setup() now uses a managed allocation for the
+ * keymap copy, use of this function is deprecated.
  */
 void sparse_keymap_free(struct input_dev *dev)
 {
-       unsigned long flags;
-
-       /*
-        * Take event lock to prevent racing with input_get_keycode()
-        * and input_set_keycode() if we are called while input device
-        * is still registered.
-        */
-       spin_lock_irqsave(&dev->event_lock, flags);
-
-       kfree(dev->keycode);
-       dev->keycode = NULL;
-       dev->keycodemax = 0;
-
-       spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 EXPORT_SYMBOL(sparse_keymap_free);
 
index 0335997..6515e64 100644 (file)
@@ -73,6 +73,7 @@ config TOUCHSCREEN_AD7879
 config TOUCHSCREEN_AD7879_I2C
        tristate "support I2C bus connection"
        depends on TOUCHSCREEN_AD7879 && I2C
+       select REGMAP_I2C
        help
          Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus.
 
@@ -82,6 +83,7 @@ config TOUCHSCREEN_AD7879_I2C
 config TOUCHSCREEN_AD7879_SPI
        tristate "support SPI bus connection"
        depends on TOUCHSCREEN_AD7879 && SPI_MASTER
+       select REGMAP_SPI
        help
          Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
 
@@ -1023,6 +1025,16 @@ config TOUCHSCREEN_TSC2007
          To compile this driver as a module, choose M here: the
          module will be called tsc2007.
 
+config TOUCHSCREEN_TSC2007_IIO
+       bool "IIO interface for external ADC input and temperature"
+       depends on TOUCHSCREEN_TSC2007
+       depends on IIO=y || IIO=TOUCHSCREEN_TSC2007
+       help
+         Saying Y here adds an iio interface to the tsc2007 which
+         provides values for the AUX input (used for e.g. battery
+         or ambient light monitoring), temperature and raw input
+         values.
+
 config TOUCHSCREEN_W90X900
        tristate "W90P910 touchscreen driver"
        depends on ARCH_W90X900
index b622e53..ad5c896 100644 (file)
@@ -79,6 +79,8 @@ obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO)   += tsc40.o
 obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2004)      += tsc2004.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2005)      += tsc2005.o
+tsc2007-y := tsc2007_core.o
+tsc2007-$(CONFIG_TOUCHSCREEN_TSC2007_IIO)      += tsc2007_iio.o
 obj-$(CONFIG_TOUCHSCREEN_TSC2007)      += tsc2007.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
 obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)  += wacom_w8001.o
index 58f72e0..49b902b 100644 (file)
 #include <linux/types.h>
 #include <linux/of.h>
 #include <linux/pm.h>
+#include <linux/regmap.h>
 
 #include "ad7879.h"
 
 #define AD7879_DEVID           0x79    /* AD7879-1/AD7889-1 */
 
-/* All registers are word-sized.
- * AD7879 uses a high-byte first convention.
- */
-static int ad7879_i2c_read(struct device *dev, u8 reg)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-
-       return i2c_smbus_read_word_swapped(client, reg);
-}
-
-static int ad7879_i2c_multi_read(struct device *dev,
-                                u8 first_reg, u8 count, u16 *buf)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-       u8 idx;
-
-       i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf);
-
-       for (idx = 0; idx < count; ++idx)
-               buf[idx] = swab16(buf[idx]);
-
-       return 0;
-}
-
-static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val)
-{
-       struct i2c_client *client = to_i2c_client(dev);
-
-       return i2c_smbus_write_word_swapped(client, reg, val);
-}
-
-static const struct ad7879_bus_ops ad7879_i2c_bus_ops = {
-       .bustype        = BUS_I2C,
-       .read           = ad7879_i2c_read,
-       .multi_read     = ad7879_i2c_multi_read,
-       .write          = ad7879_i2c_write,
+static const struct regmap_config ad7879_i2c_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .max_register = 15,
 };
 
 static int ad7879_i2c_probe(struct i2c_client *client,
                                      const struct i2c_device_id *id)
 {
-       struct ad7879 *ts;
+       struct regmap *regmap;
 
        if (!i2c_check_functionality(client->adapter,
                                     I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -66,23 +35,12 @@ static int ad7879_i2c_probe(struct i2c_client *client,
                return -EIO;
        }
 
-       ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq,
-                         &ad7879_i2c_bus_ops);
-       if (IS_ERR(ts))
-               return PTR_ERR(ts);
-
-       i2c_set_clientdata(client, ts);
-
-       return 0;
-}
-
-static int ad7879_i2c_remove(struct i2c_client *client)
-{
-       struct ad7879 *ts = i2c_get_clientdata(client);
-
-       ad7879_remove(ts);
+       regmap = devm_regmap_init_i2c(client, &ad7879_i2c_regmap_config);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
 
-       return 0;
+       return ad7879_probe(&client->dev, regmap, client->irq,
+                           BUS_I2C, AD7879_DEVID);
 }
 
 static const struct i2c_device_id ad7879_id[] = {
@@ -107,12 +65,11 @@ static struct i2c_driver ad7879_i2c_driver = {
                .of_match_table = of_match_ptr(ad7879_i2c_dt_ids),
        },
        .probe          = ad7879_i2c_probe,
-       .remove         = ad7879_i2c_remove,
        .id_table       = ad7879_id,
 };
 
 module_i2c_driver(ad7879_i2c_driver);
 
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
 MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
 MODULE_LICENSE("GPL");
index d42b6b9..3457a56 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/regmap.h>
 
 #include "ad7879.h"
 
 #define AD7879_DEVID           0x7A    /* AD7879/AD7889 */
 
 #define MAX_SPI_FREQ_HZ      5000000
-#define AD7879_CMD_MAGIC     0xE000
-#define AD7879_CMD_READ      (1 << 10)
-#define AD7879_CMD(reg)      (AD7879_CMD_MAGIC | ((reg) & 0xF))
-#define AD7879_WRITECMD(reg) (AD7879_CMD(reg))
-#define AD7879_READCMD(reg)  (AD7879_CMD(reg) | AD7879_CMD_READ)
 
-/*
- * ad7879_read/write are only used for initial setup and for sysfs controls.
- * The main traffic is done in ad7879_collect().
- */
-
-static int ad7879_spi_xfer(struct spi_device *spi,
-                          u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf)
-{
-       struct spi_message msg;
-       struct spi_transfer *xfers;
-       void *spi_data;
-       u16 *command;
-       u16 *_rx_buf = _rx_buf; /* shut gcc up */
-       u8 idx;
-       int ret;
-
-       xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL);
-       if (!spi_data)
-               return -ENOMEM;
-
-       spi_message_init(&msg);
-
-       command = spi_data;
-       command[0] = cmd;
-       if (count == 1) {
-               /* ad7879_spi_{read,write} gave us buf on stack */
-               command[1] = *tx_buf;
-               tx_buf = &command[1];
-               _rx_buf = rx_buf;
-               rx_buf = &command[2];
-       }
-
-       ++xfers;
-       xfers[0].tx_buf = command;
-       xfers[0].len = 2;
-       spi_message_add_tail(&xfers[0], &msg);
-       ++xfers;
-
-       for (idx = 0; idx < count; ++idx) {
-               if (rx_buf)
-                       xfers[idx].rx_buf = &rx_buf[idx];
-               if (tx_buf)
-                       xfers[idx].tx_buf = &tx_buf[idx];
-               xfers[idx].len = 2;
-               spi_message_add_tail(&xfers[idx], &msg);
-       }
-
-       ret = spi_sync(spi, &msg);
-
-       if (count == 1)
-               _rx_buf[0] = command[2];
-
-       kfree(spi_data);
-
-       return ret;
-}
-
-static int ad7879_spi_multi_read(struct device *dev,
-                                u8 first_reg, u8 count, u16 *buf)
-{
-       struct spi_device *spi = to_spi_device(dev);
+#define AD7879_CMD_MAGIC     0xE0
+#define AD7879_CMD_READ      BIT(2)
 
-       return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf);
-}
-
-static int ad7879_spi_read(struct device *dev, u8 reg)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       u16 ret, dummy;
-
-       return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret;
-}
-
-static int ad7879_spi_write(struct device *dev, u8 reg, u16 val)
-{
-       struct spi_device *spi = to_spi_device(dev);
-       u16 dummy;
-
-       return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy);
-}
-
-static const struct ad7879_bus_ops ad7879_spi_bus_ops = {
-       .bustype        = BUS_SPI,
-       .read           = ad7879_spi_read,
-       .multi_read     = ad7879_spi_multi_read,
-       .write          = ad7879_spi_write,
+static const struct regmap_config ad7879_spi_regmap_config = {
+       .reg_bits = 16,
+       .val_bits = 16,
+       .max_register = 15,
+       .read_flag_mask = AD7879_CMD_MAGIC | AD7879_CMD_READ,
+       .write_flag_mask = AD7879_CMD_MAGIC,
 };
 
 static int ad7879_spi_probe(struct spi_device *spi)
 {
-       struct ad7879 *ts;
-       int err;
+       struct regmap *regmap;
 
        /* don't exceed max specified SPI CLK frequency */
        if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
@@ -122,29 +40,11 @@ static int ad7879_spi_probe(struct spi_device *spi)
                return -EINVAL;
        }
 
-       spi->bits_per_word = 16;
-       err = spi_setup(spi);
-       if (err) {
-               dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n");
-               return err;
-       }
-
-       ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops);
-       if (IS_ERR(ts))
-               return PTR_ERR(ts);
-
-       spi_set_drvdata(spi, ts);
-
-       return 0;
-}
-
-static int ad7879_spi_remove(struct spi_device *spi)
-{
-       struct ad7879 *ts = spi_get_drvdata(spi);
-
-       ad7879_remove(ts);
+       regmap = devm_regmap_init_spi(spi, &ad7879_spi_regmap_config);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
 
-       return 0;
+       return ad7879_probe(&spi->dev, regmap, spi->irq, BUS_SPI, AD7879_DEVID);
 }
 
 #ifdef CONFIG_OF
@@ -162,12 +62,11 @@ static struct spi_driver ad7879_spi_driver = {
                .of_match_table = of_match_ptr(ad7879_spi_dt_ids),
        },
        .probe          = ad7879_spi_probe,
-       .remove         = ad7879_spi_remove,
 };
 
 module_spi_driver(ad7879_spi_driver);
 
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
 MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("spi:ad7879");
index e16a446..196028c 100644 (file)
@@ -26,9 +26,9 @@
 #include <linux/input.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
-#include <linux/spi/spi.h>
-#include <linux/i2c.h>
 #include <linux/gpio.h>
 
 #include <linux/input/touchscreen.h>
@@ -106,8 +106,7 @@ enum {
 #define        TS_PEN_UP_TIMEOUT               msecs_to_jiffies(50)
 
 struct ad7879 {
-       const struct ad7879_bus_ops *bops;
-
+       struct regmap           *regmap;
        struct device           *dev;
        struct input_dev        *input;
        struct timer_list       timer;
@@ -137,17 +136,32 @@ struct ad7879 {
 
 static int ad7879_read(struct ad7879 *ts, u8 reg)
 {
-       return ts->bops->read(ts->dev, reg);
-}
+       unsigned int val;
+       int error;
 
-static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf)
-{
-       return ts->bops->multi_read(ts->dev, first_reg, count, buf);
+       error = regmap_read(ts->regmap, reg, &val);
+       if (error) {
+               dev_err(ts->dev, "failed to read register %#02x: %d\n",
+                       reg, error);
+               return error;
+       }
+
+       return val;
 }
 
 static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val)
 {
-       return ts->bops->write(ts->dev, reg, val);
+       int error;
+
+       error = regmap_write(ts->regmap, reg, val);
+       if (error) {
+               dev_err(ts->dev,
+                       "failed to write %#04x to register %#02x: %d\n",
+                       val, reg, error);
+               return error;
+       }
+
+       return 0;
 }
 
 static int ad7879_report(struct ad7879 *ts)
@@ -234,7 +248,8 @@ static irqreturn_t ad7879_irq(int irq, void *handle)
 {
        struct ad7879 *ts = handle;
 
-       ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data);
+       regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
+                        ts->conversion_data, AD7879_NR_SENSE);
 
        if (!ad7879_report(ts))
                mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
@@ -440,23 +455,34 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip,
 static int ad7879_gpio_add(struct ad7879 *ts,
                           const struct ad7879_platform_data *pdata)
 {
+       bool gpio_export;
+       int gpio_base;
        int ret = 0;
 
+       if (pdata) {
+               gpio_export = pdata->gpio_export;
+               gpio_base = pdata->gpio_base;
+       } else {
+               gpio_export = device_property_read_bool(ts->dev,
+                                                       "gpio-controller");
+               gpio_base = -1;
+       }
+
        mutex_init(&ts->mutex);
 
-       if (pdata->gpio_export) {
+       if (gpio_export) {
                ts->gc.direction_input = ad7879_gpio_direction_input;
                ts->gc.direction_output = ad7879_gpio_direction_output;
                ts->gc.get = ad7879_gpio_get_value;
                ts->gc.set = ad7879_gpio_set_value;
                ts->gc.can_sleep = 1;
-               ts->gc.base = pdata->gpio_base;
+               ts->gc.base = gpio_base;
                ts->gc.ngpio = 1;
                ts->gc.label = "AD7879-GPIO";
                ts->gc.owner = THIS_MODULE;
                ts->gc.parent = ts->dev;
 
-               ret = gpiochip_add_data(&ts->gc, ts);
+               ret = devm_gpiochip_add_data(ts->dev, &ts->gc, ts);
                if (ret)
                        dev_err(ts->dev, "failed to register gpio %d\n",
                                ts->gc.base);
@@ -464,25 +490,12 @@ static int ad7879_gpio_add(struct ad7879 *ts,
 
        return ret;
 }
-
-static void ad7879_gpio_remove(struct ad7879 *ts)
-{
-       const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
-
-       if (pdata && pdata->gpio_export)
-               gpiochip_remove(&ts->gc);
-
-}
 #else
-static inline int ad7879_gpio_add(struct ad7879 *ts,
-                                 const struct ad7879_platform_data *pdata)
+static int ad7879_gpio_add(struct ad7879 *ts,
+                          const struct ad7879_platform_data *pdata)
 {
        return 0;
 }
-
-static inline void ad7879_gpio_remove(struct ad7879 *ts)
-{
-}
 #endif
 
 static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
@@ -511,8 +524,15 @@ static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
        return 0;
 }
 
-struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
-                           const struct ad7879_bus_ops *bops)
+static void ad7879_cleanup_sysfs(void *_ts)
+{
+       struct ad7879 *ts = _ts;
+
+       sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
+}
+
+int ad7879_probe(struct device *dev, struct regmap *regmap,
+                int irq, u16 bustype, u8 devid)
 {
        struct ad7879_platform_data *pdata = dev_get_platdata(dev);
        struct ad7879 *ts;
@@ -520,14 +540,14 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
        int err;
        u16 revid;
 
-       if (!irq) {
+       if (irq <= 0) {
                dev_err(dev, "No IRQ specified\n");
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
        }
 
        ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
        if (!ts)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
 
        if (pdata) {
                /* Platform data use swapped axis (backward compatibility) */
@@ -540,23 +560,22 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
                ts->averaging = pdata->averaging;
                ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
                ts->median = pdata->median;
-       } else if (dev->of_node) {
-               ad7879_parse_dt(dev, ts);
        } else {
-               dev_err(dev, "No platform data\n");
-               return ERR_PTR(-EINVAL);
+               err = ad7879_parse_dt(dev, ts);
+               if (err)
+                       return err;
        }
 
        input_dev = devm_input_allocate_device(dev);
        if (!input_dev) {
                dev_err(dev, "Failed to allocate input device\n");
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        }
 
-       ts->bops = bops;
        ts->dev = dev;
        ts->input = input_dev;
        ts->irq = irq;
+       ts->regmap = regmap;
 
        setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
        snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
@@ -564,20 +583,14 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
        input_dev->name = "AD7879 Touchscreen";
        input_dev->phys = ts->phys;
        input_dev->dev.parent = dev;
-       input_dev->id.bustype = bops->bustype;
+       input_dev->id.bustype = bustype;
 
        input_dev->open = ad7879_open;
        input_dev->close = ad7879_close;
 
        input_set_drvdata(input_dev, ts);
 
-       __set_bit(EV_ABS, input_dev->evbit);
-       __set_bit(ABS_X, input_dev->absbit);
-       __set_bit(ABS_Y, input_dev->absbit);
-       __set_bit(ABS_PRESSURE, input_dev->absbit);
-
-       __set_bit(EV_KEY, input_dev->evbit);
-       __set_bit(BTN_TOUCH, input_dev->keybit);
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
 
        if (pdata) {
                input_set_abs_params(input_dev, ABS_X,
@@ -595,17 +608,18 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
        } else {
                input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
                input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+               input_set_capability(input_dev, EV_ABS, ABS_PRESSURE);
                touchscreen_parse_properties(input_dev, false, NULL);
                if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
                        dev_err(dev, "Touchscreen pressure is not specified\n");
-                       return ERR_PTR(-EINVAL);
+                       return -EINVAL;
                }
        }
 
        err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
        if (err < 0) {
                dev_err(dev, "Failed to write %s\n", input_dev->name);
-               return ERR_PTR(err);
+               return err;
        }
 
        revid = ad7879_read(ts, AD7879_REG_REVID);
@@ -614,7 +628,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
        if (input_dev->id.product != devid) {
                dev_err(dev, "Failed to probe %s (%x vs %x)\n",
                        input_dev->name, devid, revid);
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
        }
 
        ts->cmd_crtl3 = AD7879_YPLUS_BIT |
@@ -639,43 +653,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
                                        dev_name(dev), ts);
        if (err) {
                dev_err(dev, "Failed to request IRQ: %d\n", err);
-               return ERR_PTR(err);
+               return err;
        }
 
        __ad7879_disable(ts);
 
        err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
        if (err)
-               goto err_out;
+               return err;
 
-       if (pdata) {
-               err = ad7879_gpio_add(ts, pdata);
-               if (err)
-                       goto err_remove_attr;
-       }
+       err = devm_add_action_or_reset(dev, ad7879_cleanup_sysfs, ts);
+       if (err)
+               return err;
+
+       err = ad7879_gpio_add(ts, pdata);
+       if (err)
+               return err;
 
        err = input_register_device(input_dev);
        if (err)
-               goto err_remove_gpio;
+               return err;
 
-       return ts;
+       dev_set_drvdata(dev, ts);
 
-err_remove_gpio:
-       ad7879_gpio_remove(ts);
-err_remove_attr:
-       sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
-err_out:
-       return ERR_PTR(err);
+       return 0;
 }
 EXPORT_SYMBOL(ad7879_probe);
 
-void ad7879_remove(struct ad7879 *ts)
-{
-       ad7879_gpio_remove(ts);
-       sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
-}
-EXPORT_SYMBOL(ad7879_remove);
-
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
 MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
 MODULE_LICENSE("GPL");
index 6fd13c4..7e43066 100644 (file)
 
 #include <linux/types.h>
 
-struct ad7879;
 struct device;
-
-struct ad7879_bus_ops {
-       u16 bustype;
-       int (*read)(struct device *dev, u8 reg);
-       int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf);
-       int (*write)(struct device *dev, u8 reg, u16 val);
-};
+struct regmap;
 
 extern const struct dev_pm_ops ad7879_pm_ops;
 
-struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq,
-                           const struct ad7879_bus_ops *bops);
-void ad7879_remove(struct ad7879 *);
+int ad7879_probe(struct device *dev, struct regmap *regmap,
+                int irq, u16 bustype, u8 devid);
 
 #endif
index f5793e3..735a0be 100644 (file)
@@ -871,7 +871,7 @@ static irqreturn_t ads7846_irq(int irq, void *handle)
                                   msecs_to_jiffies(TS_POLL_PERIOD));
        }
 
-       if (ts->pendown) {
+       if (ts->pendown && !ts->stopped) {
                struct input_dev *input = ts->input;
 
                input_report_key(input, BTN_TOUCH, 0);
index a595ae5..72ca3ef 100644 (file)
@@ -224,9 +224,16 @@ static const struct i2c_device_id max11801_ts_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
 
+static const struct of_device_id max11801_ts_dt_ids[] = {
+       { .compatible = "maxim,max11801" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max11801_ts_dt_ids);
+
 static struct i2c_driver max11801_ts_driver = {
        .driver = {
                .name   = "max11801_ts",
+               .of_match_table = max11801_ts_dt_ids,
        },
        .id_table       = max11801_ts_id,
        .probe          = max11801_ts_probe,
index 703d7f9..05108c2 100644 (file)
@@ -253,10 +253,21 @@ static int mip4_get_fw_version(struct mip4_ts *ts)
  */
 static int mip4_query_device(struct mip4_ts *ts)
 {
+       union i2c_smbus_data dummy;
        int error;
        u8 cmd[2];
        u8 buf[14];
 
+       /*
+        * Make sure there is something at this address as we do not
+        * consider subsequent failures as fatal.
+        */
+       if (i2c_smbus_xfer(ts->client->adapter, ts->client->addr,
+                          0, I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
+               dev_err(&ts->client->dev, "nothing at this address\n");
+               return -ENXIO;
+       }
+
        /* Product name */
        cmd[0] = MIP4_R0_INFO;
        cmd[1] = MIP4_R1_INFO_PRODUCT_NAME;
index 404830a..813dd68 100644 (file)
@@ -580,12 +580,25 @@ static const struct acpi_device_id silead_ts_acpi_match[] = {
 MODULE_DEVICE_TABLE(acpi, silead_ts_acpi_match);
 #endif
 
+#ifdef CONFIG_OF
+static const struct of_device_id silead_ts_of_match[] = {
+       { .compatible = "silead,gsl1680" },
+       { .compatible = "silead,gsl1688" },
+       { .compatible = "silead,gsl3670" },
+       { .compatible = "silead,gsl3675" },
+       { .compatible = "silead,gsl3692" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, silead_ts_of_match);
+#endif
+
 static struct i2c_driver silead_ts_driver = {
        .probe = silead_ts_probe,
        .id_table = silead_ts_id,
        .driver = {
                .name = SILEAD_TS_NAME,
                .acpi_match_table = ACPI_PTR(silead_ts_acpi_match),
+               .of_match_table = of_match_ptr(silead_ts_of_match),
                .pm = &silead_ts_pm,
        },
 };
index a340bfc..75170a7 100644 (file)
@@ -226,7 +226,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
         */
        init_data = tps_board->tps6507x_ts_init_data;
 
-       tsc = kzalloc(sizeof(struct tps6507x_ts), GFP_KERNEL);
+       tsc = devm_kzalloc(&pdev->dev, sizeof(struct tps6507x_ts), GFP_KERNEL);
        if (!tsc) {
                dev_err(tps6507x_dev->dev, "failed to allocate driver data\n");
                return -ENOMEM;
@@ -240,11 +240,10 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
        snprintf(tsc->phys, sizeof(tsc->phys),
                 "%s/input0", dev_name(tsc->dev));
 
-       poll_dev = input_allocate_polled_device();
+       poll_dev = devm_input_allocate_polled_device(&pdev->dev);
        if (!poll_dev) {
                dev_err(tsc->dev, "Failed to allocate polled input device.\n");
-               error = -ENOMEM;
-               goto err_free_mem;
+               return -ENOMEM;
        }
 
        tsc->poll_dev = poll_dev;
@@ -274,32 +273,11 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
 
        error = tps6507x_adc_standby(tsc);
        if (error)
-               goto err_free_polled_dev;
+               return error;
 
        error = input_register_polled_device(poll_dev);
        if (error)
-               goto err_free_polled_dev;
-
-       platform_set_drvdata(pdev, tsc);
-
-       return 0;
-
-err_free_polled_dev:
-       input_free_polled_device(poll_dev);
-err_free_mem:
-       kfree(tsc);
-       return error;
-}
-
-static int tps6507x_ts_remove(struct platform_device *pdev)
-{
-       struct tps6507x_ts *tsc = platform_get_drvdata(pdev);
-       struct input_polled_dev *poll_dev = tsc->poll_dev;
-
-       input_unregister_polled_device(poll_dev);
-       input_free_polled_device(poll_dev);
-
-       kfree(tsc);
+               return error;
 
        return 0;
 }
@@ -309,7 +287,6 @@ static struct platform_driver tps6507x_ts_driver = {
                .name = "tps6507x-ts",
        },
        .probe = tps6507x_ts_probe,
-       .remove = tps6507x_ts_remove,
 };
 module_platform_driver(tps6507x_ts_driver);
 
diff --git a/drivers/input/touchscreen/tsc2007.h b/drivers/input/touchscreen/tsc2007.h
new file mode 100644 (file)
index 0000000..30fdf5b
--- /dev/null
@@ -0,0 +1,101 @@
+
+/*
+ * Copyright (c) 2008 MtekVision Co., Ltd.
+ *     Kwangwoo Lee <kwlee@mtekvision.com>
+ *
+ * Using code from:
+ *  - ads7846.c
+ *     Copyright (c) 2005 David Brownell
+ *     Copyright (c) 2006 Nokia Corporation
+ *  - corgi_ts.c
+ *     Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *     Copyright (C) 2002 MontaVista Software
+ *     Copyright (C) 2004 Texas Instruments
+ *     Copyright (C) 2005 Dirk Behme
+ *
+ *  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.
+ */
+
+#ifndef _TSC2007_H
+#define _TSC2007_H
+
+#define TSC2007_MEASURE_TEMP0          (0x0 << 4)
+#define TSC2007_MEASURE_AUX            (0x2 << 4)
+#define TSC2007_MEASURE_TEMP1          (0x4 << 4)
+#define TSC2007_ACTIVATE_XN            (0x8 << 4)
+#define TSC2007_ACTIVATE_YN            (0x9 << 4)
+#define TSC2007_ACTIVATE_YP_XN         (0xa << 4)
+#define TSC2007_SETUP                  (0xb << 4)
+#define TSC2007_MEASURE_X              (0xc << 4)
+#define TSC2007_MEASURE_Y              (0xd << 4)
+#define TSC2007_MEASURE_Z1             (0xe << 4)
+#define TSC2007_MEASURE_Z2             (0xf << 4)
+
+#define TSC2007_POWER_OFF_IRQ_EN       (0x0 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS0                (0x1 << 2)
+#define TSC2007_ADC_OFF_IRQ_EN         (0x2 << 2)
+#define TSC2007_ADC_ON_IRQ_DIS1                (0x3 << 2)
+
+#define TSC2007_12BIT                  (0x0 << 1)
+#define TSC2007_8BIT                   (0x1 << 1)
+
+#define MAX_12BIT                      ((1 << 12) - 1)
+
+#define ADC_ON_12BIT   (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
+
+#define READ_Y         (ADC_ON_12BIT | TSC2007_MEASURE_Y)
+#define READ_Z1                (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
+#define READ_Z2                (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
+#define READ_X         (ADC_ON_12BIT | TSC2007_MEASURE_X)
+#define PWRDOWN                (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
+
+struct ts_event {
+       u16     x;
+       u16     y;
+       u16     z1, z2;
+};
+
+struct tsc2007 {
+       struct input_dev        *input;
+       char                    phys[32];
+
+       struct i2c_client       *client;
+
+       u16                     model;
+       u16                     x_plate_ohms;
+       u16                     max_rt;
+       unsigned long           poll_period; /* in jiffies */
+       int                     fuzzx;
+       int                     fuzzy;
+       int                     fuzzz;
+
+       unsigned int            gpio;
+       int                     irq;
+
+       wait_queue_head_t       wait;
+       bool                    stopped;
+
+       int                     (*get_pendown_state)(struct device *);
+       void                    (*clear_penirq)(void);
+
+       struct mutex            mlock;
+};
+
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd);
+u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc);
+bool tsc2007_is_pen_down(struct tsc2007 *ts);
+
+#if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2007_IIO)
+/* defined in tsc2007_iio.c */
+int tsc2007_iio_configure(struct tsc2007 *ts);
+#else
+static inline int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+       return 0;
+}
+#endif /* CONFIG_TOUCHSCREEN_TSC2007_IIO */
+
+#endif /* _TSC2007_H */
similarity index 83%
rename from drivers/input/touchscreen/tsc2007.c
rename to drivers/input/touchscreen/tsc2007_core.c
index 5d0cd51..fc73849 100644 (file)
 #include <linux/i2c.h>
 #include <linux/i2c/tsc2007.h>
 #include <linux/of_device.h>
-#include <linux/of.h>
 #include <linux/of_gpio.h>
+#include "tsc2007.h"
 
-#define TSC2007_MEASURE_TEMP0          (0x0 << 4)
-#define TSC2007_MEASURE_AUX            (0x2 << 4)
-#define TSC2007_MEASURE_TEMP1          (0x4 << 4)
-#define TSC2007_ACTIVATE_XN            (0x8 << 4)
-#define TSC2007_ACTIVATE_YN            (0x9 << 4)
-#define TSC2007_ACTIVATE_YP_XN         (0xa << 4)
-#define TSC2007_SETUP                  (0xb << 4)
-#define TSC2007_MEASURE_X              (0xc << 4)
-#define TSC2007_MEASURE_Y              (0xd << 4)
-#define TSC2007_MEASURE_Z1             (0xe << 4)
-#define TSC2007_MEASURE_Z2             (0xf << 4)
-
-#define TSC2007_POWER_OFF_IRQ_EN       (0x0 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS0                (0x1 << 2)
-#define TSC2007_ADC_OFF_IRQ_EN         (0x2 << 2)
-#define TSC2007_ADC_ON_IRQ_DIS1                (0x3 << 2)
-
-#define TSC2007_12BIT                  (0x0 << 1)
-#define TSC2007_8BIT                   (0x1 << 1)
-
-#define        MAX_12BIT                       ((1 << 12) - 1)
-
-#define ADC_ON_12BIT   (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)
-
-#define READ_Y         (ADC_ON_12BIT | TSC2007_MEASURE_Y)
-#define READ_Z1                (ADC_ON_12BIT | TSC2007_MEASURE_Z1)
-#define READ_Z2                (ADC_ON_12BIT | TSC2007_MEASURE_Z2)
-#define READ_X         (ADC_ON_12BIT | TSC2007_MEASURE_X)
-#define PWRDOWN                (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)
-
-struct ts_event {
-       u16     x;
-       u16     y;
-       u16     z1, z2;
-};
-
-struct tsc2007 {
-       struct input_dev        *input;
-       char                    phys[32];
-
-       struct i2c_client       *client;
-
-       u16                     model;
-       u16                     x_plate_ohms;
-       u16                     max_rt;
-       unsigned long           poll_period; /* in jiffies */
-       int                     fuzzx;
-       int                     fuzzy;
-       int                     fuzzz;
-
-       unsigned                gpio;
-       int                     irq;
-
-       wait_queue_head_t       wait;
-       bool                    stopped;
-
-       int                     (*get_pendown_state)(struct device *);
-       void                    (*clear_penirq)(void);
-};
-
-static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
+int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
 {
        s32 data;
        u16 val;
@@ -128,7 +68,7 @@ static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)
        tsc2007_xfer(tsc, PWRDOWN);
 }
 
-static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
+u32 tsc2007_calculate_resistance(struct tsc2007 *tsc, struct ts_event *tc)
 {
        u32 rt = 0;
 
@@ -137,7 +77,7 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
                tc->x = 0;
 
        if (likely(tc->x && tc->z1)) {
-               /* compute touch pressure resistance using equation #1 */
+               /* compute touch resistance using equation #1 */
                rt = tc->z2 - tc->z1;
                rt *= tc->x;
                rt *= tsc->x_plate_ohms;
@@ -148,7 +88,7 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
        return rt;
 }
 
-static bool tsc2007_is_pen_down(struct tsc2007 *ts)
+bool tsc2007_is_pen_down(struct tsc2007 *ts)
 {
        /*
         * NOTE: We can't rely on the pressure to determine the pen down
@@ -180,9 +120,12 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
        while (!ts->stopped && tsc2007_is_pen_down(ts)) {
 
                /* pen is down, continue with the measurement */
+
+               mutex_lock(&ts->mlock);
                tsc2007_read_values(ts, &tc);
+               mutex_unlock(&ts->mlock);
 
-               rt = tsc2007_calculate_pressure(ts, &tc);
+               rt = tsc2007_calculate_resistance(ts, &tc);
 
                if (!rt && !ts->get_pendown_state) {
                        /*
@@ -195,9 +138,11 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
 
                if (rt <= ts->max_rt) {
                        dev_dbg(&ts->client->dev,
-                               "DOWN point(%4d,%4d), pressure (%4u)\n",
+                               "DOWN point(%4d,%4d), resistance (%4u)\n",
                                tc.x, tc.y, rt);
 
+                       rt = ts->max_rt - rt;
+
                        input_report_key(input, BTN_TOUCH, 1);
                        input_report_abs(input, ABS_X, tc.x);
                        input_report_abs(input, ABS_Y, tc.y);
@@ -375,7 +320,8 @@ static void tsc2007_call_exit_platform_hw(void *data)
 static int tsc2007_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
-       const struct tsc2007_platform_data *pdata = dev_get_platdata(&client->dev);
+       const struct tsc2007_platform_data *pdata =
+               dev_get_platdata(&client->dev);
        struct tsc2007 *ts;
        struct input_dev *input_dev;
        int err;
@@ -404,7 +350,9 @@ static int tsc2007_probe(struct i2c_client *client,
        ts->client = client;
        ts->irq = client->irq;
        ts->input = input_dev;
+
        init_waitqueue_head(&ts->wait);
+       mutex_init(&ts->mlock);
 
        snprintf(ts->phys, sizeof(ts->phys),
                 "%s/input0", dev_name(&client->dev));
@@ -418,8 +366,7 @@ static int tsc2007_probe(struct i2c_client *client,
 
        input_set_drvdata(input_dev, ts);
 
-       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-       input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+       input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
 
        input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, ts->fuzzx, 0);
        input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, ts->fuzzy, 0);
@@ -455,6 +402,14 @@ static int tsc2007_probe(struct i2c_client *client,
 
        tsc2007_stop(ts);
 
+       /* power down the chip (TSC2007_SETUP does not ACK on I2C) */
+       err = tsc2007_xfer(ts, PWRDOWN);
+       if (err < 0) {
+               dev_err(&client->dev,
+                       "Failed to setup chip: %d\n", err);
+               return err;     /* chip does not respond */
+       }
+
        err = input_register_device(input_dev);
        if (err) {
                dev_err(&client->dev,
@@ -462,6 +417,13 @@ static int tsc2007_probe(struct i2c_client *client,
                return err;
        }
 
+       err =  tsc2007_iio_configure(ts);
+       if (err) {
+               dev_err(&client->dev,
+                       "Failed to register with IIO: %d\n", err);
+               return err;
+       }
+
        return 0;
 }
 
diff --git a/drivers/input/touchscreen/tsc2007_iio.c b/drivers/input/touchscreen/tsc2007_iio.c
new file mode 100644 (file)
index 0000000..27b25a9
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2016 Golden Delicious Comp. GmbH&Co. KG
+ *     Nikolaus Schaller <hns@goldelico.com>
+ *
+ *  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/i2c.h>
+#include <linux/iio/iio.h>
+#include "tsc2007.h"
+
+struct tsc2007_iio {
+       struct tsc2007 *ts;
+};
+
+#define TSC2007_CHAN_IIO(_chan, _name, _type, _chan_info) \
+{ \
+       .datasheet_name = _name, \
+       .type = _type, \
+       .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \
+                       BIT(_chan_info), \
+       .indexed = 1, \
+       .channel = _chan, \
+}
+
+static const struct iio_chan_spec tsc2007_iio_channel[] = {
+       TSC2007_CHAN_IIO(0, "x", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(1, "y", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(2, "z1", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(3, "z2", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(4, "adc", IIO_VOLTAGE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(5, "rt", IIO_VOLTAGE, IIO_CHAN_INFO_RAW), /* Ohms? */
+       TSC2007_CHAN_IIO(6, "pen", IIO_PRESSURE, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(7, "temp0", IIO_TEMP, IIO_CHAN_INFO_RAW),
+       TSC2007_CHAN_IIO(8, "temp1", IIO_TEMP, IIO_CHAN_INFO_RAW),
+};
+
+static int tsc2007_read_raw(struct iio_dev *indio_dev,
+                           struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct tsc2007_iio *iio = iio_priv(indio_dev);
+       struct tsc2007 *tsc = iio->ts;
+       int adc_chan = chan->channel;
+       int ret = 0;
+
+       if (adc_chan >= ARRAY_SIZE(tsc2007_iio_channel))
+               return -EINVAL;
+
+       if (mask != IIO_CHAN_INFO_RAW)
+               return -EINVAL;
+
+       mutex_lock(&tsc->mlock);
+
+       switch (chan->channel) {
+       case 0:
+               *val = tsc2007_xfer(tsc, READ_X);
+               break;
+       case 1:
+               *val = tsc2007_xfer(tsc, READ_Y);
+               break;
+       case 2:
+               *val = tsc2007_xfer(tsc, READ_Z1);
+               break;
+       case 3:
+               *val = tsc2007_xfer(tsc, READ_Z2);
+               break;
+       case 4:
+               *val = tsc2007_xfer(tsc, (ADC_ON_12BIT | TSC2007_MEASURE_AUX));
+               break;
+       case 5: {
+               struct ts_event tc;
+
+               tc.x = tsc2007_xfer(tsc, READ_X);
+               tc.z1 = tsc2007_xfer(tsc, READ_Z1);
+               tc.z2 = tsc2007_xfer(tsc, READ_Z2);
+               *val = tsc2007_calculate_resistance(tsc, &tc);
+               break;
+       }
+       case 6:
+               *val = tsc2007_is_pen_down(tsc);
+               break;
+       case 7:
+               *val = tsc2007_xfer(tsc,
+                                   (ADC_ON_12BIT | TSC2007_MEASURE_TEMP0));
+               break;
+       case 8:
+               *val = tsc2007_xfer(tsc,
+                                   (ADC_ON_12BIT | TSC2007_MEASURE_TEMP1));
+               break;
+       }
+
+       /* Prepare for next touch reading - power down ADC, enable PENIRQ */
+       tsc2007_xfer(tsc, PWRDOWN);
+
+       mutex_unlock(&tsc->mlock);
+
+       ret = IIO_VAL_INT;
+
+       return ret;
+}
+
+static const struct iio_info tsc2007_iio_info = {
+       .read_raw = tsc2007_read_raw,
+       .driver_module = THIS_MODULE,
+};
+
+int tsc2007_iio_configure(struct tsc2007 *ts)
+{
+       struct iio_dev *indio_dev;
+       struct tsc2007_iio *iio;
+       int error;
+
+       indio_dev = devm_iio_device_alloc(&ts->client->dev, sizeof(*iio));
+       if (!indio_dev) {
+               dev_err(&ts->client->dev, "iio_device_alloc failed\n");
+               return -ENOMEM;
+       }
+
+       iio = iio_priv(indio_dev);
+       iio->ts = ts;
+
+       indio_dev->name = "tsc2007";
+       indio_dev->dev.parent = &ts->client->dev;
+       indio_dev->info = &tsc2007_iio_info;
+       indio_dev->modes = INDIO_DIRECT_MODE;
+       indio_dev->channels = tsc2007_iio_channel;
+       indio_dev->num_channels = ARRAY_SIZE(tsc2007_iio_channel);
+
+       error = devm_iio_device_register(&ts->client->dev, indio_dev);
+       if (error) {
+               dev_err(&ts->client->dev,
+                       "iio_device_register() failed: %d\n", error);
+               return error;
+       }
+
+       return 0;
+}
index 37b04a0..6174733 100644 (file)
@@ -49,6 +49,8 @@ struct matrix_keymap_data {
  * @wakeup: controls whether the device should be set up as wakeup
  *     source
  * @no_autorepeat: disable key autorepeat
+ * @drive_inactive_cols: drive inactive columns during scan, rather than
+ *     making them inputs.
  *
  * This structure represents platform-specific data that use used by
  * matrix_keypad driver to perform proper initialization.
@@ -73,6 +75,7 @@ struct matrix_keypad_platform_data {
        bool            active_low;
        bool            wakeup;
        bool            no_autorepeat;
+       bool            drive_inactive_cols;
 };
 
 int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
index f1ef638..c93e7e0 100644 (file)
@@ -2041,6 +2041,9 @@ enum ec_mkbp_event {
        /* The state of the switches have changed. */
        EC_MKBP_EVENT_SWITCH = 4,
 
+       /* EC sent a sysrq command */
+       EC_MKBP_EVENT_SYSRQ = 6,
+
        /* Number of MKBP events */
        EC_MKBP_EVENT_COUNT,
 };
@@ -2053,6 +2056,7 @@ union ec_response_get_next_data {
 
        uint32_t   buttons;
        uint32_t   switches;
+       uint32_t   sysrq;
 } __packed;
 
 struct ec_response_get_next_event {
index c733cff..138a5ef 100644 (file)
@@ -77,6 +77,7 @@ struct serio_driver {
        irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int);
        int  (*connect)(struct serio *, struct serio_driver *drv);
        int  (*reconnect)(struct serio *);
+       int  (*fast_reconnect)(struct serio *);
        void (*disconnect)(struct serio *);
        void (*cleanup)(struct serio *);