Merge commit 'v3.1-rc4' into next
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 7 Sep 2011 21:18:36 +0000 (14:18 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Wed, 7 Sep 2011 21:18:36 +0000 (14:18 -0700)
14 files changed:
Documentation/input/multi-touch-protocol.txt
drivers/input/input-mt.c
drivers/input/input-polldev.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ad714x-i2c.c
drivers/input/misc/bma150.c [new file with mode: 0644]
drivers/input/misc/pm8xxx-vibrator.c [new file with mode: 0644]
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/touchscreen/tsc2007.c
drivers/input/touchscreen/wacom_w8001.c
include/linux/bma150.h [new file with mode: 0644]
include/linux/input.h

index 71536e7..543101c 100644 (file)
@@ -65,6 +65,20 @@ the full state of each initiated contact has to reside in the receiving
 end.  Upon receiving an MT event, one simply updates the appropriate
 attribute of the current slot.
 
+Some devices identify and/or track more contacts than they can report to the
+driver.  A driver for such a device should associate one type B slot with each
+contact that is reported by the hardware.  Whenever the identity of the
+contact associated with a slot changes, the driver should invalidate that
+slot by changing its ABS_MT_TRACKING_ID.  If the hardware signals that it is
+tracking more contacts than it is currently reporting, the driver should use
+a BTN_TOOL_*TAP event to inform userspace of the total number of contacts
+being tracked by the hardware at that moment.  The driver should do this by
+explicitly sending the corresponding BTN_TOOL_*TAP event and setting
+use_count to false when calling input_mt_report_pointer_emulation().
+The driver should only advertise as many slots as the hardware can report.
+Userspace can detect that a driver can report more total contacts than slots
+by noting that the largest supported BTN_TOOL_*TAP event is larger than the
+total number of type B slots reported in the absinfo for the ABS_MT_SLOT axis.
 
 Protocol Example A
 ------------------
index c48c81f..9150ee7 100644 (file)
@@ -117,6 +117,7 @@ void input_mt_report_finger_count(struct input_dev *dev, int count)
        input_event(dev, EV_KEY, BTN_TOOL_DOUBLETAP, count == 2);
        input_event(dev, EV_KEY, BTN_TOOL_TRIPLETAP, count == 3);
        input_event(dev, EV_KEY, BTN_TOOL_QUADTAP, count == 4);
+       input_event(dev, EV_KEY, BTN_TOOL_QUINTTAP, count == 5);
 }
 EXPORT_SYMBOL(input_mt_report_finger_count);
 
index b1aabde..b253973 100644 (file)
@@ -49,8 +49,10 @@ static int input_open_polled_device(struct input_dev *input)
                dev->open(dev);
 
        /* Only start polling if polling is enabled */
-       if (dev->poll_interval > 0)
-               queue_delayed_work(system_freezable_wq, &dev->work, 0);
+       if (dev->poll_interval > 0) {
+               dev->poll(dev);
+               input_polldev_queue_work(dev);
+       }
 
        return 0;
 }
index c9104bb..a1aa35a 100644 (file)
@@ -62,6 +62,17 @@ config INPUT_AD714X_SPI
          To compile this driver as a module, choose M here: the
          module will be called ad714x-spi.
 
+config INPUT_BMA150
+       tristate "BMA150/SMB380 acceleration sensor support"
+       depends on I2C
+       select INPUT_POLLDEV
+       help
+         Say Y here if you have Bosch Sensortec's BMA150 or SMB380
+         acceleration sensor hooked to an I2C bus.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bma150.
+
 config INPUT_PCSPKR
        tristate "PC Speaker support"
        depends on PCSPKR_PLATFORM
@@ -74,6 +85,29 @@ config INPUT_PCSPKR
          To compile this driver as a module, choose M here: the
          module will be called pcspkr.
 
+config INPUT_PM8XXX_VIBRATOR
+       tristate "Qualcomm PM8XXX vibrator support"
+       depends on MFD_PM8XXX
+       select INPUT_FF_MEMLESS
+       help
+         This option enables device driver support for the vibrator
+         on Qualcomm PM8xxx chip. This driver supports ff-memless interface
+         from input framework.
+
+         To compile this driver as module, choose M here: the
+         module will be called pm8xxx-vibrator.
+
+config INPUT_PMIC8XXX_PWRKEY
+       tristate "PMIC8XXX power key support"
+       depends on MFD_PM8XXX
+       help
+         Say Y here if you want support for the PMIC8XXX power key.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called pmic8xxx-pwrkey.
+
 config INPUT_SPARCSPKR
        tristate "SPARC Speaker support"
        depends on PCI && SPARC64
@@ -379,17 +413,6 @@ config INPUT_PWM_BEEPER
          To compile this driver as a module, choose M here: the module will be
          called pwm-beeper.
 
-config INPUT_PMIC8XXX_PWRKEY
-       tristate "PMIC8XXX power key support"
-       depends on MFD_PM8XXX
-       help
-         Say Y here if you want support for the PMIC8XXX power key.
-
-         If unsure, say N.
-
-         To compile this driver as a module, choose M here: the
-         module will be called pmic8xxx-pwrkey.
-
 config INPUT_GPIO_ROTARY_ENCODER
        tristate "Rotary encoders connected to GPIO pins"
        depends on GPIOLIB && GENERIC_GPIO
index 299ad5e..53a8d0f 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE)                += ati_remote.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)                += ati_remote2.o
 obj-$(CONFIG_INPUT_ATLAS_BTNS)         += atlas_btns.o
 obj-$(CONFIG_INPUT_BFIN_ROTARY)                += bfin_rotary.o
+obj-$(CONFIG_INPUT_BMA150)             += bma150.o
 obj-$(CONFIG_INPUT_CM109)              += cm109.o
 obj-$(CONFIG_INPUT_CMA3000)            += cma3000_d0x.o
 obj-$(CONFIG_INPUT_CMA3000_I2C)                += cma3000_d0x_i2c.o
@@ -34,9 +35,10 @@ obj-$(CONFIG_INPUT_PCAP)             += pcap_keys.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
 obj-$(CONFIG_INPUT_PCF8574)            += pcf8574_keypad.o
 obj-$(CONFIG_INPUT_PCSPKR)             += pcspkr.o
+obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)    += pm8xxx-vibrator.o
+obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)    += pmic8xxx-pwrkey.o
 obj-$(CONFIG_INPUT_POWERMATE)          += powermate.o
 obj-$(CONFIG_INPUT_PWM_BEEPER)         += pwm-beeper.o
-obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)    += pmic8xxx-pwrkey.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)       += rb532_button.o
 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)        += rotary_encoder.o
 obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
index 025417d..56810fb 100644 (file)
@@ -116,13 +116,13 @@ static struct i2c_driver ad714x_i2c_driver = {
        .id_table = ad714x_id,
 };
 
-static __init int ad714x_i2c_init(void)
+static int __init ad714x_i2c_init(void)
 {
        return i2c_add_driver(&ad714x_i2c_driver);
 }
 module_init(ad714x_i2c_init);
 
-static __exit void ad714x_i2c_exit(void)
+static void __exit ad714x_i2c_exit(void)
 {
        i2c_del_driver(&ad714x_i2c_driver);
 }
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
new file mode 100644 (file)
index 0000000..8f55b54
--- /dev/null
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2011 Bosch Sensortec GmbH
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for Bosch Sensortec's digital acceleration
+ * sensors BMA150 and SMB380.
+ * The SMB380 is fully compatible with BMA150 and only differs in packaging.
+ *
+ * The datasheet for the BMA150 chip can be found here:
+ * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/bma150.h>
+
+#define ABSMAX_ACC_VAL         0x01FF
+#define ABSMIN_ACC_VAL         -(ABSMAX_ACC_VAL)
+
+/* Each axis is represented by a 2-byte data word */
+#define BMA150_XYZ_DATA_SIZE   6
+
+/* Input poll interval in milliseconds */
+#define BMA150_POLL_INTERVAL   10
+#define BMA150_POLL_MAX                200
+#define BMA150_POLL_MIN                0
+
+#define BMA150_BW_25HZ         0
+#define BMA150_BW_50HZ         1
+#define BMA150_BW_100HZ                2
+#define BMA150_BW_190HZ                3
+#define BMA150_BW_375HZ                4
+#define BMA150_BW_750HZ                5
+#define BMA150_BW_1500HZ       6
+
+#define BMA150_RANGE_2G                0
+#define BMA150_RANGE_4G                1
+#define BMA150_RANGE_8G                2
+
+#define BMA150_MODE_NORMAL     0
+#define BMA150_MODE_SLEEP      2
+#define BMA150_MODE_WAKE_UP    3
+
+/* Data register addresses */
+#define BMA150_DATA_0_REG      0x00
+#define BMA150_DATA_1_REG      0x01
+#define BMA150_DATA_2_REG      0x02
+
+/* Control register addresses */
+#define BMA150_CTRL_0_REG      0x0A
+#define BMA150_CTRL_1_REG      0x0B
+#define BMA150_CTRL_2_REG      0x14
+#define BMA150_CTRL_3_REG      0x15
+
+/* Configuration/Setting register addresses */
+#define BMA150_CFG_0_REG       0x0C
+#define BMA150_CFG_1_REG       0x0D
+#define BMA150_CFG_2_REG       0x0E
+#define BMA150_CFG_3_REG       0x0F
+#define BMA150_CFG_4_REG       0x10
+#define BMA150_CFG_5_REG       0x11
+
+#define BMA150_CHIP_ID         2
+#define BMA150_CHIP_ID_REG     BMA150_DATA_0_REG
+
+#define BMA150_ACC_X_LSB_REG   BMA150_DATA_2_REG
+
+#define BMA150_SLEEP_POS       0
+#define BMA150_SLEEP_MSK       0x01
+#define BMA150_SLEEP_REG       BMA150_CTRL_0_REG
+
+#define BMA150_BANDWIDTH_POS   0
+#define BMA150_BANDWIDTH_MSK   0x07
+#define BMA150_BANDWIDTH_REG   BMA150_CTRL_2_REG
+
+#define BMA150_RANGE_POS       3
+#define BMA150_RANGE_MSK       0x18
+#define BMA150_RANGE_REG       BMA150_CTRL_2_REG
+
+#define BMA150_WAKE_UP_POS     0
+#define BMA150_WAKE_UP_MSK     0x01
+#define BMA150_WAKE_UP_REG     BMA150_CTRL_3_REG
+
+#define BMA150_SW_RES_POS      1
+#define BMA150_SW_RES_MSK      0x02
+#define BMA150_SW_RES_REG      BMA150_CTRL_0_REG
+
+/* Any-motion interrupt register fields */
+#define BMA150_ANY_MOTION_EN_POS       6
+#define BMA150_ANY_MOTION_EN_MSK       0x40
+#define BMA150_ANY_MOTION_EN_REG       BMA150_CTRL_1_REG
+
+#define BMA150_ANY_MOTION_DUR_POS      6
+#define BMA150_ANY_MOTION_DUR_MSK      0xC0
+#define BMA150_ANY_MOTION_DUR_REG      BMA150_CFG_5_REG
+
+#define BMA150_ANY_MOTION_THRES_REG    BMA150_CFG_4_REG
+
+/* Advanced interrupt register fields */
+#define BMA150_ADV_INT_EN_POS          6
+#define BMA150_ADV_INT_EN_MSK          0x40
+#define BMA150_ADV_INT_EN_REG          BMA150_CTRL_3_REG
+
+/* High-G interrupt register fields */
+#define BMA150_HIGH_G_EN_POS           1
+#define BMA150_HIGH_G_EN_MSK           0x02
+#define BMA150_HIGH_G_EN_REG           BMA150_CTRL_1_REG
+
+#define BMA150_HIGH_G_HYST_POS         3
+#define BMA150_HIGH_G_HYST_MSK         0x38
+#define BMA150_HIGH_G_HYST_REG         BMA150_CFG_5_REG
+
+#define BMA150_HIGH_G_DUR_REG          BMA150_CFG_3_REG
+#define BMA150_HIGH_G_THRES_REG                BMA150_CFG_2_REG
+
+/* Low-G interrupt register fields */
+#define BMA150_LOW_G_EN_POS            0
+#define BMA150_LOW_G_EN_MSK            0x01
+#define BMA150_LOW_G_EN_REG            BMA150_CTRL_1_REG
+
+#define BMA150_LOW_G_HYST_POS          0
+#define BMA150_LOW_G_HYST_MSK          0x07
+#define BMA150_LOW_G_HYST_REG          BMA150_CFG_5_REG
+
+#define BMA150_LOW_G_DUR_REG           BMA150_CFG_1_REG
+#define BMA150_LOW_G_THRES_REG         BMA150_CFG_0_REG
+
+struct bma150_data {
+       struct i2c_client *client;
+       struct input_polled_dev *input_polled;
+       struct input_dev *input;
+       u8 mode;
+};
+
+/*
+ * The settings for the given range, bandwidth and interrupt features
+ * are stated and verified by Bosch Sensortec where they are configured
+ * to provide a generic sensitivity performance.
+ */
+static struct bma150_cfg default_cfg __devinitdata = {
+       .any_motion_int = 1,
+       .hg_int = 1,
+       .lg_int = 1,
+       .any_motion_dur = 0,
+       .any_motion_thres = 0,
+       .hg_hyst = 0,
+       .hg_dur = 150,
+       .hg_thres = 160,
+       .lg_hyst = 0,
+       .lg_dur = 150,
+       .lg_thres = 20,
+       .range = BMA150_RANGE_2G,
+       .bandwidth = BMA150_BW_50HZ
+};
+
+static int bma150_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+       s32 ret;
+
+       /* As per specification, disable irq in between register writes */
+       if (client->irq)
+               disable_irq_nosync(client->irq);
+
+       ret = i2c_smbus_write_byte_data(client, reg, val);
+
+       if (client->irq)
+               enable_irq(client->irq);
+
+       return ret;
+}
+
+static int bma150_set_reg_bits(struct i2c_client *client,
+                                       int val, int shift, u8 mask, u8 reg)
+{
+       int data;
+
+       data = i2c_smbus_read_byte_data(client, reg);
+       if (data < 0)
+               return data;
+
+       data = (data & ~mask) | ((val << shift) & mask);
+       return bma150_write_byte(client, reg, data);
+}
+
+static int bma150_set_mode(struct bma150_data *bma150, u8 mode)
+{
+       int error;
+
+       error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS,
+                               BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG);
+       if (error)
+               return error;
+
+       error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS,
+                               BMA150_SLEEP_MSK, BMA150_SLEEP_REG);
+       if (error)
+               return error;
+
+       if (mode == BMA150_MODE_NORMAL)
+               msleep(2);
+
+       bma150->mode = mode;
+       return 0;
+}
+
+static int __devinit bma150_soft_reset(struct bma150_data *bma150)
+{
+       int error;
+
+       error = bma150_set_reg_bits(bma150->client, 1, BMA150_SW_RES_POS,
+                               BMA150_SW_RES_MSK, BMA150_SW_RES_REG);
+       if (error)
+               return error;
+
+       msleep(2);
+       return 0;
+}
+
+static int __devinit bma150_set_range(struct bma150_data *bma150, u8 range)
+{
+       return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS,
+                               BMA150_RANGE_MSK, BMA150_RANGE_REG);
+}
+
+static int __devinit bma150_set_bandwidth(struct bma150_data *bma150, u8 bw)
+{
+       return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS,
+                               BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG);
+}
+
+static int __devinit bma150_set_low_g_interrupt(struct bma150_data *bma150,
+                                       u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+       int error;
+
+       error = bma150_set_reg_bits(bma150->client, hyst,
+                               BMA150_LOW_G_HYST_POS, BMA150_LOW_G_HYST_MSK,
+                               BMA150_LOW_G_HYST_REG);
+       if (error)
+               return error;
+
+       error = bma150_write_byte(bma150->client, BMA150_LOW_G_DUR_REG, dur);
+       if (error)
+               return error;
+
+       error = bma150_write_byte(bma150->client, BMA150_LOW_G_THRES_REG, thres);
+       if (error)
+               return error;
+
+       return bma150_set_reg_bits(bma150->client, !!enable,
+                               BMA150_LOW_G_EN_POS, BMA150_LOW_G_EN_MSK,
+                               BMA150_LOW_G_EN_REG);
+}
+
+static int __devinit bma150_set_high_g_interrupt(struct bma150_data *bma150,
+                                       u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+       int error;
+
+       error = bma150_set_reg_bits(bma150->client, hyst,
+                               BMA150_HIGH_G_HYST_POS, BMA150_HIGH_G_HYST_MSK,
+                               BMA150_HIGH_G_HYST_REG);
+       if (error)
+               return error;
+
+       error = bma150_write_byte(bma150->client,
+                               BMA150_HIGH_G_DUR_REG, dur);
+       if (error)
+               return error;
+
+       error = bma150_write_byte(bma150->client,
+                               BMA150_HIGH_G_THRES_REG, thres);
+       if (error)
+               return error;
+
+       return bma150_set_reg_bits(bma150->client, !!enable,
+                               BMA150_HIGH_G_EN_POS, BMA150_HIGH_G_EN_MSK,
+                               BMA150_HIGH_G_EN_REG);
+}
+
+
+static int __devinit bma150_set_any_motion_interrupt(struct bma150_data *bma150,
+                                               u8 enable, u8 dur, u8 thres)
+{
+       int error;
+
+       error = bma150_set_reg_bits(bma150->client, dur,
+                               BMA150_ANY_MOTION_DUR_POS,
+                               BMA150_ANY_MOTION_DUR_MSK,
+                               BMA150_ANY_MOTION_DUR_REG);
+       if (error)
+               return error;
+
+       error = bma150_write_byte(bma150->client,
+                               BMA150_ANY_MOTION_THRES_REG, thres);
+       if (error)
+               return error;
+
+       error = bma150_set_reg_bits(bma150->client, !!enable,
+                               BMA150_ADV_INT_EN_POS, BMA150_ADV_INT_EN_MSK,
+                               BMA150_ADV_INT_EN_REG);
+       if (error)
+               return error;
+
+       return bma150_set_reg_bits(bma150->client, !!enable,
+                               BMA150_ANY_MOTION_EN_POS,
+                               BMA150_ANY_MOTION_EN_MSK,
+                               BMA150_ANY_MOTION_EN_REG);
+}
+
+static void bma150_report_xyz(struct bma150_data *bma150)
+{
+       u8 data[BMA150_XYZ_DATA_SIZE];
+       s16 x, y, z;
+       s32 ret;
+
+       ret = i2c_smbus_read_i2c_block_data(bma150->client,
+                       BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data);
+       if (ret != BMA150_XYZ_DATA_SIZE)
+               return;
+
+       x = ((0xc0 & data[0]) >> 6) | (data[1] << 2);
+       y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);
+       z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);
+
+       /* sign extension */
+       x = (s16) (x << 6) >> 6;
+       y = (s16) (y << 6) >> 6;
+       z = (s16) (z << 6) >> 6;
+
+       input_report_abs(bma150->input, ABS_X, x);
+       input_report_abs(bma150->input, ABS_Y, y);
+       input_report_abs(bma150->input, ABS_Z, z);
+       input_sync(bma150->input);
+}
+
+static irqreturn_t bma150_irq_thread(int irq, void *dev)
+{
+       bma150_report_xyz(dev);
+
+       return IRQ_HANDLED;
+}
+
+static void bma150_poll(struct input_polled_dev *dev)
+{
+       bma150_report_xyz(dev->private);
+}
+
+static int bma150_open(struct bma150_data *bma150)
+{
+       int error;
+
+       error = pm_runtime_get_sync(&bma150->client->dev);
+       if (error && error != -ENOSYS)
+               return error;
+
+       /*
+        * See if runtime PM woke up the device. If runtime PM
+        * is disabled we need to do it ourselves.
+        */
+       if (bma150->mode != BMA150_MODE_NORMAL) {
+               error = bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+               if (error)
+                       return error;
+       }
+
+       return 0;
+}
+
+static void bma150_close(struct bma150_data *bma150)
+{
+       pm_runtime_put_sync(&bma150->client->dev);
+
+       if (bma150->mode != BMA150_MODE_SLEEP)
+               bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_irq_open(struct input_dev *input)
+{
+       struct bma150_data *bma150 = input_get_drvdata(input);
+
+       return bma150_open(bma150);
+}
+
+static void bma150_irq_close(struct input_dev *input)
+{
+       struct bma150_data *bma150 = input_get_drvdata(input);
+
+       bma150_close(bma150);
+}
+
+static void bma150_poll_open(struct input_polled_dev *ipoll_dev)
+{
+       struct bma150_data *bma150 = ipoll_dev->private;
+
+       bma150_open(bma150);
+}
+
+static void bma150_poll_close(struct input_polled_dev *ipoll_dev)
+{
+       struct bma150_data *bma150 = ipoll_dev->private;
+
+       bma150_close(bma150);
+}
+
+static int __devinit bma150_initialize(struct bma150_data *bma150,
+                                      const struct bma150_cfg *cfg)
+{
+       int error;
+
+       error = bma150_soft_reset(bma150);
+       if (error)
+               return error;
+
+       error = bma150_set_bandwidth(bma150, cfg->bandwidth);
+       if (error)
+               return error;
+
+       error = bma150_set_range(bma150, cfg->range);
+       if (error)
+               return error;
+
+       if (bma150->client->irq) {
+               error = bma150_set_any_motion_interrupt(bma150,
+                                       cfg->any_motion_int,
+                                       cfg->any_motion_dur,
+                                       cfg->any_motion_thres);
+               if (error)
+                       return error;
+
+               error = bma150_set_high_g_interrupt(bma150,
+                                       cfg->hg_int, cfg->hg_hyst,
+                                       cfg->hg_dur, cfg->hg_thres);
+               if (error)
+                       return error;
+
+               error = bma150_set_low_g_interrupt(bma150,
+                                       cfg->lg_int, cfg->lg_hyst,
+                                       cfg->lg_dur, cfg->lg_thres);
+               if (error)
+                       return error;
+       }
+
+       return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static void __devinit bma150_init_input_device(struct bma150_data *bma150,
+                                               struct input_dev *idev)
+{
+       idev->name = BMA150_DRIVER;
+       idev->phys = BMA150_DRIVER "/input0";
+       idev->id.bustype = BUS_I2C;
+       idev->dev.parent = &bma150->client->dev;
+
+       idev->evbit[0] = BIT_MASK(EV_ABS);
+       input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+       input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+       input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+}
+
+static int __devinit bma150_register_input_device(struct bma150_data *bma150)
+{
+       struct input_dev *idev;
+       int error;
+
+       idev = input_allocate_device();
+       if (!idev)
+               return -ENOMEM;
+
+       bma150_init_input_device(bma150, idev);
+
+       idev->open = bma150_irq_open;
+       idev->close = bma150_irq_close;
+       input_set_drvdata(idev, bma150);
+
+       error = input_register_device(idev);
+       if (error) {
+               input_free_device(idev);
+               return error;
+       }
+
+       bma150->input = idev;
+       return 0;
+}
+
+static int __devinit bma150_register_polled_device(struct bma150_data *bma150)
+{
+       struct input_polled_dev *ipoll_dev;
+       int error;
+
+       ipoll_dev = input_allocate_polled_device();
+       if (!ipoll_dev)
+               return -ENOMEM;
+
+       ipoll_dev->private = bma150;
+       ipoll_dev->open = bma150_poll_open;
+       ipoll_dev->close = bma150_poll_close;
+       ipoll_dev->poll = bma150_poll;
+       ipoll_dev->poll_interval = BMA150_POLL_INTERVAL;
+       ipoll_dev->poll_interval_min = BMA150_POLL_MIN;
+       ipoll_dev->poll_interval_max = BMA150_POLL_MAX;
+
+       bma150_init_input_device(bma150, ipoll_dev->input);
+
+       error = input_register_polled_device(ipoll_dev);
+       if (error) {
+               input_free_polled_device(ipoll_dev);
+               return error;
+       }
+
+       bma150->input_polled = ipoll_dev;
+       bma150->input = ipoll_dev->input;
+
+       return 0;
+}
+
+static int __devinit bma150_probe(struct i2c_client *client,
+                                 const struct i2c_device_id *id)
+{
+       const struct bma150_platform_data *pdata = client->dev.platform_data;
+       const struct bma150_cfg *cfg;
+       struct bma150_data *bma150;
+       int chip_id;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev, "i2c_check_functionality error\n");
+               return -EIO;
+       }
+
+       chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
+       if (chip_id != BMA150_CHIP_ID) {
+               dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
+               return -EINVAL;
+       }
+
+       bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+       if (!bma150)
+               return -ENOMEM;
+
+       bma150->client = client;
+
+       if (pdata) {
+               if (pdata->irq_gpio_cfg) {
+                       error = pdata->irq_gpio_cfg();
+                       if (error) {
+                               dev_err(&client->dev,
+                                       "IRQ GPIO conf. error %d, error %d\n",
+                                       client->irq, error);
+                               goto err_free_mem;
+                       }
+               }
+               cfg = &pdata->cfg;
+       } else {
+               cfg = &default_cfg;
+       }
+
+       error = bma150_initialize(bma150, cfg);
+       if (error)
+               goto err_free_mem;
+
+       if (client->irq > 0) {
+               error = bma150_register_input_device(bma150);
+               if (error)
+                       goto err_free_mem;
+
+               error = request_threaded_irq(client->irq,
+                                       NULL, bma150_irq_thread,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       BMA150_DRIVER, bma150);
+               if (error) {
+                       dev_err(&client->dev,
+                               "irq request failed %d, error %d\n",
+                               client->irq, error);
+                       input_unregister_device(bma150->input);
+                       goto err_free_mem;
+               }
+       } else {
+               error = bma150_register_polled_device(bma150);
+               if (error)
+                       goto err_free_mem;
+       }
+
+       i2c_set_clientdata(client, bma150);
+
+       pm_runtime_enable(&client->dev);
+
+       return 0;
+
+err_free_mem:
+       kfree(bma150);
+       return error;
+}
+
+static int __devexit bma150_remove(struct i2c_client *client)
+{
+       struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+       pm_runtime_disable(&client->dev);
+
+       if (client->irq > 0) {
+               free_irq(client->irq, bma150);
+               input_unregister_device(bma150->input);
+       } else {
+               input_unregister_polled_device(bma150->input_polled);
+               input_free_polled_device(bma150->input_polled);
+       }
+
+       kfree(bma150);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int bma150_suspend(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+       return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_resume(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+       return bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
+
+static const struct i2c_device_id bma150_id[] = {
+       { "bma150", 0 },
+       { "smb380", 0 },
+       { "bma023", 0 },
+       { }
+};
+
+MODULE_DEVICE_TABLE(i2c, bma150_id);
+
+static struct i2c_driver bma150_driver = {
+       .driver = {
+               .owner  = THIS_MODULE,
+               .name   = BMA150_DRIVER,
+               .pm     = &bma150_pm,
+       },
+       .class          = I2C_CLASS_HWMON,
+       .id_table       = bma150_id,
+       .probe          = bma150_probe,
+       .remove         = __devexit_p(bma150_remove),
+};
+
+static int __init BMA150_init(void)
+{
+       return i2c_add_driver(&bma150_driver);
+}
+
+static void __exit BMA150_exit(void)
+{
+       i2c_del_driver(&bma150_driver);
+}
+
+MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>");
+MODULE_DESCRIPTION("BMA150 driver");
+MODULE_LICENSE("GPL");
+
+module_init(BMA150_init);
+module_exit(BMA150_exit);
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
new file mode 100644 (file)
index 0000000..4319293
--- /dev/null
@@ -0,0 +1,296 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define VIB_DRV                        0x4A
+
+#define VIB_DRV_SEL_MASK       0xf8
+#define VIB_DRV_SEL_SHIFT      0x03
+#define VIB_DRV_EN_MANUAL_MASK 0xfc
+
+#define VIB_MAX_LEVEL_mV       (3100)
+#define VIB_MIN_LEVEL_mV       (1200)
+#define VIB_MAX_LEVELS         (VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
+
+#define MAX_FF_SPEED           0xff
+
+/**
+ * struct pm8xxx_vib - structure to hold vibrator data
+ * @vib_input_dev: input device supporting force feedback
+ * @work: work structure to set the vibration parameters
+ * @dev: device supporting force feedback
+ * @speed: speed of vibration set from userland
+ * @active: state of vibrator
+ * @level: level of vibration to set in the chip
+ * @reg_vib_drv: VIB_DRV register value
+ */
+struct pm8xxx_vib {
+       struct input_dev *vib_input_dev;
+       struct work_struct work;
+       struct device *dev;
+       int speed;
+       int level;
+       bool active;
+       u8  reg_vib_drv;
+};
+
+/**
+ * pm8xxx_vib_read_u8 - helper to read a byte from pmic chip
+ * @vib: pointer to vibrator structure
+ * @data: placeholder for data to be read
+ * @reg: register address
+ */
+static int pm8xxx_vib_read_u8(struct pm8xxx_vib *vib,
+                                u8 *data, u16 reg)
+{
+       int rc;
+
+       rc = pm8xxx_readb(vib->dev->parent, reg, data);
+       if (rc < 0)
+               dev_warn(vib->dev, "Error reading pm8xxx reg 0x%x(0x%x)\n",
+                               reg, rc);
+       return rc;
+}
+
+/**
+ * pm8xxx_vib_write_u8 - helper to write a byte to pmic chip
+ * @vib: pointer to vibrator structure
+ * @data: data to write
+ * @reg: register address
+ */
+static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib,
+                                u8 data, u16 reg)
+{
+       int rc;
+
+       rc = pm8xxx_writeb(vib->dev->parent, reg, data);
+       if (rc < 0)
+               dev_warn(vib->dev, "Error writing pm8xxx reg 0x%x(0x%x)\n",
+                               reg, rc);
+       return rc;
+}
+
+/**
+ * pm8xxx_vib_set - handler to start/stop vibration
+ * @vib: pointer to vibrator structure
+ * @on: state to set
+ */
+static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
+{
+       int rc;
+       u8 val = vib->reg_vib_drv;
+
+       if (on)
+               val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+       else
+               val &= ~VIB_DRV_SEL_MASK;
+
+       rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+       if (rc < 0)
+               return rc;
+
+       vib->reg_vib_drv = val;
+       return 0;
+}
+
+/**
+ * pm8xxx_work_handler - worker to set vibration level
+ * @work: pointer to work_struct
+ */
+static void pm8xxx_work_handler(struct work_struct *work)
+{
+       struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work);
+       int rc;
+       u8 val;
+
+       rc = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+       if (rc < 0)
+               return;
+
+       /*
+        * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so
+        * scale the level to fit into these ranges.
+        */
+       if (vib->speed) {
+               vib->active = true;
+               vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) +
+                                               VIB_MIN_LEVEL_mV;
+               vib->level /= 100;
+       } else {
+               vib->active = false;
+               vib->level = VIB_MIN_LEVEL_mV / 100;
+       }
+
+       pm8xxx_vib_set(vib, vib->active);
+}
+
+/**
+ * pm8xxx_vib_close - callback of input close callback
+ * @dev: input device pointer
+ *
+ * Turns off the vibrator.
+ */
+static void pm8xxx_vib_close(struct input_dev *dev)
+{
+       struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+       cancel_work_sync(&vib->work);
+       if (vib->active)
+               pm8xxx_vib_set(vib, false);
+}
+
+/**
+ * pm8xxx_vib_play_effect - function to handle vib effects.
+ * @dev: input device pointer
+ * @data: data of effect
+ * @effect: effect to play
+ *
+ * Currently this driver supports only rumble effects.
+ */
+static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
+                                 struct ff_effect *effect)
+{
+       struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+       vib->speed = effect->u.rumble.strong_magnitude >> 8;
+       if (!vib->speed)
+               vib->speed = effect->u.rumble.weak_magnitude >> 9;
+
+       schedule_work(&vib->work);
+
+       return 0;
+}
+
+static int __devinit pm8xxx_vib_probe(struct platform_device *pdev)
+
+{
+       struct pm8xxx_vib *vib;
+       struct input_dev *input_dev;
+       int error;
+       u8 val;
+
+       vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!vib || !input_dev) {
+               dev_err(&pdev->dev, "couldn't allocate memory\n");
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       INIT_WORK(&vib->work, pm8xxx_work_handler);
+       vib->dev = &pdev->dev;
+       vib->vib_input_dev = input_dev;
+
+       /* operate in manual mode */
+       error = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+       if (error < 0)
+               goto err_free_mem;
+       val &= ~VIB_DRV_EN_MANUAL_MASK;
+       error = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+       if (error < 0)
+               goto err_free_mem;
+
+       vib->reg_vib_drv = val;
+
+       input_dev->name = "pm8xxx_vib_ffmemless";
+       input_dev->id.version = 1;
+       input_dev->dev.parent = &pdev->dev;
+       input_dev->close = pm8xxx_vib_close;
+       input_set_drvdata(input_dev, vib);
+       input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE);
+
+       error = input_ff_create_memless(input_dev, NULL,
+                                       pm8xxx_vib_play_effect);
+       if (error) {
+               dev_err(&pdev->dev,
+                       "couldn't register vibrator as FF device\n");
+               goto err_free_mem;
+       }
+
+       error = input_register_device(input_dev);
+       if (error) {
+               dev_err(&pdev->dev, "couldn't register input device\n");
+               goto err_destroy_memless;
+       }
+
+       platform_set_drvdata(pdev, vib);
+       return 0;
+
+err_destroy_memless:
+       input_ff_destroy(input_dev);
+err_free_mem:
+       input_free_device(input_dev);
+       kfree(vib);
+
+       return error;
+}
+
+static int __devexit pm8xxx_vib_remove(struct platform_device *pdev)
+{
+       struct pm8xxx_vib *vib = platform_get_drvdata(pdev);
+
+       input_unregister_device(vib->vib_input_dev);
+       kfree(vib);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm8xxx_vib_suspend(struct device *dev)
+{
+       struct pm8xxx_vib *vib = dev_get_drvdata(dev);
+
+       /* Turn off the vibrator */
+       pm8xxx_vib_set(vib, false);
+
+       return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
+
+static struct platform_driver pm8xxx_vib_driver = {
+       .probe          = pm8xxx_vib_probe,
+       .remove         = __devexit_p(pm8xxx_vib_remove),
+       .driver         = {
+               .name   = "pm8xxx-vib",
+               .owner  = THIS_MODULE,
+               .pm     = &pm8xxx_vib_pm_ops,
+       },
+};
+
+static int __init pm8xxx_vib_init(void)
+{
+       return platform_driver_register(&pm8xxx_vib_driver);
+}
+module_init(pm8xxx_vib_init);
+
+static void __exit pm8xxx_vib_exit(void)
+{
+       platform_driver_unregister(&pm8xxx_vib_driver);
+}
+module_exit(pm8xxx_vib_exit);
+
+MODULE_ALIAS("platform:pm8xxx_vib");
+MODULE_DESCRIPTION("PMIC8xxx vibrator driver based on ff-memless framework");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Amy Maloche <amaloche@codeaurora.org>");
index 5538fc6..30c85a5 100644 (file)
 #define YMIN_NOMINAL 1408
 #define YMAX_NOMINAL 4448
 
+/*
+ * 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;
+}
+
 
 /*****************************************************************************
  *     Stuff we need even when we do not want native Synaptics support
@@ -294,7 +304,8 @@ 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))
+       if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+                       SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
                return 0;
 
        if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
@@ -409,6 +420,44 @@ static void synaptics_pt_create(struct psmouse *psmouse)
  *     Functions to interpret the absolute mode packets
  ****************************************************************************/
 
+static void synaptics_mt_state_set(struct synaptics_mt_state *state, int count,
+                                  int sgm, int agm)
+{
+       state->count = count;
+       state->sgm = sgm;
+       state->agm = agm;
+}
+
+static void synaptics_parse_agm(const unsigned char buf[],
+                               struct synaptics_data *priv,
+                               struct synaptics_hw_state *hw)
+{
+       struct synaptics_hw_state *agm = &priv->agm;
+       int agm_packet_type;
+
+       agm_packet_type = (buf[5] & 0x30) >> 4;
+       switch (agm_packet_type) {
+       case 1:
+               /* Gesture packet: (x, y, z) half resolution */
+               agm->w = hw->w;
+               agm->x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
+               agm->y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
+               agm->z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+               break;
+
+       case 2:
+               /* AGM-CONTACT packet: (count, sgm, agm) */
+               synaptics_mt_state_set(&agm->mt_state, buf[1], buf[2], buf[4]);
+               break;
+
+       default:
+               break;
+       }
+
+       /* Record that at least one AGM has been received since last SGM */
+       priv->agm_pending = true;
+}
+
 static int synaptics_parse_hw_state(const unsigned char buf[],
                                    struct synaptics_data *priv,
                                    struct synaptics_hw_state *hw)
@@ -442,11 +491,10 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
                        hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
                }
 
-               if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
-                       /* Gesture packet: (x, y, z) at half resolution */
-                       priv->mt.x = (((buf[4] & 0x0f) << 8) | buf[1]) << 1;
-                       priv->mt.y = (((buf[4] & 0xf0) << 4) | buf[2]) << 1;
-                       priv->mt.z = ((buf[3] & 0x30) | (buf[5] & 0x0f)) << 1;
+               if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+                       SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) &&
+                   hw->w == 2) {
+                       synaptics_parse_agm(buf, priv, hw);
                        return 1;
                }
 
@@ -502,8 +550,7 @@ static void synaptics_report_semi_mt_slot(struct input_dev *dev, int slot,
        input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
        if (active) {
                input_report_abs(dev, ABS_MT_POSITION_X, x);
-               input_report_abs(dev, ABS_MT_POSITION_Y,
-                                YMAX_NOMINAL + YMIN_NOMINAL - y);
+               input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(y));
        }
 }
 
@@ -526,6 +573,388 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev,
        }
 }
 
+static void synaptics_report_buttons(struct psmouse *psmouse,
+                                    const struct synaptics_hw_state *hw)
+{
+       struct input_dev *dev = psmouse->dev;
+       struct synaptics_data *priv = psmouse->private;
+       int i;
+
+       input_report_key(dev, BTN_LEFT, hw->left);
+       input_report_key(dev, BTN_RIGHT, hw->right);
+
+       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
+               input_report_key(dev, BTN_MIDDLE, hw->middle);
+
+       if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
+               input_report_key(dev, BTN_FORWARD, hw->up);
+               input_report_key(dev, BTN_BACK, hw->down);
+       }
+
+       for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
+               input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i));
+}
+
+static void synaptics_report_slot(struct input_dev *dev, int slot,
+                                 const struct synaptics_hw_state *hw)
+{
+       input_mt_slot(dev, slot);
+       input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL));
+       if (!hw)
+               return;
+
+       input_report_abs(dev, ABS_MT_POSITION_X, hw->x);
+       input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y));
+       input_report_abs(dev, ABS_MT_PRESSURE, hw->z);
+}
+
+static void synaptics_report_mt_data(struct psmouse *psmouse,
+                                    struct synaptics_mt_state *mt_state,
+                                    const struct synaptics_hw_state *sgm)
+{
+       struct input_dev *dev = psmouse->dev;
+       struct synaptics_data *priv = psmouse->private;
+       struct synaptics_hw_state *agm = &priv->agm;
+       struct synaptics_mt_state *old = &priv->mt_state;
+
+       switch (mt_state->count) {
+       case 0:
+               synaptics_report_slot(dev, 0, NULL);
+               synaptics_report_slot(dev, 1, NULL);
+               break;
+       case 1:
+               if (mt_state->sgm == -1) {
+                       synaptics_report_slot(dev, 0, NULL);
+                       synaptics_report_slot(dev, 1, NULL);
+               } else if (mt_state->sgm == 0) {
+                       synaptics_report_slot(dev, 0, sgm);
+                       synaptics_report_slot(dev, 1, NULL);
+               } else {
+                       synaptics_report_slot(dev, 0, NULL);
+                       synaptics_report_slot(dev, 1, sgm);
+               }
+               break;
+       default:
+               /*
+                * If the finger slot contained in SGM is valid, and either
+                * hasn't changed, or is new, then report SGM in MTB slot 0.
+                * Otherwise, empty MTB slot 0.
+                */
+               if (mt_state->sgm != -1 &&
+                   (mt_state->sgm == old->sgm || old->sgm == -1))
+                       synaptics_report_slot(dev, 0, sgm);
+               else
+                       synaptics_report_slot(dev, 0, NULL);
+
+               /*
+                * If the finger slot contained in AGM is valid, and either
+                * hasn't changed, or is new, then report AGM in MTB slot 1.
+                * Otherwise, empty MTB slot 1.
+                */
+               if (mt_state->agm != -1 &&
+                   (mt_state->agm == old->agm || old->agm == -1))
+                       synaptics_report_slot(dev, 1, agm);
+               else
+                       synaptics_report_slot(dev, 1, NULL);
+               break;
+       }
+
+       /* Don't use active slot count to generate BTN_TOOL events. */
+       input_mt_report_pointer_emulation(dev, false);
+
+       /* Send the number of fingers reported by touchpad itself. */
+       input_mt_report_finger_count(dev, mt_state->count);
+
+       synaptics_report_buttons(psmouse, sgm);
+
+       input_sync(dev);
+}
+
+/* Handle case where mt_state->count = 0 */
+static void synaptics_image_sensor_0f(struct synaptics_data *priv,
+                                     struct synaptics_mt_state *mt_state)
+{
+       synaptics_mt_state_set(mt_state, 0, -1, -1);
+       priv->mt_state_lost = false;
+}
+
+/* Handle case where mt_state->count = 1 */
+static void synaptics_image_sensor_1f(struct synaptics_data *priv,
+                                     struct synaptics_mt_state *mt_state)
+{
+       struct synaptics_hw_state *agm = &priv->agm;
+       struct synaptics_mt_state *old = &priv->mt_state;
+
+       /*
+        * If the last AGM was (0,0,0), and there is only one finger left,
+        * then we absolutely know that SGM contains slot 0, and all other
+        * fingers have been removed.
+        */
+       if (priv->agm_pending && agm->z == 0) {
+               synaptics_mt_state_set(mt_state, 1, 0, -1);
+               priv->mt_state_lost = false;
+               return;
+       }
+
+       switch (old->count) {
+       case 0:
+               synaptics_mt_state_set(mt_state, 1, 0, -1);
+               break;
+       case 1:
+               /*
+                * If mt_state_lost, then the previous transition was 3->1,
+                * and SGM now contains either slot 0 or 1, but we don't know
+                * which.  So, we just assume that the SGM now contains slot 1.
+                *
+                * If pending AGM and either:
+                *   (a) the previous SGM slot contains slot 0, or
+                *   (b) there was no SGM slot
+                * then, the SGM now contains slot 1
+                *
+                * Case (a) happens with very rapid "drum roll" gestures, where
+                * slot 0 finger is lifted and a new slot 1 finger touches
+                * within one reporting interval.
+                *
+                * Case (b) happens if initially two or more fingers tap
+                * briefly, and all but one lift before the end of the first
+                * reporting interval.
+                *
+                * (In both these cases, slot 0 will becomes empty, so SGM
+                * contains slot 1 with the new finger)
+                *
+                * Else, if there was no previous SGM, it now contains slot 0.
+                *
+                * Otherwise, SGM still contains the same slot.
+                */
+               if (priv->mt_state_lost ||
+                   (priv->agm_pending && old->sgm <= 0))
+                       synaptics_mt_state_set(mt_state, 1, 1, -1);
+               else if (old->sgm == -1)
+                       synaptics_mt_state_set(mt_state, 1, 0, -1);
+               break;
+       case 2:
+               /*
+                * If mt_state_lost, we don't know which finger SGM contains.
+                *
+                * So, report 1 finger, but with both slots empty.
+                * We will use slot 1 on subsequent 1->1
+                */
+               if (priv->mt_state_lost) {
+                       synaptics_mt_state_set(mt_state, 1, -1, -1);
+                       break;
+               }
+               /*
+                * Since the last AGM was NOT (0,0,0), it was the finger in
+                * slot 0 that has been removed.
+                * So, SGM now contains previous AGM's slot, and AGM is now
+                * empty.
+                */
+               synaptics_mt_state_set(mt_state, 1, old->agm, -1);
+               break;
+       case 3:
+               /*
+                * Since last AGM was not (0,0,0), we don't know which finger
+                * is left.
+                *
+                * So, report 1 finger, but with both slots empty.
+                * We will use slot 1 on subsequent 1->1
+                */
+               synaptics_mt_state_set(mt_state, 1, -1, -1);
+               priv->mt_state_lost = true;
+               break;
+       case 4:
+       case 5:
+               /* mt_state was updated by AGM-CONTACT packet */
+               break;
+       }
+}
+
+/* Handle case where mt_state->count = 2 */
+static void synaptics_image_sensor_2f(struct synaptics_data *priv,
+                                     struct synaptics_mt_state *mt_state)
+{
+       struct synaptics_mt_state *old = &priv->mt_state;
+
+       switch (old->count) {
+       case 0:
+               synaptics_mt_state_set(mt_state, 2, 0, 1);
+               break;
+       case 1:
+               /*
+                * If previous SGM contained slot 1 or higher, SGM now contains
+                * slot 0 (the newly touching finger) and AGM contains SGM's
+                * previous slot.
+                *
+                * Otherwise, SGM still contains slot 0 and AGM now contains
+                * slot 1.
+                */
+               if (old->sgm >= 1)
+                       synaptics_mt_state_set(mt_state, 2, 0, old->sgm);
+               else
+                       synaptics_mt_state_set(mt_state, 2, 0, 1);
+               break;
+       case 2:
+               /*
+                * If mt_state_lost, SGM now contains either finger 1 or 2, but
+                * we don't know which.
+                * So, we just assume that the SGM contains slot 0 and AGM 1.
+                */
+               if (priv->mt_state_lost)
+                       synaptics_mt_state_set(mt_state, 2, 0, 1);
+               /*
+                * Otherwise, use the same mt_state, since it either hasn't
+                * changed, or was updated by a recently received AGM-CONTACT
+                * packet.
+                */
+               break;
+       case 3:
+               /*
+                * 3->2 transitions have two unsolvable problems:
+                *  1) no indication is given which finger was removed
+                *  2) no way to tell if agm packet was for finger 3
+                *     before 3->2, or finger 2 after 3->2.
+                *
+                * So, report 2 fingers, but empty all slots.
+                * We will guess slots [0,1] on subsequent 2->2.
+                */
+               synaptics_mt_state_set(mt_state, 2, -1, -1);
+               priv->mt_state_lost = true;
+               break;
+       case 4:
+       case 5:
+               /* mt_state was updated by AGM-CONTACT packet */
+               break;
+       }
+}
+
+/* Handle case where mt_state->count = 3 */
+static void synaptics_image_sensor_3f(struct synaptics_data *priv,
+                                     struct synaptics_mt_state *mt_state)
+{
+       struct synaptics_mt_state *old = &priv->mt_state;
+
+       switch (old->count) {
+       case 0:
+               synaptics_mt_state_set(mt_state, 3, 0, 2);
+               break;
+       case 1:
+               /*
+                * If previous SGM contained slot 2 or higher, SGM now contains
+                * slot 0 (one of the newly touching fingers) and AGM contains
+                * SGM's previous slot.
+                *
+                * Otherwise, SGM now contains slot 0 and AGM contains slot 2.
+                */
+               if (old->sgm >= 2)
+                       synaptics_mt_state_set(mt_state, 3, 0, old->sgm);
+               else
+                       synaptics_mt_state_set(mt_state, 3, 0, 2);
+               break;
+       case 2:
+               /*
+                * If the AGM previously contained slot 3 or higher, then the
+                * newly touching finger is in the lowest available slot.
+                *
+                * If SGM was previously 1 or higher, then the new SGM is
+                * now slot 0 (with a new finger), otherwise, the new finger
+                * is now in a hidden slot between 0 and AGM's slot.
+                *
+                * In all such cases, the SGM now contains slot 0, and the AGM
+                * continues to contain the same slot as before.
+                */
+               if (old->agm >= 3) {
+                       synaptics_mt_state_set(mt_state, 3, 0, old->agm);
+                       break;
+               }
+
+               /*
+                * After some 3->1 and all 3->2 transitions, we lose track
+                * of which slot is reported by SGM and AGM.
+                *
+                * For 2->3 in this state, report 3 fingers, but empty all
+                * slots, and we will guess (0,2) on a subsequent 0->3.
+                *
+                * To userspace, the resulting transition will look like:
+                *    2:[0,1] -> 3:[-1,-1] -> 3:[0,2]
+                */
+               if (priv->mt_state_lost) {
+                       synaptics_mt_state_set(mt_state, 3, -1, -1);
+                       break;
+               }
+
+               /*
+                * If the (SGM,AGM) really previously contained slots (0, 1),
+                * then we cannot know what slot was just reported by the AGM,
+                * because the 2->3 transition can occur either before or after
+                * the AGM packet. Thus, this most recent AGM could contain
+                * either the same old slot 1 or the new slot 2.
+                * Subsequent AGMs will be reporting slot 2.
+                *
+                * To userspace, the resulting transition will look like:
+                *    2:[0,1] -> 3:[0,-1] -> 3:[0,2]
+                */
+               synaptics_mt_state_set(mt_state, 3, 0, -1);
+               break;
+       case 3:
+               /*
+                * If, for whatever reason, the previous agm was invalid,
+                * Assume SGM now contains slot 0, AGM now contains slot 2.
+                */
+               if (old->agm <= 2)
+                       synaptics_mt_state_set(mt_state, 3, 0, 2);
+               /*
+                * mt_state either hasn't changed, or was updated by a recently
+                * received AGM-CONTACT packet.
+                */
+               break;
+
+       case 4:
+       case 5:
+               /* mt_state was updated by AGM-CONTACT packet */
+               break;
+       }
+}
+
+/* Handle case where mt_state->count = 4, or = 5 */
+static void synaptics_image_sensor_45f(struct synaptics_data *priv,
+                                      struct synaptics_mt_state *mt_state)
+{
+       /* mt_state was updated correctly by AGM-CONTACT packet */
+       priv->mt_state_lost = false;
+}
+
+static void synaptics_image_sensor_process(struct psmouse *psmouse,
+                                          struct synaptics_hw_state *sgm)
+{
+       struct synaptics_data *priv = psmouse->private;
+       struct synaptics_hw_state *agm = &priv->agm;
+       struct synaptics_mt_state mt_state;
+
+       /* Initialize using current mt_state (as updated by last agm) */
+       mt_state = agm->mt_state;
+
+       /*
+        * Update mt_state using the new finger count and current mt_state.
+        */
+       if (sgm->z == 0)
+               synaptics_image_sensor_0f(priv, &mt_state);
+       else if (sgm->w >= 4)
+               synaptics_image_sensor_1f(priv, &mt_state);
+       else if (sgm->w == 0)
+               synaptics_image_sensor_2f(priv, &mt_state);
+       else if (sgm->w == 1 && mt_state.count <= 3)
+               synaptics_image_sensor_3f(priv, &mt_state);
+       else
+               synaptics_image_sensor_45f(priv, &mt_state);
+
+       /* Send resulting input events to user space */
+       synaptics_report_mt_data(psmouse, &mt_state, sgm);
+
+       /* Store updated mt_state */
+       priv->mt_state = agm->mt_state = mt_state;
+       priv->agm_pending = false;
+}
+
 /*
  *  called for each full received packet from the touchpad
  */
@@ -536,11 +965,15 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        struct synaptics_hw_state hw;
        int num_fingers;
        int finger_width;
-       int i;
 
        if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
                return;
 
+       if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+               synaptics_image_sensor_process(psmouse, &hw);
+               return;
+       }
+
        if (hw.scroll) {
                priv->scroll += hw.scroll;
 
@@ -586,7 +1019,8 @@ static void synaptics_process_packet(struct psmouse *psmouse)
        }
 
        if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
-               synaptics_report_semi_mt_data(dev, &hw, &priv->mt, num_fingers);
+               synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
+                                             num_fingers);
 
        /* Post events
         * BTN_TOUCH has to be first as mousedev relies on it when doing
@@ -597,7 +1031,7 @@ static void synaptics_process_packet(struct psmouse *psmouse)
 
        if (num_fingers > 0) {
                input_report_abs(dev, ABS_X, hw.x);
-               input_report_abs(dev, ABS_Y, YMAX_NOMINAL + YMIN_NOMINAL - hw.y);
+               input_report_abs(dev, ABS_Y, synaptics_invert_y(hw.y));
        }
        input_report_abs(dev, ABS_PRESSURE, hw.z);
 
@@ -605,24 +1039,12 @@ static void synaptics_process_packet(struct psmouse *psmouse)
                input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
 
        input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
-       input_report_key(dev, BTN_LEFT, hw.left);
-       input_report_key(dev, BTN_RIGHT, hw.right);
-
        if (SYN_CAP_MULTIFINGER(priv->capabilities)) {
                input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
                input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
        }
 
-       if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities))
-               input_report_key(dev, BTN_MIDDLE, hw.middle);
-
-       if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) {
-               input_report_key(dev, BTN_FORWARD, hw.up);
-               input_report_key(dev, BTN_BACK, hw.down);
-       }
-
-       for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++)
-               input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i));
+       synaptics_report_buttons(psmouse, &hw);
 
        input_sync(dev);
 }
@@ -694,39 +1116,49 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
 /*****************************************************************************
  *     Driver initialization/cleanup functions
  ****************************************************************************/
-static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+static void set_abs_position_params(struct input_dev *dev,
+                                   struct synaptics_data *priv, int x_code,
+                                   int y_code)
 {
-       int i;
+       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) ?
                        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);
+}
+
+static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
+{
+       int i;
+
        __set_bit(INPUT_PROP_POINTER, dev->propbit);
 
        __set_bit(EV_ABS, dev->evbit);
-       input_set_abs_params(dev, ABS_X,
-                            priv->x_min ?: XMIN_NOMINAL,
-                            priv->x_max ?: XMAX_NOMINAL,
-                            fuzz, 0);
-       input_set_abs_params(dev, ABS_Y,
-                            priv->y_min ?: YMIN_NOMINAL,
-                            priv->y_max ?: YMAX_NOMINAL,
-                            fuzz, 0);
+       set_abs_position_params(dev, priv, ABS_X, ABS_Y);
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
 
-       if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+       if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+               input_mt_init_slots(dev, 2);
+               set_abs_position_params(dev, priv, 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);
+
+               /* 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)) {
+               /* Non-image sensors with AGM use semi-mt */
                __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
                input_mt_init_slots(dev, 2);
-               input_set_abs_params(dev, ABS_MT_POSITION_X,
-                                    priv->x_min ?: XMIN_NOMINAL,
-                                    priv->x_max ?: XMAX_NOMINAL,
-                                    fuzz, 0);
-               input_set_abs_params(dev, ABS_MT_POSITION_Y,
-                                    priv->y_min ?: YMIN_NOMINAL,
-                                    priv->y_max ?: YMAX_NOMINAL,
-                                    fuzz, 0);
-
-               input_abs_set_res(dev, ABS_MT_POSITION_X, priv->x_res);
-               input_abs_set_res(dev, ABS_MT_POSITION_Y, priv->y_res);
+               set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+                                       ABS_MT_POSITION_Y);
        }
 
        if (SYN_CAP_PALMDETECT(priv->capabilities))
@@ -759,9 +1191,6 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        __clear_bit(REL_X, dev->relbit);
        __clear_bit(REL_Y, dev->relbit);
 
-       input_abs_set_res(dev, ABS_X, priv->x_res);
-       input_abs_set_res(dev, ABS_Y, priv->y_res);
-
        if (SYN_CAP_CLICKPAD(priv->ext_cap_0c)) {
                __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
                /* Clickpads report only left button */
index ca040aa..622aea8 100644 (file)
@@ -74,6 +74,8 @@
  * 2   0x04    reduced filtering       firmware does less filtering on
  *                                     position data, driver should watch
  *                                     for noise.
+ * 2   0x08    image sensor            image sensor tracks 5 fingers, but only
+ *                                     reports 2.
  * 2   0x20    report min              query 0x0f gives min coord reported
  */
 #define SYN_CAP_CLICKPAD(ex0c)         ((ex0c) & 0x100000) /* 1-button ClickPad */
@@ -82,6 +84,7 @@
 #define SYN_CAP_MIN_DIMENSIONS(ex0c)   ((ex0c) & 0x002000)
 #define SYN_CAP_ADV_GESTURE(ex0c)      ((ex0c) & 0x080000)
 #define SYN_CAP_REDUCED_FILTERING(ex0c)        ((ex0c) & 0x000400)
+#define SYN_CAP_IMAGE_SENSOR(ex0c)     ((ex0c) & 0x000800)
 
 /* synaptics modes query bits */
 #define SYN_MODE_ABSOLUTE(m)           ((m) & (1 << 7))
 #define SYN_REDUCED_FILTER_FUZZ                8
 
 /*
- * A structure to describe the state of the touchpad hardware (buttons and pad)
+ * A structure to describe which internal touchpad finger slots are being
+ * reported in raw packets.
  */
+struct synaptics_mt_state {
+       int count;                      /* num fingers being tracked */
+       int sgm;                        /* which slot is reported by sgm pkt */
+       int agm;                        /* which slot is reported by agm pkt*/
+};
 
+/*
+ * A structure to describe the state of the touchpad hardware (buttons and pad)
+ */
 struct synaptics_hw_state {
        int x;
        int y;
@@ -127,6 +139,9 @@ struct synaptics_hw_state {
        unsigned int down:1;
        unsigned char ext_buttons;
        signed char scroll;
+
+       /* As reported in last AGM-CONTACT packets */
+       struct synaptics_mt_state mt_state;
 };
 
 struct synaptics_data {
@@ -146,7 +161,15 @@ struct synaptics_data {
 
        struct serio *pt_port;                  /* Pass-through serio port */
 
-       struct synaptics_hw_state mt;           /* current gesture packet */
+       struct synaptics_mt_state mt_state;     /* Current mt finger state */
+       bool mt_state_lost;                     /* mt_state may be incorrect */
+
+       /*
+        * Last received Advanced Gesture Mode (AGM) packet. An AGM packet
+        * contains position data for a second contact, at half resolution.
+        */
+       struct synaptics_hw_state agm;
+       bool agm_pending;                       /* new AGM packet received */
 };
 
 void synaptics_module_init(void);
index fadc115..0acca68 100644 (file)
@@ -66,7 +66,6 @@ struct ts_event {
 struct tsc2007 {
        struct input_dev        *input;
        char                    phys[32];
-       struct delayed_work     work;
 
        struct i2c_client       *client;
 
@@ -76,9 +75,11 @@ struct tsc2007 {
        unsigned long           poll_delay;
        unsigned long           poll_period;
 
-       bool                    pendown;
        int                     irq;
 
+       wait_queue_head_t       wait;
+       bool                    stopped;
+
        int                     (*get_pendown_state)(void);
        void                    (*clear_penirq)(void);
 };
@@ -141,25 +142,8 @@ static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)
        return rt;
 }
 
-static void tsc2007_send_up_event(struct tsc2007 *tsc)
+static bool tsc2007_is_pen_down(struct tsc2007 *ts)
 {
-       struct input_dev *input = tsc->input;
-
-       dev_dbg(&tsc->client->dev, "UP\n");
-
-       input_report_key(input, BTN_TOUCH, 0);
-       input_report_abs(input, ABS_PRESSURE, 0);
-       input_sync(input);
-}
-
-static void tsc2007_work(struct work_struct *work)
-{
-       struct tsc2007 *ts =
-               container_of(to_delayed_work(work), struct tsc2007, work);
-       bool debounced = false;
-       struct ts_event tc;
-       u32 rt;
-
        /*
         * NOTE: We can't rely on the pressure to determine the pen down
         * state, even though this controller has a pressure sensor.
@@ -170,79 +154,82 @@ static void tsc2007_work(struct work_struct *work)
         * The only safe way to check for the pen up condition is in the
         * work function by reading the pen signal state (it's a GPIO
         * and IRQ). Unfortunately such callback is not always available,
-        * in that case we have rely on the pressure anyway.
+        * in that case we assume that the pen is down and expect caller
+        * to fall back on the pressure reading.
         */
-       if (ts->get_pendown_state) {
-               if (unlikely(!ts->get_pendown_state())) {
-                       tsc2007_send_up_event(ts);
-                       ts->pendown = false;
-                       goto out;
-               }
 
-               dev_dbg(&ts->client->dev, "pen is still down\n");
-       }
+       if (!ts->get_pendown_state)
+               return true;
 
-       tsc2007_read_values(ts, &tc);
+       return ts->get_pendown_state();
+}
 
-       rt = tsc2007_calculate_pressure(ts, &tc);
-       if (rt > ts->max_rt) {
-               /*
-                * Sample found inconsistent by debouncing or pressure is
-                * beyond the maximum. Don't report it to user space,
-                * repeat at least once more the measurement.
-                */
-               dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
-               debounced = true;
-               goto out;
+static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
+{
+       struct tsc2007 *ts = handle;
+       struct input_dev *input = ts->input;
+       struct ts_event tc;
+       u32 rt;
 
-       }
+       while (!ts->stopped && tsc2007_is_pen_down(ts)) {
+
+               /* pen is down, continue with the measurement */
+               tsc2007_read_values(ts, &tc);
+
+               rt = tsc2007_calculate_pressure(ts, &tc);
 
-       if (rt) {
-               struct input_dev *input = ts->input;
+               if (rt == 0 && !ts->get_pendown_state) {
+                       /*
+                        * If pressure reported is 0 and we don't have
+                        * callback to check pendown state, we have to
+                        * assume that pen was lifted up.
+                        */
+                       break;
+               }
 
-               if (!ts->pendown) {
-                       dev_dbg(&ts->client->dev, "DOWN\n");
+               if (rt <= ts->max_rt) {
+                       dev_dbg(&ts->client->dev,
+                               "DOWN point(%4d,%4d), pressure (%4u)\n",
+                               tc.x, tc.y, rt);
 
                        input_report_key(input, BTN_TOUCH, 1);
-                       ts->pendown = true;
+                       input_report_abs(input, ABS_X, tc.x);
+                       input_report_abs(input, ABS_Y, tc.y);
+                       input_report_abs(input, ABS_PRESSURE, rt);
+
+                       input_sync(input);
+
+               } else {
+                       /*
+                        * Sample found inconsistent by debouncing or pressure is
+                        * beyond the maximum. Don't report it to user space,
+                        * repeat at least once more the measurement.
+                        */
+                       dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
                }
 
-               input_report_abs(input, ABS_X, tc.x);
-               input_report_abs(input, ABS_Y, tc.y);
-               input_report_abs(input, ABS_PRESSURE, rt);
+               wait_event_timeout(ts->wait, ts->stopped,
+                                  msecs_to_jiffies(ts->poll_period));
+       }
 
-               input_sync(input);
+       dev_dbg(&ts->client->dev, "UP\n");
 
-               dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",
-                       tc.x, tc.y, rt);
+       input_report_key(input, BTN_TOUCH, 0);
+       input_report_abs(input, ABS_PRESSURE, 0);
+       input_sync(input);
 
-       } else if (!ts->get_pendown_state && ts->pendown) {
-               /*
-                * We don't have callback to check pendown state, so we
-                * have to assume that since pressure reported is 0 the
-                * pen was lifted up.
-                */
-               tsc2007_send_up_event(ts);
-               ts->pendown = false;
-       }
+       if (ts->clear_penirq)
+               ts->clear_penirq();
 
- out:
-       if (ts->pendown || debounced)
-               schedule_delayed_work(&ts->work,
-                                     msecs_to_jiffies(ts->poll_period));
-       else
-               enable_irq(ts->irq);
+       return IRQ_HANDLED;
 }
 
-static irqreturn_t tsc2007_irq(int irq, void *handle)
+static irqreturn_t tsc2007_hard_irq(int irq, void *handle)
 {
        struct tsc2007 *ts = handle;
 
-       if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {
-               disable_irq_nosync(ts->irq);
-               schedule_delayed_work(&ts->work,
-                                     msecs_to_jiffies(ts->poll_delay));
-       }
+       if (!ts->get_pendown_state || likely(ts->get_pendown_state()))
+               return IRQ_WAKE_THREAD;
 
        if (ts->clear_penirq)
                ts->clear_penirq();
@@ -250,17 +237,40 @@ static irqreturn_t tsc2007_irq(int irq, void *handle)
        return IRQ_HANDLED;
 }
 
-static void tsc2007_free_irq(struct tsc2007 *ts)
+static void tsc2007_stop(struct tsc2007 *ts)
 {
-       free_irq(ts->irq, ts);
-       if (cancel_delayed_work_sync(&ts->work)) {
-               /*
-                * Work was pending, therefore we need to enable
-                * IRQ here to balance the disable_irq() done in the
-                * interrupt handler.
-                */
-               enable_irq(ts->irq);
+       ts->stopped = true;
+       mb();
+       wake_up(&ts->wait);
+
+       disable_irq(ts->irq);
+}
+
+static int tsc2007_open(struct input_dev *input_dev)
+{
+       struct tsc2007 *ts = input_get_drvdata(input_dev);
+       int err;
+
+       ts->stopped = false;
+       mb();
+
+       enable_irq(ts->irq);
+
+       /* Prepare for touch readings - power down ADC and enable PENIRQ */
+       err = tsc2007_xfer(ts, PWRDOWN);
+       if (err < 0) {
+               tsc2007_stop(ts);
+               return err;
        }
+
+       return 0;
+}
+
+static void tsc2007_close(struct input_dev *input_dev)
+{
+       struct tsc2007 *ts = input_get_drvdata(input_dev);
+
+       tsc2007_stop(ts);
 }
 
 static int __devinit tsc2007_probe(struct i2c_client *client,
@@ -290,7 +300,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
        ts->client = client;
        ts->irq = client->irq;
        ts->input = input_dev;
-       INIT_DELAYED_WORK(&ts->work, tsc2007_work);
+       init_waitqueue_head(&ts->wait);
 
        ts->model             = pdata->model;
        ts->x_plate_ohms      = pdata->x_plate_ohms;
@@ -307,6 +317,11 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
        input_dev->phys = ts->phys;
        input_dev->id.bustype = BUS_I2C;
 
+       input_dev->open = tsc2007_open;
+       input_dev->close = tsc2007_close;
+
+       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);
 
@@ -318,17 +333,14 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
        if (pdata->init_platform_hw)
                pdata->init_platform_hw();
 
-       err = request_irq(ts->irq, tsc2007_irq, 0,
-                       client->dev.driver->name, ts);
+       err = request_threaded_irq(ts->irq, tsc2007_hard_irq, tsc2007_soft_irq,
+                                  IRQF_ONESHOT, client->dev.driver->name, ts);
        if (err < 0) {
                dev_err(&client->dev, "irq %d busy?\n", ts->irq);
                goto err_free_mem;
        }
 
-       /* Prepare for touch readings - power down ADC and enable PENIRQ */
-       err = tsc2007_xfer(ts, PWRDOWN);
-       if (err < 0)
-               goto err_free_irq;
+       tsc2007_stop(ts);
 
        err = input_register_device(input_dev);
        if (err)
@@ -339,7 +351,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
        return 0;
 
  err_free_irq:
-       tsc2007_free_irq(ts);
+       free_irq(ts->irq, ts);
        if (pdata->exit_platform_hw)
                pdata->exit_platform_hw();
  err_free_mem:
@@ -353,7 +365,7 @@ static int __devexit tsc2007_remove(struct i2c_client *client)
        struct tsc2007  *ts = i2c_get_clientdata(client);
        struct tsc2007_platform_data *pdata = client->dev.platform_data;
 
-       tsc2007_free_irq(ts);
+       free_irq(ts->irq, ts);
 
        if (pdata->exit_platform_hw)
                pdata->exit_platform_hw();
index c14412e..1f42d91 100644 (file)
@@ -367,6 +367,20 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,
        return rc;
 }
 
+static int w8001_open(struct input_dev *dev)
+{
+       struct w8001 *w8001 = input_get_drvdata(dev);
+
+       return w8001_command(w8001, W8001_CMD_START, false);
+}
+
+static void w8001_close(struct input_dev *dev)
+{
+       struct w8001 *w8001 = input_get_drvdata(dev);
+
+       w8001_command(w8001, W8001_CMD_STOP, false);
+}
+
 static int w8001_setup(struct w8001 *w8001)
 {
        struct input_dev *dev = w8001->dev;
@@ -474,7 +488,7 @@ static int w8001_setup(struct w8001 *w8001)
 
        strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
 
-       return w8001_command(w8001, W8001_CMD_START, false);
+       return 0;
 }
 
 /*
@@ -485,12 +499,12 @@ static void w8001_disconnect(struct serio *serio)
 {
        struct w8001 *w8001 = serio_get_drvdata(serio);
 
-       input_get_device(w8001->dev);
-       input_unregister_device(w8001->dev);
        serio_close(serio);
-       serio_set_drvdata(serio, NULL);
-       input_put_device(w8001->dev);
+
+       input_unregister_device(w8001->dev);
        kfree(w8001);
+
+       serio_set_drvdata(serio, NULL);
 }
 
 /*
@@ -534,6 +548,11 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
        input_dev->id.version = 0x0100;
        input_dev->dev.parent = &serio->dev;
 
+       input_dev->open = w8001_open;
+       input_dev->close = w8001_close;
+
+       input_set_drvdata(input_dev, w8001);
+
        err = input_register_device(w8001->dev);
        if (err)
                goto fail3;
diff --git a/include/linux/bma150.h b/include/linux/bma150.h
new file mode 100644 (file)
index 0000000..7911fda
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2011 Bosch Sensortec GmbH
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _BMA150_H_
+#define _BMA150_H_
+
+#define BMA150_DRIVER          "bma150"
+
+struct bma150_cfg {
+       bool any_motion_int;            /* Set to enable any-motion interrupt */
+       bool hg_int;                    /* Set to enable high-G interrupt */
+       bool lg_int;                    /* Set to enable low-G interrupt */
+       unsigned char any_motion_dur;   /* Any-motion duration */
+       unsigned char any_motion_thres; /* Any-motion threshold */
+       unsigned char hg_hyst;          /* High-G hysterisis */
+       unsigned char hg_dur;           /* High-G duration */
+       unsigned char hg_thres;         /* High-G threshold */
+       unsigned char lg_hyst;          /* Low-G hysterisis */
+       unsigned char lg_dur;           /* Low-G duration */
+       unsigned char lg_thres;         /* Low-G threshold */
+       unsigned char range;            /* BMA0150_RANGE_xxx (in G) */
+       unsigned char bandwidth;        /* BMA0150_BW_xxx (in Hz) */
+};
+
+struct bma150_platform_data {
+       struct bma150_cfg cfg;
+       int (*irq_gpio_cfg)(void);
+};
+
+#endif /* _BMA150_H_ */
index a637e78..57add32 100644 (file)
@@ -505,6 +505,7 @@ struct input_keymap_entry {
 #define BTN_TOOL_FINGER                0x145
 #define BTN_TOOL_MOUSE         0x146
 #define BTN_TOOL_LENS          0x147
+#define BTN_TOOL_QUINTTAP      0x148   /* Five fingers on trackpad */
 #define BTN_TOUCH              0x14a
 #define BTN_STYLUS             0x14b
 #define BTN_STYLUS2            0x14c