Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 25 Oct 2010 14:59:01 +0000 (07:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 25 Oct 2010 14:59:01 +0000 (07:59 -0700)
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (75 commits)
  Input: wacom - specify Cinitq supported tools
  Input: ab8500-ponkey - fix IRQ freeing in error path
  Input: adp5588-keys - use more obvious i2c_device_id name string
  Input: ad7877 - switch to using threaded IRQ
  Input: ad7877 - use attribute group to control visibility of attributes
  Input: serio - add support for PS2Mult multiplexer protocol
  Input: wacom - properly enable runtime PM
  Input: ad7877 - filter events where pressure is beyond the maximum
  Input: ad7877 - implement EV_KEY:BTN_TOUCH reporting
  Input: ad7877 - implement specified chip select behavior
  Input: hp680_ts_input - use cancel_delayed_work_sync()
  Input: mousedev - correct lockdep annotation
  Input: ads7846 - switch to using threaded IRQ
  Input: serio - support multiple child devices per single parent
  Input: synaptics - simplify pass-through port handling
  Input: add ROHM BU21013 touch panel controller support
  Input: omap4-keypad - wake-up on events & long presses
  Input: omap4-keypad - fix interrupt line configuration
  Input: omap4-keypad - SYSCONFIG register configuration
  Input: omap4-keypad - use platform device helpers
  ...

75 files changed:
arch/arm/mach-ux500/board-mop500.c
arch/arm/mach-ux500/clock.c
arch/arm/mach-ux500/devices-db8500.c
arch/arm/mach-ux500/include/mach/devices.h
arch/arm/mach-ux500/pins-db8500.h
arch/arm/plat-nomadik/include/plat/ske.h [new file with mode: 0644]
arch/arm/plat-omap/include/plat/omap4-keypad.h [new file with mode: 0644]
drivers/char/keyboard.c
drivers/char/sysrq.c
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/input/evdev.c
drivers/input/gameport/emu10k1-gp.c
drivers/input/gameport/fm801-gp.c
drivers/input/input.c
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/adp5588-keys.c
drivers/input/keyboard/hil_kbd.c
drivers/input/keyboard/nomadik-ske-keypad.c [new file with mode: 0644]
drivers/input/keyboard/omap4-keypad.c [new file with mode: 0644]
drivers/input/keyboard/twl4030_keypad.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ab8500-ponkey.c [new file with mode: 0644]
drivers/input/misc/ati_remote2.c
drivers/input/misc/powermate.c
drivers/input/mouse/elantech.c
drivers/input/mouse/psmouse-base.c
drivers/input/mouse/synaptics.c
drivers/input/mouse/synaptics.h
drivers/input/mouse/trackpoint.c
drivers/input/mousedev.c
drivers/input/serio/Kconfig
drivers/input/serio/Makefile
drivers/input/serio/i8042.c
drivers/input/serio/ps2mult.c [new file with mode: 0644]
drivers/input/serio/serio.c
drivers/input/sparse-keymap.c
drivers/input/tablet/Kconfig
drivers/input/tablet/Makefile
drivers/input/tablet/hanwang.c [new file with mode: 0644]
drivers/input/tablet/wacom.h
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/tablet/wacom_wac.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7877.c
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/bu21013_ts.c [new file with mode: 0644]
drivers/input/touchscreen/cy8ctmg110_ts.c
drivers/input/touchscreen/hp680_ts_input.c
drivers/input/touchscreen/intel-mid-touch.c [new file with mode: 0644]
drivers/input/touchscreen/lpc32xx_ts.c [new file with mode: 0644]
drivers/input/touchscreen/s3c2410_ts.c
drivers/input/touchscreen/stmpe-ts.c
drivers/input/touchscreen/tps6507x-ts.c
drivers/input/touchscreen/tsc2007.c
drivers/input/touchscreen/wacom_w8001.c
drivers/input/touchscreen/wm97xx-core.c
drivers/media/IR/ir-keytable.c
drivers/mfd/ab8500-core.c
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/mrst-touchscreen/Kconfig [deleted file]
drivers/staging/mrst-touchscreen/Makefile [deleted file]
drivers/staging/mrst-touchscreen/TODO [deleted file]
drivers/staging/mrst-touchscreen/intel-mid-touch.c [deleted file]
include/linux/gameport.h
include/linux/input.h
include/linux/input/bu21013.h [new file with mode: 0644]
include/linux/serio.h
include/media/rc-map.h

index fcb587f825ccf6c7b195277365e29e5aa58d2710..cac83a694880f6dd6d17a62d39387cbf58128144 100644 (file)
 #include <linux/amba/pl022.h>
 #include <linux/spi/spi.h>
 #include <linux/mfd/ab8500.h>
+#include <linux/input/matrix_keypad.h>
 
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 
 #include <plat/pincfg.h>
 #include <plat/i2c.h>
+#include <plat/ske.h>
 
 #include <mach/hardware.h>
 #include <mach/setup.h>
@@ -49,6 +51,24 @@ static pin_cfg_t mop500_pins[] = {
        GPIO11_I2C2_SCL,
        GPIO229_I2C3_SDA,
        GPIO230_I2C3_SCL,
+
+       /* SKE keypad */
+       GPIO153_KP_I7,
+       GPIO154_KP_I6,
+       GPIO155_KP_I5,
+       GPIO156_KP_I4,
+       GPIO157_KP_O7,
+       GPIO158_KP_O6,
+       GPIO159_KP_O5,
+       GPIO160_KP_O4,
+       GPIO161_KP_I3,
+       GPIO162_KP_I2,
+       GPIO163_KP_I1,
+       GPIO164_KP_I0,
+       GPIO165_KP_O3,
+       GPIO166_KP_O2,
+       GPIO167_KP_O1,
+       GPIO168_KP_O0,
 };
 
 static void ab4500_spi_cs_control(u32 command)
@@ -148,12 +168,120 @@ static struct amba_device *amba_devs[] __initdata = {
        &u8500_ssp0_device,
 };
 
+static const unsigned int ux500_keymap[] = {
+       KEY(2, 5, KEY_END),
+       KEY(4, 1, KEY_POWER),
+       KEY(3, 5, KEY_VOLUMEDOWN),
+       KEY(1, 3, KEY_3),
+       KEY(5, 2, KEY_RIGHT),
+       KEY(5, 0, KEY_9),
+
+       KEY(0, 5, KEY_MENU),
+       KEY(7, 6, KEY_ENTER),
+       KEY(4, 5, KEY_0),
+       KEY(6, 7, KEY_2),
+       KEY(3, 4, KEY_UP),
+       KEY(3, 3, KEY_DOWN),
+
+       KEY(6, 4, KEY_SEND),
+       KEY(6, 2, KEY_BACK),
+       KEY(4, 2, KEY_VOLUMEUP),
+       KEY(5, 5, KEY_1),
+       KEY(4, 3, KEY_LEFT),
+       KEY(3, 2, KEY_7),
+};
+
+static const struct matrix_keymap_data ux500_keymap_data = {
+       .keymap         = ux500_keymap,
+       .keymap_size    = ARRAY_SIZE(ux500_keymap),
+};
+
+/*
+ * Nomadik SKE keypad
+ */
+#define ROW_PIN_I0      164
+#define ROW_PIN_I1      163
+#define ROW_PIN_I2      162
+#define ROW_PIN_I3      161
+#define ROW_PIN_I4      156
+#define ROW_PIN_I5      155
+#define ROW_PIN_I6      154
+#define ROW_PIN_I7      153
+#define COL_PIN_O0      168
+#define COL_PIN_O1      167
+#define COL_PIN_O2      166
+#define COL_PIN_O3      165
+#define COL_PIN_O4      160
+#define COL_PIN_O5      159
+#define COL_PIN_O6      158
+#define COL_PIN_O7      157
+
+#define SKE_KPD_MAX_ROWS        8
+#define SKE_KPD_MAX_COLS        8
+
+static int ske_kp_rows[] = {
+       ROW_PIN_I0, ROW_PIN_I1, ROW_PIN_I2, ROW_PIN_I3,
+       ROW_PIN_I4, ROW_PIN_I5, ROW_PIN_I6, ROW_PIN_I7,
+};
+
+/*
+ * ske_set_gpio_row: request and set gpio rows
+ */
+static int ske_set_gpio_row(int gpio)
+{
+       int ret;
+
+       ret = gpio_request(gpio, "ske-kp");
+       if (ret < 0) {
+               pr_err("ske_set_gpio_row: gpio request failed\n");
+               return ret;
+       }
+
+       ret = gpio_direction_output(gpio, 1);
+       if (ret < 0) {
+               pr_err("ske_set_gpio_row: gpio direction failed\n");
+               gpio_free(gpio);
+       }
+
+       return ret;
+}
+
+/*
+ * ske_kp_init - enable the gpio configuration
+ */
+static int ske_kp_init(void)
+{
+       int ret, i;
+
+       for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
+               ret = ske_set_gpio_row(ske_kp_rows[i]);
+               if (ret < 0) {
+                       pr_err("ske_kp_init: failed init\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static struct ske_keypad_platform_data ske_keypad_board = {
+       .init           = ske_kp_init,
+       .keymap_data    = &ux500_keymap_data,
+       .no_autorepeat  = true,
+       .krow           = SKE_KPD_MAX_ROWS,     /* 8x8 matrix */
+       .kcol           = SKE_KPD_MAX_COLS,
+       .debounce_ms    = 40,                   /* in millsecs */
+};
+
+
+
 /* add any platform devices here - TODO */
 static struct platform_device *platform_devs[] __initdata = {
        &u8500_i2c0_device,
        &ux500_i2c1_device,
        &ux500_i2c2_device,
        &ux500_i2c3_device,
+       &ux500_ske_keypad_device,
 };
 
 static void __init u8500_init_machine(void)
@@ -168,6 +296,7 @@ static void __init u8500_init_machine(void)
        ux500_i2c1_device.dev.platform_data = &u8500_i2c1_data;
        ux500_i2c2_device.dev.platform_data = &u8500_i2c2_data;
        ux500_i2c3_device.dev.platform_data = &u8500_i2c3_data;
+       ux500_ske_keypad_device.dev.platform_data = &ske_keypad_board;
 
        u8500_ssp0_device.dev.platform_data = &ssp0_platform_data;
 
index d8ab7f184fe439ec2492079a063def04c5e3c690..1675047daf20f74f5f00a1a7e88feab2e5c4f281 100644 (file)
@@ -477,6 +477,7 @@ static struct clk_lookup u8500_common_clks[] = {
        CLK(sdi5,       "sdi5",         NULL),
        CLK(uart2,      "uart2",        NULL),
        CLK(ske,        "ske",          NULL),
+       CLK(ske,        "nmk-ske-keypad",       NULL),
        CLK(sdi2,       "sdi2",         NULL),
        CLK(i2c0,       "nmk-i2c.0",    NULL),
        CLK(fsmc,       "fsmc",         NULL),
index 40032fecbc165538c9822fac801d7e72780d4c3b..cbbe69a76a7ccf773c4ea408bd1f5d268bbf6fbf 100644 (file)
@@ -292,3 +292,23 @@ void dma40_u8500ed_fixup(void)
        dma40_resources[1].start = U8500_DMA_LCPA_BASE_ED;
        dma40_resources[1].end = U8500_DMA_LCPA_BASE_ED + 2 * SZ_1K - 1;
 }
+
+struct resource keypad_resources[] = {
+       [0] = {
+               .start = U8500_SKE_BASE,
+               .end = U8500_SKE_BASE + SZ_4K - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = IRQ_DB8500_KB,
+               .end = IRQ_DB8500_KB,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+struct platform_device ux500_ske_keypad_device = {
+       .name = "nmk-ske-keypad",
+       .id = -1,
+       .num_resources = ARRAY_SIZE(keypad_resources),
+       .resource = keypad_resources,
+};
index 33a120c2e82eaf0749e480149cdd2539c671aab8..b91a4d1211a27997141a38055151c45e19698078 100644 (file)
@@ -26,6 +26,7 @@ extern struct platform_device ux500_i2c3_device;
 extern struct platform_device u8500_i2c0_device;
 extern struct platform_device u8500_i2c4_device;
 extern struct platform_device u8500_dma40_device;
+extern struct platform_device ux500_ske_keypad_device;
 
 extern struct amba_device u8500_sdi0_device;
 extern struct amba_device u8500_sdi1_device;
index 66f8761cc823cfd5a3670c773da50b087822bb94..f923764ee16cb09706d3e0ecbfe889b760897847 100644 (file)
 #define GPIO152_KP_O9          PIN_CFG(152, ALT_C)
 
 #define GPIO153_GPIO           PIN_CFG(153, GPIO)
-#define GPIO153_KP_I7          PIN_CFG(153, ALT_A)
+#define GPIO153_KP_I7          PIN_CFG_PULL(153, ALT_A, DOWN)
 #define GPIO153_LCD_D24                PIN_CFG(153, ALT_B)
 #define GPIO153_U2_RXD         PIN_CFG(153, ALT_C)
 
 #define GPIO154_GPIO           PIN_CFG(154, GPIO)
-#define GPIO154_KP_I6          PIN_CFG(154, ALT_A)
+#define GPIO154_KP_I6          PIN_CFG_PULL(154, ALT_A, DOWN)
 #define GPIO154_LCD_D25                PIN_CFG(154, ALT_B)
 #define GPIO154_U2_TXD         PIN_CFG(154, ALT_C)
 
 #define GPIO155_GPIO           PIN_CFG(155, GPIO)
-#define GPIO155_KP_I5          PIN_CFG(155, ALT_A)
+#define GPIO155_KP_I5          PIN_CFG_PULL(155, ALT_A, DOWN)
 #define GPIO155_LCD_D26                PIN_CFG(155, ALT_B)
 #define GPIO155_STMAPE_CLK     PIN_CFG(155, ALT_C)
 
 #define GPIO156_GPIO           PIN_CFG(156, GPIO)
-#define GPIO156_KP_I4          PIN_CFG(156, ALT_A)
+#define GPIO156_KP_I4          PIN_CFG_PULL(156, ALT_A, DOWN)
 #define GPIO156_LCD_D27                PIN_CFG(156, ALT_B)
 #define GPIO156_STMAPE_DAT3    PIN_CFG(156, ALT_C)
 
 #define GPIO157_GPIO           PIN_CFG(157, GPIO)
-#define GPIO157_KP_O7          PIN_CFG(157, ALT_A)
+#define GPIO157_KP_O7          PIN_CFG_PULL(157, ALT_A, UP)
 #define GPIO157_LCD_D28                PIN_CFG(157, ALT_B)
 #define GPIO157_STMAPE_DAT2    PIN_CFG(157, ALT_C)
 
 #define GPIO158_GPIO           PIN_CFG(158, GPIO)
-#define GPIO158_KP_O6          PIN_CFG(158, ALT_A)
+#define GPIO158_KP_O6          PIN_CFG_PULL(158, ALT_A, UP)
 #define GPIO158_LCD_D29                PIN_CFG(158, ALT_B)
 #define GPIO158_STMAPE_DAT1    PIN_CFG(158, ALT_C)
 
 #define GPIO159_GPIO           PIN_CFG(159, GPIO)
-#define GPIO159_KP_O5          PIN_CFG(159, ALT_A)
+#define GPIO159_KP_O5          PIN_CFG_PULL(159, ALT_A, UP)
 #define GPIO159_LCD_D30                PIN_CFG(159, ALT_B)
 #define GPIO159_STMAPE_DAT0    PIN_CFG(159, ALT_C)
 
 #define GPIO160_GPIO           PIN_CFG(160, GPIO)
-#define GPIO160_KP_O4          PIN_CFG(160, ALT_A)
+#define GPIO160_KP_O4          PIN_CFG_PULL(160, ALT_A, UP)
 #define GPIO160_LCD_D31                PIN_CFG(160, ALT_B)
 #define GPIO160_NONE           PIN_CFG(160, ALT_C)
 
 #define GPIO161_GPIO           PIN_CFG(161, GPIO)
-#define GPIO161_KP_I3          PIN_CFG(161, ALT_A)
+#define GPIO161_KP_I3          PIN_CFG_PULL(161, ALT_A, DOWN)
 #define GPIO161_LCD_D32                PIN_CFG(161, ALT_B)
 #define GPIO161_UARTMOD_RXD    PIN_CFG(161, ALT_C)
 
 #define GPIO162_GPIO           PIN_CFG(162, GPIO)
-#define GPIO162_KP_I2          PIN_CFG(162, ALT_A)
+#define GPIO162_KP_I2          PIN_CFG_PULL(162, ALT_A, DOWN)
 #define GPIO162_LCD_D33                PIN_CFG(162, ALT_B)
 #define GPIO162_UARTMOD_TXD    PIN_CFG(162, ALT_C)
 
 #define GPIO163_GPIO           PIN_CFG(163, GPIO)
-#define GPIO163_KP_I1          PIN_CFG(163, ALT_A)
+#define GPIO163_KP_I1          PIN_CFG_PULL(163, ALT_A, DOWN)
 #define GPIO163_LCD_D34                PIN_CFG(163, ALT_B)
 #define GPIO163_STMMOD_CLK     PIN_CFG(163, ALT_C)
 
 #define GPIO164_GPIO           PIN_CFG(164, GPIO)
-#define GPIO164_KP_I0          PIN_CFG(164, ALT_A)
+#define GPIO164_KP_I0          PIN_CFG_PULL(164, ALT_A, UP)
 #define GPIO164_LCD_D35                PIN_CFG(164, ALT_B)
 #define GPIO164_STMMOD_DAT3    PIN_CFG(164, ALT_C)
 
 #define GPIO165_GPIO           PIN_CFG(165, GPIO)
-#define GPIO165_KP_O3          PIN_CFG(165, ALT_A)
+#define GPIO165_KP_O3          PIN_CFG_PULL(165, ALT_A, UP)
 #define GPIO165_LCD_D36                PIN_CFG(165, ALT_B)
 #define GPIO165_STMMOD_DAT2    PIN_CFG(165, ALT_C)
 
 #define GPIO166_GPIO           PIN_CFG(166, GPIO)
-#define GPIO166_KP_O2          PIN_CFG(166, ALT_A)
+#define GPIO166_KP_O2          PIN_CFG_PULL(166, ALT_A, UP)
 #define GPIO166_LCD_D37                PIN_CFG(166, ALT_B)
 #define GPIO166_STMMOD_DAT1    PIN_CFG(166, ALT_C)
 
 #define GPIO167_GPIO           PIN_CFG(167, GPIO)
-#define GPIO167_KP_O1          PIN_CFG(167, ALT_A)
+#define GPIO167_KP_O1          PIN_CFG_PULL(167, ALT_A, UP)
 #define GPIO167_LCD_D38                PIN_CFG(167, ALT_B)
 #define GPIO167_STMMOD_DAT0    PIN_CFG(167, ALT_C)
 
 #define GPIO168_GPIO           PIN_CFG(168, GPIO)
-#define GPIO168_KP_O0          PIN_CFG(168, ALT_A)
+#define GPIO168_KP_O0          PIN_CFG_PULL(168, ALT_A, UP)
 #define GPIO168_LCD_D39                PIN_CFG(168, ALT_B)
 #define GPIO168_NONE           PIN_CFG(168, ALT_C)
 
diff --git a/arch/arm/plat-nomadik/include/plat/ske.h b/arch/arm/plat-nomadik/include/plat/ske.h
new file mode 100644 (file)
index 0000000..31382fb
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
+ *
+ * ux500 Scroll key and Keypad Encoder (SKE) header
+ */
+
+#ifndef __SKE_H
+#define __SKE_H
+
+#include <linux/input/matrix_keypad.h>
+
+/* register definitions for SKE peripheral */
+#define SKE_CR         0x00
+#define SKE_VAL0       0x04
+#define SKE_VAL1       0x08
+#define SKE_DBCR       0x0C
+#define SKE_IMSC       0x10
+#define SKE_RIS                0x14
+#define SKE_MIS                0x18
+#define SKE_ICR                0x1C
+
+/*
+ * Keypad module
+ */
+
+/**
+ * struct keypad_platform_data - structure for platform specific data
+ * @init:      pointer to keypad init function
+ * @exit:      pointer to keypad deinitialisation function
+ * @keymap_data: matrix scan code table for keycodes
+ * @krow:      maximum number of rows
+ * @kcol:      maximum number of columns
+ * @debounce_ms: platform specific debounce time
+ * @no_autorepeat: flag for auto repetition
+ * @wakeup_enable: allow waking up the system
+ */
+struct ske_keypad_platform_data {
+       int (*init)(void);
+       int (*exit)(void);
+       const struct matrix_keymap_data *keymap_data;
+       u8 krow;
+       u8 kcol;
+       u8 debounce_ms;
+       bool no_autorepeat;
+       bool wakeup_enable;
+};
+#endif /*__SKE_KPD_H*/
diff --git a/arch/arm/plat-omap/include/plat/omap4-keypad.h b/arch/arm/plat-omap/include/plat/omap4-keypad.h
new file mode 100644 (file)
index 0000000..2b1d9bc
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef ARCH_ARM_PLAT_OMAP4_KEYPAD_H
+#define ARCH_ARM_PLAT_OMAP4_KEYPAD_H
+
+#include <linux/input/matrix_keypad.h>
+
+struct omap4_keypad_platform_data {
+       const struct matrix_keymap_data *keymap_data;
+
+       u8 rows;
+       u8 cols;
+};
+
+extern int omap4_keyboard_init(struct omap4_keypad_platform_data *);
+#endif
index a7ca75212bfe1a00cc68bce670db9f9d6ce66dc9..e95d7876ca6bd573b110b778ffb52574dda62038 100644 (file)
@@ -175,8 +175,7 @@ EXPORT_SYMBOL_GPL(unregister_keyboard_notifier);
  */
 
 struct getset_keycode_data {
-       unsigned int scancode;
-       unsigned int keycode;
+       struct input_keymap_entry ke;
        int error;
 };
 
@@ -184,32 +183,50 @@ static int getkeycode_helper(struct input_handle *handle, void *data)
 {
        struct getset_keycode_data *d = data;
 
-       d->error = input_get_keycode(handle->dev, d->scancode, &d->keycode);
+       d->error = input_get_keycode(handle->dev, &d->ke);
 
        return d->error == 0; /* stop as soon as we successfully get one */
 }
 
 int getkeycode(unsigned int scancode)
 {
-       struct getset_keycode_data d = { scancode, 0, -ENODEV };
+       struct getset_keycode_data d = {
+               .ke     = {
+                       .flags          = 0,
+                       .len            = sizeof(scancode),
+                       .keycode        = 0,
+               },
+               .error  = -ENODEV,
+       };
+
+       memcpy(d.ke.scancode, &scancode, sizeof(scancode));
 
        input_handler_for_each_handle(&kbd_handler, &d, getkeycode_helper);
 
-       return d.error ?: d.keycode;
+       return d.error ?: d.ke.keycode;
 }
 
 static int setkeycode_helper(struct input_handle *handle, void *data)
 {
        struct getset_keycode_data *d = data;
 
-       d->error = input_set_keycode(handle->dev, d->scancode, d->keycode);
+       d->error = input_set_keycode(handle->dev, &d->ke);
 
        return d->error == 0; /* stop as soon as we successfully set one */
 }
 
 int setkeycode(unsigned int scancode, unsigned int keycode)
 {
-       struct getset_keycode_data d = { scancode, keycode, -ENODEV };
+       struct getset_keycode_data d = {
+               .ke     = {
+                       .flags          = 0,
+                       .len            = sizeof(scancode),
+                       .keycode        = keycode,
+               },
+               .error  = -ENODEV,
+       };
+
+       memcpy(d.ke.scancode, &scancode, sizeof(scancode));
 
        input_handler_for_each_handle(&kbd_handler, &d, setkeycode_helper);
 
index f3019f53e8752becb85b459cc6ad5bc902b986ab..eaa5d3efa79da4cb061b4fbaf9d6acb6f21afd5e 100644 (file)
@@ -566,10 +566,16 @@ static const unsigned char sysrq_xlate[KEY_MAX + 1] =
 static bool sysrq_down;
 static int sysrq_alt_use;
 static int sysrq_alt;
+static DEFINE_SPINLOCK(sysrq_event_lock);
 
 static bool sysrq_filter(struct input_handle *handle, unsigned int type,
                         unsigned int code, int value)
 {
+       bool suppress;
+
+       /* We are called with interrupts disabled, just take the lock */
+       spin_lock(&sysrq_event_lock);
+
        if (type != EV_KEY)
                goto out;
 
@@ -601,7 +607,10 @@ static bool sysrq_filter(struct input_handle *handle, unsigned int type,
        }
 
 out:
-       return sysrq_down;
+       suppress = sysrq_down;
+       spin_unlock(&sysrq_event_lock);
+
+       return suppress;
 }
 
 static int sysrq_connect(struct input_handler *handler,
@@ -652,8 +661,8 @@ static void sysrq_disconnect(struct input_handle *handle)
 }
 
 /*
- * We are matching on KEY_LEFTALT insteard of KEY_SYSRQ because not all
- * keyboards have SysRq ikey predefined and so user may add it to keymap
+ * We are matching on KEY_LEFTALT instead of KEY_SYSRQ because not all
+ * keyboards have SysRq key predefined and so user may add it to keymap
  * later, but we expect all such keyboards to have left alt.
  */
 static const struct input_device_id sysrq_ids[] = {
index 7832b6e2478b09c9f51ae9abc31ce4c98d708c01..515345b11ac95b380942638ce25f8ea8afccd472 100644 (file)
@@ -1780,6 +1780,11 @@ static bool hid_ignore(struct hid_device *hdev)
                    hdev->product <= USB_DEVICE_ID_SOUNDGRAPH_IMON_LAST)
                        return true;
                break;
+       case USB_VENDOR_ID_HANWANG:
+               if (hdev->product >= USB_DEVICE_ID_HANWANG_TABLET_FIRST &&
+                   hdev->product <= USB_DEVICE_ID_HANWANG_TABLET_LAST)
+                       return true;
+               break;
        }
 
        if (hdev->type == HID_TYPE_USBMOUSE &&
index 3ee999d33004a64ea5801987bb7d3016960f0990..3341baa86a30f66ab426b78cd68b2f953f311a1b 100644 (file)
 #define USB_DEVICE_ID_GYRATION_REMOTE_2 0x0003
 #define USB_DEVICE_ID_GYRATION_REMOTE_3 0x0008
 
+#define USB_VENDOR_ID_HANWANG          0x0b57
+#define USB_DEVICE_ID_HANWANG_TABLET_FIRST     0x5000
+#define USB_DEVICE_ID_HANWANG_TABLET_LAST      0x8fff
+
 #define USB_VENDOR_ID_HAPP             0x078b
 #define USB_DEVICE_ID_UGCI_DRIVING     0x0010
 #define USB_DEVICE_ID_UGCI_FLYING      0x0020
index 834ef47b76d6f456184dff1adc9663160bebfa28..bb0b3659437b1a98302f159f2e1773db21a0e2ee 100644 (file)
@@ -68,39 +68,52 @@ static const struct {
 #define map_key_clear(c)       hid_map_usage_clear(hidinput, usage, &bit, \
                &max, EV_KEY, (c))
 
-static inline int match_scancode(unsigned int code, unsigned int scancode)
+static bool match_scancode(struct hid_usage *usage,
+                          unsigned int cur_idx, unsigned int scancode)
 {
-       if (scancode == 0)
-               return 1;
-
-       return (code & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
+       return (usage->hid & (HID_USAGE_PAGE | HID_USAGE)) == scancode;
 }
 
-static inline int match_keycode(unsigned int code, unsigned int keycode)
+static bool match_keycode(struct hid_usage *usage,
+                         unsigned int cur_idx, unsigned int keycode)
 {
-       if (keycode == 0)
-               return 1;
+       /*
+        * We should exclude unmapped usages when doing lookup by keycode.
+        */
+       return (usage->type == EV_KEY && usage->code == keycode);
+}
 
-       return code == keycode;
+static bool match_index(struct hid_usage *usage,
+                       unsigned int cur_idx, unsigned int idx)
+{
+       return cur_idx == idx;
 }
 
+typedef bool (*hid_usage_cmp_t)(struct hid_usage *usage,
+                               unsigned int cur_idx, unsigned int val);
+
 static struct hid_usage *hidinput_find_key(struct hid_device *hid,
-                                          unsigned int scancode,
-                                          unsigned int keycode)
+                                          hid_usage_cmp_t match,
+                                          unsigned int value,
+                                          unsigned int *usage_idx)
 {
-       int i, j, k;
+       unsigned int i, j, k, cur_idx = 0;
        struct hid_report *report;
        struct hid_usage *usage;
 
        for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) {
                list_for_each_entry(report, &hid->report_enum[k].report_list, list) {
                        for (i = 0; i < report->maxfield; i++) {
-                               for ( j = 0; j < report->field[i]->maxusage; j++) {
+                               for (j = 0; j < report->field[i]->maxusage; j++) {
                                        usage = report->field[i]->usage + j;
-                                       if (usage->type == EV_KEY &&
-                                               match_scancode(usage->hid, scancode) &&
-                                               match_keycode(usage->code, keycode))
-                                               return usage;
+                                       if (usage->type == EV_KEY || usage->type == 0) {
+                                               if (match(usage, cur_idx, value)) {
+                                                       if (usage_idx)
+                                                               *usage_idx = cur_idx;
+                                                       return usage;
+                                               }
+                                               cur_idx++;
+                                       }
                                }
                        }
                }
@@ -108,39 +121,68 @@ static struct hid_usage *hidinput_find_key(struct hid_device *hid,
        return NULL;
 }
 
+static struct hid_usage *hidinput_locate_usage(struct hid_device *hid,
+                                       const struct input_keymap_entry *ke,
+                                       unsigned int *index)
+{
+       struct hid_usage *usage;
+       unsigned int scancode;
+
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+               usage = hidinput_find_key(hid, match_index, ke->index, index);
+       else if (input_scancode_to_scalar(ke, &scancode) == 0)
+               usage = hidinput_find_key(hid, match_scancode, scancode, index);
+       else
+               usage = NULL;
+
+       return usage;
+}
+
 static int hidinput_getkeycode(struct input_dev *dev,
-                              unsigned int scancode, unsigned int *keycode)
+                              struct input_keymap_entry *ke)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct hid_usage *usage;
+       unsigned int scancode, index;
 
-       usage = hidinput_find_key(hid, scancode, 0);
+       usage = hidinput_locate_usage(hid, ke, &index);
        if (usage) {
-               *keycode = usage->code;
+               ke->keycode = usage->type == EV_KEY ?
+                               usage->code : KEY_RESERVED;
+               ke->index = index;
+               scancode = usage->hid & (HID_USAGE_PAGE | HID_USAGE);
+               ke->len = sizeof(scancode);
+               memcpy(ke->scancode, &scancode, sizeof(scancode));
                return 0;
        }
+
        return -EINVAL;
 }
 
 static int hidinput_setkeycode(struct input_dev *dev,
-                              unsigned int scancode, unsigned int keycode)
+                              const struct input_keymap_entry *ke,
+                              unsigned int *old_keycode)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct hid_usage *usage;
-       int old_keycode;
 
-       usage = hidinput_find_key(hid, scancode, 0);
+       usage = hidinput_locate_usage(hid, ke, NULL);
        if (usage) {
-               old_keycode = usage->code;
-               usage->code = keycode;
+               *old_keycode = usage->type == EV_KEY ?
+                               usage->code : KEY_RESERVED;
+               usage->code = ke->keycode;
 
-               clear_bit(old_keycode, dev->keybit);
+               clear_bit(*old_keycode, dev->keybit);
                set_bit(usage->code, dev->keybit);
-               dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n", keycode, scancode);
-               /* Set the keybit for the old keycode if the old keycode is used
-                * by another key */
-               if (hidinput_find_key (hid, 0, old_keycode))
-                       set_bit(old_keycode, dev->keybit);
+               dbg_hid(KERN_DEBUG "Assigned keycode %d to HID usage code %x\n",
+                       usage->code, usage->hid);
+
+               /*
+                * Set the keybit for the old keycode if the old keycode is used
+                * by another key
+                */
+               if (hidinput_find_key(hid, match_keycode, *old_keycode, NULL))
+                       set_bit(*old_keycode, dev->keybit);
 
                return 0;
        }
@@ -835,8 +877,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                                        hid->ll_driver->hidinput_input_event;
                                input_dev->open = hidinput_open;
                                input_dev->close = hidinput_close;
-                               input_dev->setkeycode = hidinput_setkeycode;
-                               input_dev->getkeycode = hidinput_getkeycode;
+                               input_dev->setkeycode_new = hidinput_setkeycode;
+                               input_dev->getkeycode_new = hidinput_getkeycode;
 
                                input_dev->name = hid->name;
                                input_dev->phys = hid->phys;
index 535fea4fe67f026bbe6475c5cd70c8a18d0bc4dd..e3f7fc6f95657e2f4846cebc0efae905d309c31d 100644 (file)
@@ -534,6 +534,80 @@ static int handle_eviocgbit(struct input_dev *dev,
 }
 #undef OLD_KEY_MAX
 
+static int evdev_handle_get_keycode(struct input_dev *dev,
+                                   void __user *p, size_t size)
+{
+       struct input_keymap_entry ke;
+       int error;
+
+       memset(&ke, 0, sizeof(ke));
+
+       if (size == sizeof(unsigned int[2])) {
+               /* legacy case */
+               int __user *ip = (int __user *)p;
+
+               if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+                       return -EFAULT;
+
+               ke.len = sizeof(unsigned int);
+               ke.flags = 0;
+
+               error = input_get_keycode(dev, &ke);
+               if (error)
+                       return error;
+
+               if (put_user(ke.keycode, ip + 1))
+                       return -EFAULT;
+
+       } else {
+               size = min(size, sizeof(ke));
+
+               if (copy_from_user(&ke, p, size))
+                       return -EFAULT;
+
+               error = input_get_keycode(dev, &ke);
+               if (error)
+                       return error;
+
+               if (copy_to_user(p, &ke, size))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+static int evdev_handle_set_keycode(struct input_dev *dev,
+                                   void __user *p, size_t size)
+{
+       struct input_keymap_entry ke;
+
+       memset(&ke, 0, sizeof(ke));
+
+       if (size == sizeof(unsigned int[2])) {
+               /* legacy case */
+               int __user *ip = (int __user *)p;
+
+               if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
+                       return -EFAULT;
+
+               if (get_user(ke.keycode, ip + 1))
+                       return -EFAULT;
+
+               ke.len = sizeof(unsigned int);
+               ke.flags = 0;
+
+       } else {
+               size = min(size, sizeof(ke));
+
+               if (copy_from_user(&ke, p, size))
+                       return -EFAULT;
+
+               if (ke.len > sizeof(ke.scancode))
+                       return -EINVAL;
+       }
+
+       return input_set_keycode(dev, &ke);
+}
+
 static long evdev_do_ioctl(struct file *file, unsigned int cmd,
                           void __user *p, int compat_mode)
 {
@@ -580,25 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 
                return 0;
 
-       case EVIOCGKEYCODE:
-               if (get_user(t, ip))
-                       return -EFAULT;
-
-               error = input_get_keycode(dev, t, &v);
-               if (error)
-                       return error;
-
-               if (put_user(v, ip + 1))
-                       return -EFAULT;
-
-               return 0;
-
-       case EVIOCSKEYCODE:
-               if (get_user(t, ip) || get_user(v, ip + 1))
-                       return -EFAULT;
-
-               return input_set_keycode(dev, t, v);
-
        case EVIOCRMFF:
                return input_ff_erase(dev, (int)(unsigned long) p, file);
 
@@ -620,7 +675,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
 
        /* Now check variable-length commands */
 #define EVIOC_MASK_SIZE(nr)    ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
-
        switch (EVIOC_MASK_SIZE(cmd)) {
 
        case EVIOCGKEY(0):
@@ -654,6 +708,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
                        return -EFAULT;
 
                return error;
+
+       case EVIOC_MASK_SIZE(EVIOCGKEYCODE):
+               return evdev_handle_get_keycode(dev, p, size);
+
+       case EVIOC_MASK_SIZE(EVIOCSKEYCODE):
+               return evdev_handle_set_keycode(dev, p, size);
        }
 
        /* Multi-number variable-length handlers */
index 7392992da4243c35e127466035e1f35cb377af3a..422aa0a6b77fc62d3d4b46ffd9a42ba85cce3c99 100644 (file)
@@ -59,44 +59,52 @@ MODULE_DEVICE_TABLE(pci, emu_tbl);
 
 static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-       int ioport, iolen;
        struct emu *emu;
        struct gameport *port;
-
-       if (pci_enable_device(pdev))
-               return -EBUSY;
-
-       ioport = pci_resource_start(pdev, 0);
-       iolen = pci_resource_len(pdev, 0);
-
-       if (!request_region(ioport, iolen, "emu10k1-gp"))
-               return -EBUSY;
+       int error;
 
        emu = kzalloc(sizeof(struct emu), GFP_KERNEL);
        port = gameport_allocate_port();
        if (!emu || !port) {
                printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
-               release_region(ioport, iolen);
-               kfree(emu);
-               gameport_free_port(port);
-               return -ENOMEM;
+               error = -ENOMEM;
+               goto err_out_free;
        }
 
-       emu->io = ioport;
-       emu->size = iolen;
+       error = pci_enable_device(pdev);
+       if (error)
+               goto err_out_free;
+
+       emu->io = pci_resource_start(pdev, 0);
+       emu->size = pci_resource_len(pdev, 0);
+
        emu->dev = pdev;
        emu->gameport = port;
 
        gameport_set_name(port, "EMU10K1");
        gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
        port->dev.parent = &pdev->dev;
-       port->io = ioport;
+       port->io = emu->io;
+
+       if (!request_region(emu->io, emu->size, "emu10k1-gp")) {
+               printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n",
+                       emu->io, emu->io + emu->size - 1);
+               error = -EBUSY;
+               goto err_out_disable_dev;
+       }
 
        pci_set_drvdata(pdev, emu);
 
        gameport_register_port(port);
 
        return 0;
+
+ err_out_disable_dev:
+       pci_disable_device(pdev);
+ err_out_free:
+       gameport_free_port(port);
+       kfree(emu);
+       return error;
 }
 
 static void __devexit emu_remove(struct pci_dev *pdev)
@@ -106,6 +114,8 @@ static void __devexit emu_remove(struct pci_dev *pdev)
        gameport_unregister_port(emu->gameport);
        release_region(emu->io, emu->size);
        kfree(emu);
+
+       pci_disable_device(pdev);
 }
 
 static struct pci_driver emu_driver = {
index 14d3f3e208a2f557a80156dbe51c29f8aa0a0773..a3b70ff21018bce1c7e6e82e83ad429785d36d62 100644 (file)
@@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci)
 {
        struct fm801_gp *gp = pci_get_drvdata(pci);
 
-       if (gp) {
-               gameport_unregister_port(gp->gameport);
-               release_resource(gp->res_port);
-               kfree(gp);
-       }
+       gameport_unregister_port(gp->gameport);
+       release_resource(gp->res_port);
+       kfree(gp);
+
+       pci_disable_device(pci);
 }
 
 static const struct pci_device_id fm801_gp_id_table[] = {
index 7919c25372257e0baa922903565f55fd94effb29..d092ef9291dabe1301006bc94310bc1973037808 100644 (file)
@@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev,
        if (code == ABS_MT_SLOT) {
                /*
                 * "Stage" the event; we'll flush it later, when we
-                * get actiual touch data.
+                * get actual touch data.
                 */
                if (*pval >= 0 && *pval < dev->mtsize)
                        dev->slot = *pval;
@@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev,
                pold = &mtslot->abs[code - ABS_MT_FIRST];
        } else {
                /*
-                * Bypass filtering for multitouch events when
+                * Bypass filtering for multi-touch events when
                 * not employing slots.
                 */
                pold = NULL;
@@ -634,78 +634,141 @@ static void input_disconnect_device(struct input_dev *dev)
        spin_unlock_irq(&dev->event_lock);
 }
 
-static int input_fetch_keycode(struct input_dev *dev, int scancode)
+/**
+ * input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry
+ * @ke: keymap entry containing scancode to be converted.
+ * @scancode: pointer to the location where converted scancode should
+ *     be stored.
+ *
+ * This function is used to convert scancode stored in &struct keymap_entry
+ * into scalar form understood by legacy keymap handling methods. These
+ * methods expect scancodes to be represented as 'unsigned int'.
+ */
+int input_scancode_to_scalar(const struct input_keymap_entry *ke,
+                            unsigned int *scancode)
+{
+       switch (ke->len) {
+       case 1:
+               *scancode = *((u8 *)ke->scancode);
+               break;
+
+       case 2:
+               *scancode = *((u16 *)ke->scancode);
+               break;
+
+       case 4:
+               *scancode = *((u32 *)ke->scancode);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(input_scancode_to_scalar);
+
+/*
+ * Those routines handle the default case where no [gs]etkeycode() is
+ * defined. In this case, an array indexed by the scancode is used.
+ */
+
+static unsigned int input_fetch_keycode(struct input_dev *dev,
+                                       unsigned int index)
 {
        switch (dev->keycodesize) {
-               case 1:
-                       return ((u8 *)dev->keycode)[scancode];
+       case 1:
+               return ((u8 *)dev->keycode)[index];
 
-               case 2:
-                       return ((u16 *)dev->keycode)[scancode];
+       case 2:
+               return ((u16 *)dev->keycode)[index];
 
-               default:
-                       return ((u32 *)dev->keycode)[scancode];
+       default:
+               return ((u32 *)dev->keycode)[index];
        }
 }
 
 static int input_default_getkeycode(struct input_dev *dev,
-                                   unsigned int scancode,
-                                   unsigned int *keycode)
+                                   struct input_keymap_entry *ke)
 {
+       unsigned int index;
+       int error;
+
        if (!dev->keycodesize)
                return -EINVAL;
 
-       if (scancode >= dev->keycodemax)
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+               index = ke->index;
+       else {
+               error = input_scancode_to_scalar(ke, &index);
+               if (error)
+                       return error;
+       }
+
+       if (index >= dev->keycodemax)
                return -EINVAL;
 
-       *keycode = input_fetch_keycode(dev, scancode);
+       ke->keycode = input_fetch_keycode(dev, index);
+       ke->index = index;
+       ke->len = sizeof(index);
+       memcpy(ke->scancode, &index, sizeof(index));
 
        return 0;
 }
 
 static int input_default_setkeycode(struct input_dev *dev,
-                                   unsigned int scancode,
-                                   unsigned int keycode)
+                                   const struct input_keymap_entry *ke,
+                                   unsigned int *old_keycode)
 {
-       int old_keycode;
+       unsigned int index;
+       int error;
        int i;
 
-       if (scancode >= dev->keycodemax)
+       if (!dev->keycodesize)
                return -EINVAL;
 
-       if (!dev->keycodesize)
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+               index = ke->index;
+       } else {
+               error = input_scancode_to_scalar(ke, &index);
+               if (error)
+                       return error;
+       }
+
+       if (index >= dev->keycodemax)
                return -EINVAL;
 
-       if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))
+       if (dev->keycodesize < sizeof(dev->keycode) &&
+                       (ke->keycode >> (dev->keycodesize * 8)))
                return -EINVAL;
 
        switch (dev->keycodesize) {
                case 1: {
                        u8 *k = (u8 *)dev->keycode;
-                       old_keycode = k[scancode];
-                       k[scancode] = keycode;
+                       *old_keycode = k[index];
+                       k[index] = ke->keycode;
                        break;
                }
                case 2: {
                        u16 *k = (u16 *)dev->keycode;
-                       old_keycode = k[scancode];
-                       k[scancode] = keycode;
+                       *old_keycode = k[index];
+                       k[index] = ke->keycode;
                        break;
                }
                default: {
                        u32 *k = (u32 *)dev->keycode;
-                       old_keycode = k[scancode];
-                       k[scancode] = keycode;
+                       *old_keycode = k[index];
+                       k[index] = ke->keycode;
                        break;
                }
        }
 
-       __clear_bit(old_keycode, dev->keybit);
-       __set_bit(keycode, dev->keybit);
+       __clear_bit(*old_keycode, dev->keybit);
+       __set_bit(ke->keycode, dev->keybit);
 
        for (i = 0; i < dev->keycodemax; i++) {
-               if (input_fetch_keycode(dev, i) == old_keycode) {
-                       __set_bit(old_keycode, dev->keybit);
+               if (input_fetch_keycode(dev, i) == *old_keycode) {
+                       __set_bit(*old_keycode, dev->keybit);
                        break; /* Setting the bit twice is useless, so break */
                }
        }
@@ -716,53 +779,86 @@ static int input_default_setkeycode(struct input_dev *dev,
 /**
  * input_get_keycode - retrieve keycode currently mapped to a given scancode
  * @dev: input device which keymap is being queried
- * @scancode: scancode (or its equivalent for device in question) for which
- *     keycode is needed
- * @keycode: result
+ * @ke: keymap entry
  *
  * This function should be called by anyone interested in retrieving current
- * keymap. Presently keyboard and evdev handlers use it.
+ * keymap. Presently evdev handlers use it.
  */
-int input_get_keycode(struct input_dev *dev,
-                     unsigned int scancode, unsigned int *keycode)
+int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke)
 {
        unsigned long flags;
        int retval;
 
        spin_lock_irqsave(&dev->event_lock, flags);
-       retval = dev->getkeycode(dev, scancode, keycode);
-       spin_unlock_irqrestore(&dev->event_lock, flags);
 
+       if (dev->getkeycode) {
+               /*
+                * Support for legacy drivers, that don't implement the new
+                * ioctls
+                */
+               u32 scancode = ke->index;
+
+               memcpy(ke->scancode, &scancode, sizeof(scancode));
+               ke->len = sizeof(scancode);
+               retval = dev->getkeycode(dev, scancode, &ke->keycode);
+       } else {
+               retval = dev->getkeycode_new(dev, ke);
+       }
+
+       spin_unlock_irqrestore(&dev->event_lock, flags);
        return retval;
 }
 EXPORT_SYMBOL(input_get_keycode);
 
 /**
- * input_get_keycode - assign new keycode to a given scancode
+ * input_set_keycode - attribute a keycode to a given scancode
  * @dev: input device which keymap is being updated
- * @scancode: scancode (or its equivalent for device in question)
- * @keycode: new keycode to be assigned to the scancode
+ * @ke: new keymap entry
  *
  * This function should be called by anyone needing to update current
  * keymap. Presently keyboard and evdev handlers use it.
  */
 int input_set_keycode(struct input_dev *dev,
-                     unsigned int scancode, unsigned int keycode)
+                     const struct input_keymap_entry *ke)
 {
        unsigned long flags;
        unsigned int old_keycode;
        int retval;
 
-       if (keycode > KEY_MAX)
+       if (ke->keycode > KEY_MAX)
                return -EINVAL;
 
        spin_lock_irqsave(&dev->event_lock, flags);
 
-       retval = dev->getkeycode(dev, scancode, &old_keycode);
-       if (retval)
-               goto out;
+       if (dev->setkeycode) {
+               /*
+                * Support for legacy drivers, that don't implement the new
+                * ioctls
+                */
+               unsigned int scancode;
+
+               retval = input_scancode_to_scalar(ke, &scancode);
+               if (retval)
+                       goto out;
+
+               /*
+                * We need to know the old scancode, in order to generate a
+                * keyup effect, if the set operation happens successfully
+                */
+               if (!dev->getkeycode) {
+                       retval = -EINVAL;
+                       goto out;
+               }
+
+               retval = dev->getkeycode(dev, scancode, &old_keycode);
+               if (retval)
+                       goto out;
+
+               retval = dev->setkeycode(dev, scancode, ke->keycode);
+       } else {
+               retval = dev->setkeycode_new(dev, ke, &old_keycode);
+       }
 
-       retval = dev->setkeycode(dev, scancode, keycode);
        if (retval)
                goto out;
 
@@ -1601,7 +1697,7 @@ EXPORT_SYMBOL(input_free_device);
  *
  * This function allocates all necessary memory for MT slot handling in the
  * input device, and adds ABS_MT_SLOT to the device capabilities. All slots
- * are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1.
+ * are initially marked as unused by setting ABS_MT_TRACKING_ID to -1.
  */
 int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
 {
@@ -1759,11 +1855,11 @@ int input_register_device(struct input_dev *dev)
                dev->rep[REP_PERIOD] = 33;
        }
 
-       if (!dev->getkeycode)
-               dev->getkeycode = input_default_getkeycode;
+       if (!dev->getkeycode && !dev->getkeycode_new)
+               dev->getkeycode_new = input_default_getkeycode;
 
-       if (!dev->setkeycode)
-               dev->setkeycode = input_default_setkeycode;
+       if (!dev->setkeycode && !dev->setkeycode_new)
+               dev->setkeycode_new = input_default_setkeycode;
 
        dev_set_name(&dev->dev, "input%ld",
                     (unsigned long) atomic_inc_return(&input_no) - 1);
index aa037fec2f86122500cbd5d14cfca0c59e614804..0426630f0e981089530493862ebdc2ee9efe385d 100644 (file)
@@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
          To compile this driver as a module, choose M here: the
          module will be called newtonkbd.
 
+config KEYBOARD_NOMADIK
+       tristate "ST-Ericsson Nomadik SKE keyboard"
+       depends on PLAT_NOMADIK
+       help
+         Say Y here if you want to use a keypad provided on the SKE controller
+         used on the Ux500 and Nomadik platforms
+
+         To compile this driver as a module, choose M here: the
+         module will be called nmk-ske-keypad.
+
 config KEYBOARD_OPENCORES
        tristate "OpenCores Keyboard Controller"
        help
@@ -424,6 +434,15 @@ config KEYBOARD_OMAP
          To compile this driver as a module, choose M here: the
          module will be called omap-keypad.
 
+config KEYBOARD_OMAP4
+       tristate "TI OMAP4 keypad support"
+       depends on ARCH_OMAP4
+       help
+         Say Y here if you want to use the OMAP4 keypad.
+
+         To compile this driver as a module, choose M here: the
+         module will be called omap4-keypad.
+
 config KEYBOARD_TWL4030
        tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
        depends on TWL4030_CORE
index 504b591be0cdff5d193da2b15180a969075db724..c13809c5c3a764079bdfcd0477f9491bb0381113 100644 (file)
@@ -28,7 +28,9 @@ obj-$(CONFIG_KEYBOARD_MATRIX)         += matrix_keypad.o
 obj-$(CONFIG_KEYBOARD_MAX7359)         += max7359_keypad.o
 obj-$(CONFIG_KEYBOARD_MCS)             += mcs_touchkey.o
 obj-$(CONFIG_KEYBOARD_NEWTON)          += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_NOMADIK)         += nomadik-ske-keypad.o
 obj-$(CONFIG_KEYBOARD_OMAP)            += omap-keypad.o
+obj-$(CONFIG_KEYBOARD_OMAP4)           += omap4-keypad.o
 obj-$(CONFIG_KEYBOARD_OPENCORES)       += opencores-kbd.o
 obj-$(CONFIG_KEYBOARD_PXA27x)          += pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)   += pxa930_rotary.o
index d6918cb966c0603157265851958fd72e9ce5a7a4..b92d1cd5cba1e4ee83923b7bce43c80a7075f5b8 100644 (file)
@@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = {
 #endif
 
 static const struct i2c_device_id adp5588_id[] = {
-       { KBUILD_MODNAME, 0 },
+       { "adp5588-keys", 0 },
        { "adp5587-keys", 0 },
        { }
 };
index 19fa94af207a6fa37fbadc7a3d39a65ea2010482..fed31e0947a170f7a39468f97ec4ab10a3ab3181 100644 (file)
@@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = {
        { 0 }
 };
 
+MODULE_DEVICE_TABLE(serio, hil_dev_ids);
+
 static struct serio_driver hil_serio_drv = {
        .driver         = {
                .name   = "hil_dev",
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
new file mode 100644 (file)
index 0000000..6e0f230
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * License terms:GNU General Public License (GPL) version 2
+ *
+ * Keypad controller driver for the SKE (Scroll Key Encoder) module used in
+ * the Nomadik 8815 and Ux500 platforms.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+
+#include <plat/ske.h>
+
+/* SKE_CR bits */
+#define SKE_KPMLT      (0x1 << 6)
+#define SKE_KPCN       (0x7 << 3)
+#define SKE_KPASEN     (0x1 << 2)
+#define SKE_KPASON     (0x1 << 7)
+
+/* SKE_IMSC bits */
+#define SKE_KPIMA      (0x1 << 2)
+
+/* SKE_ICR bits */
+#define SKE_KPICS      (0x1 << 3)
+#define SKE_KPICA      (0x1 << 2)
+
+/* SKE_RIS bits */
+#define SKE_KPRISA     (0x1 << 2)
+
+#define SKE_KEYPAD_ROW_SHIFT   3
+#define SKE_KPD_KEYMAP_SIZE    (8 * 8)
+
+/* keypad auto scan registers */
+#define SKE_ASR0       0x20
+#define SKE_ASR1       0x24
+#define SKE_ASR2       0x28
+#define SKE_ASR3       0x2C
+
+#define SKE_NUM_ASRX_REGISTERS (4)
+
+/**
+ * struct ske_keypad  - data structure used by keypad driver
+ * @irq:       irq no
+ * @reg_base:  ske regsiters base address
+ * @input:     pointer to input device object
+ * @board:     keypad platform device
+ * @keymap:    matrix scan code table for keycodes
+ * @clk:       clock structure pointer
+ */
+struct ske_keypad {
+       int irq;
+       void __iomem *reg_base;
+       struct input_dev *input;
+       const struct ske_keypad_platform_data *board;
+       unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
+       struct clk *clk;
+       spinlock_t ske_keypad_lock;
+};
+
+static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
+               u8 mask, u8 data)
+{
+       u32 ret;
+
+       spin_lock(&keypad->ske_keypad_lock);
+
+       ret = readl(keypad->reg_base + addr);
+       ret &= ~mask;
+       ret |= data;
+       writel(ret, keypad->reg_base + addr);
+
+       spin_unlock(&keypad->ske_keypad_lock);
+}
+
+/*
+ * ske_keypad_chip_init: init keypad controller configuration
+ *
+ * Enable Multi key press detection, auto scan mode
+ */
+static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
+{
+       u32 value;
+       int timeout = 50;
+
+       /* check SKE_RIS to be 0 */
+       while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
+               cpu_relax();
+
+       if (!timeout)
+               return -EINVAL;
+
+       /*
+        * set debounce value
+        * keypad dbounce is configured in DBCR[15:8]
+        * dbounce value in steps of 32/32.768 ms
+        */
+       spin_lock(&keypad->ske_keypad_lock);
+       value = readl(keypad->reg_base + SKE_DBCR);
+       value = value & 0xff;
+       value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
+       writel(value, keypad->reg_base + SKE_DBCR);
+       spin_unlock(&keypad->ske_keypad_lock);
+
+       /* enable multi key detection */
+       ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
+
+       /*
+        * set up the number of columns
+        * KPCN[5:3] defines no. of keypad columns to be auto scanned
+        */
+       value = (keypad->board->kcol - 1) << 3;
+       ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
+
+       /* clear keypad interrupt for auto(and pending SW) scans */
+       ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
+
+       /* un-mask keypad interrupts */
+       ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+       /* enable automatic scan */
+       ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
+
+       return 0;
+}
+
+static void ske_keypad_read_data(struct ske_keypad *keypad)
+{
+       struct input_dev *input = keypad->input;
+       u16 status;
+       int col = 0, row = 0, code;
+       int ske_asr, ske_ris, key_pressed, i;
+
+       /*
+        * Read the auto scan registers
+        *
+        * Each SKE_ASRx (x=0 to x=3) contains two row values.
+        * lower byte contains row value for column 2*x,
+        * upper byte contains row value for column 2*x + 1
+        */
+       for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
+               ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
+               if (!ske_asr)
+                       continue;
+
+               /* now that ASRx is zero, find out the column x and row y*/
+               if (ske_asr & 0xff) {
+                       col = i * 2;
+                       status = ske_asr & 0xff;
+               } else {
+                       col = (i * 2) + 1;
+                       status = (ske_asr & 0xff00) >> 8;
+               }
+
+               /* find out the row */
+               row = __ffs(status);
+
+               code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
+               ske_ris = readl(keypad->reg_base + SKE_RIS);
+               key_pressed = ske_ris & SKE_KPRISA;
+
+               input_event(input, EV_MSC, MSC_SCAN, code);
+               input_report_key(input, keypad->keymap[code], key_pressed);
+               input_sync(input);
+       }
+}
+
+static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+{
+       struct ske_keypad *keypad = dev_id;
+       int retries = 20;
+
+       /* disable auto scan interrupt; mask the interrupt generated */
+       ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+       ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+       while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
+               msleep(5);
+
+       if (retries) {
+               /* SKEx registers are stable and can be read */
+               ske_keypad_read_data(keypad);
+       }
+
+       /* enable auto scan interrupts */
+       ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit ske_keypad_probe(struct platform_device *pdev)
+{
+       const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
+       struct ske_keypad *keypad;
+       struct input_dev *input;
+       struct resource *res;
+       int irq;
+       int error;
+
+       if (!plat) {
+               dev_err(&pdev->dev, "invalid keypad platform data\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "failed to get keypad irq\n");
+               return -EINVAL;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "missing platform resources\n");
+               return -EINVAL;
+       }
+
+       keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
+       input = input_allocate_device();
+       if (!keypad || !input) {
+               dev_err(&pdev->dev, "failed to allocate keypad memory\n");
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       keypad->irq = irq;
+       keypad->board = plat;
+       keypad->input = input;
+       spin_lock_init(&keypad->ske_keypad_lock);
+
+       if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
+               dev_err(&pdev->dev, "failed to request I/O memory\n");
+               error = -EBUSY;
+               goto err_free_mem;
+       }
+
+       keypad->reg_base = ioremap(res->start, resource_size(res));
+       if (!keypad->reg_base) {
+               dev_err(&pdev->dev, "failed to remap I/O memory\n");
+               error = -ENXIO;
+               goto err_free_mem_region;
+       }
+
+       keypad->clk = clk_get(&pdev->dev, NULL);
+       if (IS_ERR(keypad->clk)) {
+               dev_err(&pdev->dev, "failed to get clk\n");
+               error = PTR_ERR(keypad->clk);
+               goto err_iounmap;
+       }
+
+       input->id.bustype = BUS_HOST;
+       input->name = "ux500-ske-keypad";
+       input->dev.parent = &pdev->dev;
+
+       input->keycode = keypad->keymap;
+       input->keycodesize = sizeof(keypad->keymap[0]);
+       input->keycodemax = ARRAY_SIZE(keypad->keymap);
+
+       input_set_capability(input, EV_MSC, MSC_SCAN);
+
+       __set_bit(EV_KEY, input->evbit);
+       if (!plat->no_autorepeat)
+               __set_bit(EV_REP, input->evbit);
+
+       matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,
+                       input->keycode, input->keybit);
+
+       clk_enable(keypad->clk);
+
+       /* go through board initialization helpers */
+       if (keypad->board->init)
+               keypad->board->init();
+
+       error = ske_keypad_chip_init(keypad);
+       if (error) {
+               dev_err(&pdev->dev, "unable to init keypad hardware\n");
+               goto err_clk_disable;
+       }
+
+       error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
+                                    IRQF_ONESHOT, "ske-keypad", keypad);
+       if (error) {
+               dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
+               goto err_clk_disable;
+       }
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(&pdev->dev,
+                               "unable to register input device: %d\n", error);
+               goto err_free_irq;
+       }
+
+       if (plat->wakeup_enable)
+               device_init_wakeup(&pdev->dev, true);
+
+       platform_set_drvdata(pdev, keypad);
+
+       return 0;
+
+err_free_irq:
+       free_irq(keypad->irq, keypad);
+err_clk_disable:
+       clk_disable(keypad->clk);
+       clk_put(keypad->clk);
+err_iounmap:
+       iounmap(keypad->reg_base);
+err_free_mem_region:
+       release_mem_region(res->start, resource_size(res));
+err_free_mem:
+       input_free_device(input);
+       kfree(keypad);
+       return error;
+}
+
+static int __devexit ske_keypad_remove(struct platform_device *pdev)
+{
+       struct ske_keypad *keypad = platform_get_drvdata(pdev);
+       struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       free_irq(keypad->irq, keypad);
+
+       input_unregister_device(keypad->input);
+
+       clk_disable(keypad->clk);
+       clk_put(keypad->clk);
+
+       if (keypad->board->exit)
+               keypad->board->exit();
+
+       iounmap(keypad->reg_base);
+       release_mem_region(res->start, resource_size(res));
+       kfree(keypad);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ske_keypad_suspend(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct ske_keypad *keypad = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+
+       if (device_may_wakeup(dev))
+               enable_irq_wake(irq);
+       else
+               ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
+
+       return 0;
+}
+
+static int ske_keypad_resume(struct device *dev)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+       struct ske_keypad *keypad = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+
+       if (device_may_wakeup(dev))
+               disable_irq_wake(irq);
+       else
+               ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+
+       return 0;
+}
+
+static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
+       .suspend = ske_keypad_suspend,
+       .resume = ske_keypad_resume,
+};
+#endif
+
+struct platform_driver ske_keypad_driver = {
+       .driver = {
+               .name = "nmk-ske-keypad",
+               .owner  = THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm = &ske_keypad_dev_pm_ops,
+#endif
+       },
+       .probe = ske_keypad_probe,
+       .remove = __devexit_p(ske_keypad_remove),
+};
+
+static int __init ske_keypad_init(void)
+{
+       return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
+}
+module_init(ske_keypad_init);
+
+static void __exit ske_keypad_exit(void)
+{
+       platform_driver_unregister(&ske_keypad_driver);
+}
+module_exit(ske_keypad_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
+MODULE_ALIAS("platform:nomadik-ske-keypad");
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
new file mode 100644 (file)
index 0000000..45bd097
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * OMAP4 Keypad Driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Author: Abraham Arce <x0066660@ti.com>
+ * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <plat/omap4-keypad.h>
+
+/* OMAP4 registers */
+#define OMAP4_KBD_REVISION             0x00
+#define OMAP4_KBD_SYSCONFIG            0x10
+#define OMAP4_KBD_SYSSTATUS            0x14
+#define OMAP4_KBD_IRQSTATUS            0x18
+#define OMAP4_KBD_IRQENABLE            0x1C
+#define OMAP4_KBD_WAKEUPENABLE         0x20
+#define OMAP4_KBD_PENDING              0x24
+#define OMAP4_KBD_CTRL                 0x28
+#define OMAP4_KBD_DEBOUNCINGTIME       0x2C
+#define OMAP4_KBD_LONGKEYTIME          0x30
+#define OMAP4_KBD_TIMEOUT              0x34
+#define OMAP4_KBD_STATEMACHINE         0x38
+#define OMAP4_KBD_ROWINPUTS            0x3C
+#define OMAP4_KBD_COLUMNOUTPUTS                0x40
+#define OMAP4_KBD_FULLCODE31_0         0x44
+#define OMAP4_KBD_FULLCODE63_32                0x48
+
+/* OMAP4 bit definitions */
+#define OMAP4_DEF_IRQENABLE_EVENTEN    (1 << 0)
+#define OMAP4_DEF_IRQENABLE_LONGKEY    (1 << 1)
+#define OMAP4_DEF_IRQENABLE_TIMEOUTEN  (1 << 2)
+#define OMAP4_DEF_WUP_EVENT_ENA                (1 << 0)
+#define OMAP4_DEF_WUP_LONG_KEY_ENA     (1 << 1)
+#define OMAP4_DEF_CTRL_NOSOFTMODE      (1 << 1)
+#define OMAP4_DEF_CTRLPTVVALUE         (1 << 2)
+#define OMAP4_DEF_CTRLPTV              (1 << 1)
+
+/* OMAP4 values */
+#define OMAP4_VAL_IRQDISABLE           0x00
+#define OMAP4_VAL_DEBOUNCINGTIME       0x07
+#define OMAP4_VAL_FUNCTIONALCFG                0x1E
+
+#define OMAP4_MASK_IRQSTATUSDISABLE    0xFFFF
+
+struct omap4_keypad {
+       struct input_dev *input;
+
+       void __iomem *base;
+       int irq;
+
+       unsigned int rows;
+       unsigned int cols;
+       unsigned int row_shift;
+       unsigned char key_state[8];
+       unsigned short keymap[];
+};
+
+static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
+{
+       __raw_writel(OMAP4_VAL_FUNCTIONALCFG,
+                       keypad_data->base + OMAP4_KBD_CTRL);
+       __raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
+                       keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
+       __raw_writel(OMAP4_VAL_IRQDISABLE,
+                       keypad_data->base + OMAP4_KBD_IRQSTATUS);
+       __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+                       keypad_data->base + OMAP4_KBD_IRQENABLE);
+       __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
+                       keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
+}
+
+/* Interrupt handler */
+static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
+{
+       struct omap4_keypad *keypad_data = dev_id;
+       struct input_dev *input_dev = keypad_data->input;
+       unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
+       unsigned int col, row, code, changed;
+       u32 *new_state = (u32 *) key_state;
+
+       /* Disable interrupts */
+       __raw_writel(OMAP4_VAL_IRQDISABLE,
+                    keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+       *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
+       *(new_state + 1) = __raw_readl(keypad_data->base
+                                               + OMAP4_KBD_FULLCODE63_32);
+
+       for (row = 0; row < keypad_data->rows; row++) {
+               changed = key_state[row] ^ keypad_data->key_state[row];
+               if (!changed)
+                       continue;
+
+               for (col = 0; col < keypad_data->cols; col++) {
+                       if (changed & (1 << col)) {
+                               code = MATRIX_SCAN_CODE(row, col,
+                                               keypad_data->row_shift);
+                               input_event(input_dev, EV_MSC, MSC_SCAN, code);
+                               input_report_key(input_dev,
+                                                keypad_data->keymap[code],
+                                                key_state[row] & (1 << col));
+                       }
+               }
+       }
+
+       input_sync(input_dev);
+
+       memcpy(keypad_data->key_state, key_state,
+               sizeof(keypad_data->key_state));
+
+       /* clear pending interrupts */
+       __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
+                       keypad_data->base + OMAP4_KBD_IRQSTATUS);
+
+       /* enable interrupts */
+       __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
+                       keypad_data->base + OMAP4_KBD_IRQENABLE);
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit omap4_keypad_probe(struct platform_device *pdev)
+{
+       const struct omap4_keypad_platform_data *pdata;
+       struct omap4_keypad *keypad_data;
+       struct input_dev *input_dev;
+       struct resource *res;
+       resource_size_t size;
+       unsigned int row_shift, max_keys;
+       int irq;
+       int error;
+
+       /* platform data */
+       pdata = pdev->dev.platform_data;
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform data defined\n");
+               return -EINVAL;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no base address specified\n");
+               return -EINVAL;
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (!irq) {
+               dev_err(&pdev->dev, "no keyboard irq assigned\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->keymap_data) {
+               dev_err(&pdev->dev, "no keymap data defined\n");
+               return -EINVAL;
+       }
+
+       row_shift = get_count_order(pdata->cols);
+       max_keys = pdata->rows << row_shift;
+
+       keypad_data = kzalloc(sizeof(struct omap4_keypad) +
+                               max_keys * sizeof(keypad_data->keymap[0]),
+                             GFP_KERNEL);
+       if (!keypad_data) {
+               dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
+               return -ENOMEM;
+       }
+
+       size = resource_size(res);
+
+       res = request_mem_region(res->start, size, pdev->name);
+       if (!res) {
+               dev_err(&pdev->dev, "can't request mem region\n");
+               error = -EBUSY;
+               goto err_free_keypad;
+       }
+
+       keypad_data->base = ioremap(res->start, resource_size(res));
+       if (!keypad_data->base) {
+               dev_err(&pdev->dev, "can't ioremap mem resource\n");
+               error = -ENOMEM;
+               goto err_release_mem;
+       }
+
+       keypad_data->irq = irq;
+       keypad_data->row_shift = row_shift;
+       keypad_data->rows = pdata->rows;
+       keypad_data->cols = pdata->cols;
+
+       /* input device allocation */
+       keypad_data->input = input_dev = input_allocate_device();
+       if (!input_dev) {
+               error = -ENOMEM;
+               goto err_unmap;
+       }
+
+       input_dev->name = pdev->name;
+       input_dev->dev.parent = &pdev->dev;
+       input_dev->id.bustype = BUS_HOST;
+       input_dev->id.vendor = 0x0001;
+       input_dev->id.product = 0x0001;
+       input_dev->id.version = 0x0001;
+
+       input_dev->keycode      = keypad_data->keymap;
+       input_dev->keycodesize  = sizeof(keypad_data->keymap[0]);
+       input_dev->keycodemax   = max_keys;
+
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(EV_REP, input_dev->evbit);
+
+       input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+       input_set_drvdata(input_dev, keypad_data);
+
+       matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
+                       input_dev->keycode, input_dev->keybit);
+
+       omap4_keypad_config(keypad_data);
+
+       error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
+                            IRQF_TRIGGER_RISING,
+                            "omap4-keypad", keypad_data);
+       if (error) {
+               dev_err(&pdev->dev, "failed to register interrupt\n");
+               goto err_free_input;
+       }
+
+       error = input_register_device(keypad_data->input);
+       if (error < 0) {
+               dev_err(&pdev->dev, "failed to register input device\n");
+               goto err_free_irq;
+       }
+
+
+       platform_set_drvdata(pdev, keypad_data);
+       return 0;
+
+err_free_irq:
+       free_irq(keypad_data->irq, keypad_data);
+err_free_input:
+       input_free_device(input_dev);
+err_unmap:
+       iounmap(keypad_data->base);
+err_release_mem:
+       release_mem_region(res->start, size);
+err_free_keypad:
+       kfree(keypad_data);
+       return error;
+}
+
+static int __devexit omap4_keypad_remove(struct platform_device *pdev)
+{
+       struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
+       struct resource *res;
+
+       free_irq(keypad_data->irq, keypad_data);
+       input_unregister_device(keypad_data->input);
+
+       iounmap(keypad_data->base);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(res->start, resource_size(res));
+
+       kfree(keypad_data);
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver omap4_keypad_driver = {
+       .probe          = omap4_keypad_probe,
+       .remove         = __devexit_p(omap4_keypad_remove),
+       .driver         = {
+               .name   = "omap4-keypad",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap4_keypad_init(void)
+{
+       return platform_driver_register(&omap4_keypad_driver);
+}
+module_init(omap4_keypad_init);
+
+static void __exit omap4_keypad_exit(void)
+{
+       platform_driver_unregister(&omap4_keypad_driver);
+}
+module_exit(omap4_keypad_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("OMAP4 Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap4-keypad");
index fb16b5e5ea13fd64a461577778bec1d3e9c04343..09bef79d9da1c6e3aecd6efe40c0d4c4c900ba0b 100644 (file)
@@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
        if (error) {
                dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
                        kp->irq);
-               goto err3;
+               goto err2;
        }
 
        /* Enable KP and TO interrupts now. */
        reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
        if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
                error = -EIO;
-               goto err4;
+               goto err3;
        }
 
        platform_set_drvdata(pdev, kp);
        return 0;
 
-err4:
+err3:
        /* mask all events - we don't care about the result */
        (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
-err3:
        free_irq(kp->irq, NULL);
 err2:
        input_unregister_device(input);
index b49e233797235e8169ee999f4b2305fa301e327d..b99b8cbde02f531ba08fbcfa2f8b10f4d52e0c02 100644 (file)
@@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
          To compile this driver as a module, choose M here: the module
          will be called 88pm860x_onkey.
 
+config INPUT_AB8500_PONKEY
+       tristate "AB8500 Pon (PowerOn) Key"
+       depends on AB8500_CORE
+       help
+         Say Y here to use the PowerOn Key for ST-Ericsson's AB8500
+         Mix-Sig PMIC.
+
+         To compile this driver as a module, choose M here: the module
+         will be called ab8500-ponkey.
+
 config INPUT_AD714X
        tristate "Analog Devices AD714x Capacitance Touch Sensor"
        help
index 19ccca78fa766d2ea2bb24f78201a6b6d9ec770c..1fe1f6c8b7379afc6b70807be2f2428833122611 100644 (file)
@@ -5,6 +5,7 @@
 # Each configuration option enables a list of files.
 
 obj-$(CONFIG_INPUT_88PM860X_ONKEY)     += 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_AB8500_PONKEY)      += ab8500-ponkey.o
 obj-$(CONFIG_INPUT_AD714X)             += ad714x.o
 obj-$(CONFIG_INPUT_AD714X_I2C)         += ad714x-i2c.o
 obj-$(CONFIG_INPUT_AD714X_SPI)         += ad714x-spi.o
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
new file mode 100644 (file)
index 0000000..3d3288a
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 Power-On Key handler
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/ab8500.h>
+#include <linux/slab.h>
+
+/**
+ * struct ab8500_ponkey - ab8500 ponkey information
+ * @input_dev: pointer to input device
+ * @ab8500: ab8500 parent
+ * @irq_dbf: irq number for falling transition
+ * @irq_dbr: irq number for rising transition
+ */
+struct ab8500_ponkey {
+       struct input_dev        *idev;
+       struct ab8500           *ab8500;
+       int                     irq_dbf;
+       int                     irq_dbr;
+};
+
+/* AB8500 gives us an interrupt when ONKEY is held */
+static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
+{
+       struct ab8500_ponkey *ponkey = data;
+
+       if (irq == ponkey->irq_dbf)
+               input_report_key(ponkey->idev, KEY_POWER, true);
+       else if (irq == ponkey->irq_dbr)
+               input_report_key(ponkey->idev, KEY_POWER, false);
+
+       input_sync(ponkey->idev);
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
+{
+       struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+       struct ab8500_ponkey *ponkey;
+       struct input_dev *input;
+       int irq_dbf, irq_dbr;
+       int error;
+
+       irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
+       if (irq_dbf < 0) {
+               dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
+               return irq_dbf;
+       }
+
+       irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
+       if (irq_dbr < 0) {
+               dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
+               return irq_dbr;
+       }
+
+       ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL);
+       input = input_allocate_device();
+       if (!ponkey || !input) {
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       ponkey->idev = input;
+       ponkey->ab8500 = ab8500;
+       ponkey->irq_dbf = irq_dbf;
+       ponkey->irq_dbr = irq_dbr;
+
+       input->name = "AB8500 POn(PowerOn) Key";
+       input->dev.parent = &pdev->dev;
+
+       input_set_capability(input, EV_KEY, KEY_POWER);
+
+       error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler,
+                                       0, "ab8500-ponkey-dbf", ponkey);
+       if (error < 0) {
+               dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n",
+                       ponkey->irq_dbf, error);
+               goto err_free_mem;
+       }
+
+       error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler,
+                                       0, "ab8500-ponkey-dbr", ponkey);
+       if (error < 0) {
+               dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n",
+                       ponkey->irq_dbr, error);
+               goto err_free_dbf_irq;
+       }
+
+       error = input_register_device(ponkey->idev);
+       if (error) {
+               dev_err(ab8500->dev, "Can't register input device: %d\n", error);
+               goto err_free_dbr_irq;
+       }
+
+       platform_set_drvdata(pdev, ponkey);
+       return 0;
+
+err_free_dbr_irq:
+       free_irq(ponkey->irq_dbr, ponkey);
+err_free_dbf_irq:
+       free_irq(ponkey->irq_dbf, ponkey);
+err_free_mem:
+       input_free_device(input);
+       kfree(ponkey);
+
+       return error;
+}
+
+static int __devexit ab8500_ponkey_remove(struct platform_device *pdev)
+{
+       struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev);
+
+       free_irq(ponkey->irq_dbf, ponkey);
+       free_irq(ponkey->irq_dbr, ponkey);
+       input_unregister_device(ponkey->idev);
+       kfree(ponkey);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver ab8500_ponkey_driver = {
+       .driver         = {
+               .name   = "ab8500-poweron-key",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ab8500_ponkey_probe,
+       .remove         = __devexit_p(ab8500_ponkey_remove),
+};
+
+static int __init ab8500_ponkey_init(void)
+{
+       return platform_driver_register(&ab8500_ponkey_driver);
+}
+module_init(ab8500_ponkey_init);
+
+static void __exit ab8500_ponkey_exit(void)
+{
+       platform_driver_unregister(&ab8500_ponkey_driver);
+}
+module_exit(ab8500_ponkey_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
index 23257652b8e8b49f8ed85d8e2ca1f259b35f8453..0b0e9be6354215319200a8a305c9db55e8b4c3dd 100644 (file)
@@ -483,51 +483,88 @@ static void ati_remote2_complete_key(struct urb *urb)
 }
 
 static int ati_remote2_getkeycode(struct input_dev *idev,
-                                 unsigned int scancode, unsigned int *keycode)
+                                 struct input_keymap_entry *ke)
 {
        struct ati_remote2 *ar2 = input_get_drvdata(idev);
        unsigned int mode;
-       int index;
+       int offset;
+       unsigned int index;
+       unsigned int scancode;
+
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+               index = ke->index;
+               if (index >= ATI_REMOTE2_MODES *
+                               ARRAY_SIZE(ati_remote2_key_table))
+                       return -EINVAL;
+
+               mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+               offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+               scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code;
+       } else {
+               if (input_scancode_to_scalar(ke, &scancode))
+                       return -EINVAL;
+
+               mode = scancode >> 8;
+               if (mode > ATI_REMOTE2_PC)
+                       return -EINVAL;
+
+               offset = ati_remote2_lookup(scancode & 0xff);
+               if (offset < 0)
+                       return -EINVAL;
+
+               index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset;
+       }
 
-       mode = scancode >> 8;
-       if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
-               return -EINVAL;
+       ke->keycode = ar2->keycode[mode][offset];
+       ke->len = sizeof(scancode);
+       memcpy(&ke->scancode, &scancode, sizeof(scancode));
+       ke->index = index;
 
-       index = ati_remote2_lookup(scancode & 0xFF);
-       if (index < 0)
-               return -EINVAL;
-
-       *keycode = ar2->keycode[mode][index];
        return 0;
 }
 
 static int ati_remote2_setkeycode(struct input_dev *idev,
-                                 unsigned int scancode, unsigned int keycode)
+                                 const struct input_keymap_entry *ke,
+                                 unsigned int *old_keycode)
 {
        struct ati_remote2 *ar2 = input_get_drvdata(idev);
-       unsigned int mode, old_keycode;
-       int index;
-
-       mode = scancode >> 8;
-       if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
-               return -EINVAL;
-
-       index = ati_remote2_lookup(scancode & 0xFF);
-       if (index < 0)
-               return -EINVAL;
+       unsigned int mode;
+       int offset;
+       unsigned int index;
+       unsigned int scancode;
+
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+               if (ke->index >= ATI_REMOTE2_MODES *
+                               ARRAY_SIZE(ati_remote2_key_table))
+                       return -EINVAL;
+
+               mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+               offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+       } else {
+               if (input_scancode_to_scalar(ke, &scancode))
+                       return -EINVAL;
+
+               mode = scancode >> 8;
+               if (mode > ATI_REMOTE2_PC)
+                       return -EINVAL;
+
+               offset = ati_remote2_lookup(scancode & 0xff);
+               if (offset < 0)
+                       return -EINVAL;
+       }
 
-       old_keycode = ar2->keycode[mode][index];
-       ar2->keycode[mode][index] = keycode;
-       __set_bit(keycode, idev->keybit);
+       *old_keycode = ar2->keycode[mode][offset];
+       ar2->keycode[mode][offset] = ke->keycode;
+       __set_bit(ke->keycode, idev->keybit);
 
        for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
                for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
-                       if (ar2->keycode[mode][index] == old_keycode)
+                       if (ar2->keycode[mode][index] == *old_keycode)
                                return 0;
                }
        }
 
-       __clear_bit(old_keycode, idev->keybit);
+       __clear_bit(*old_keycode, idev->keybit);
 
        return 0;
 }
@@ -575,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
        idev->open = ati_remote2_open;
        idev->close = ati_remote2_close;
 
-       idev->getkeycode = ati_remote2_getkeycode;
-       idev->setkeycode = ati_remote2_setkeycode;
+       idev->getkeycode_new = ati_remote2_getkeycode;
+       idev->setkeycode_new = ati_remote2_setkeycode;
 
        idev->name = ar2->name;
        idev->phys = ar2->phys;
index bf170f6b4422676889658959af884834d8e81efb..f45947190e4fcb07668adeec9734ea28418df481 100644 (file)
@@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev
 
        pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL);
        if (!pm->configcr)
-               return -1;
+               return -ENOMEM;
 
        return 0;
 }
index 48311204ba51a76fbc90f7f3194e6a2f79e5a403..04d9bf320a4f9c090b8d4f46aeca8e848bc4f988 100644 (file)
@@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse)
 
        psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
        if (!etd)
-               return -1;
+               return -ENOMEM;
 
        etd->parity[0] = 1;
        for (i = 1; i < 256; i++)
index 73a7af2542a8c7a32c1815e72df9fb10dac3ee1c..cd9d0c97e429622ca5d7fd21c69e9e0be14156ec 100644 (file)
@@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
        if (!new_dev)
                return -ENOMEM;
 
-       while (serio->child) {
+       while (!list_empty(&serio->children)) {
                if (++retry > 3) {
                        printk(KERN_WARNING
-                               "psmouse: failed to destroy child port, "
+                               "psmouse: failed to destroy children ports, "
                                "protocol change aborted.\n");
                        input_free_device(new_dev);
                        return -EIO;
index 96b70a43515f034ff5c1c02b8ec7a35674afb394..2e300a460556573ec20f85ac536f63f6d0bed438 100644 (file)
@@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c)
        return 0;
 }
 
-static inline int synaptics_is_pt_packet(unsigned char *buf)
+static int synaptics_pt_start(struct serio *serio)
+{
+       struct psmouse *parent = serio_get_drvdata(serio->parent);
+       struct synaptics_data *priv = parent->private;
+
+       serio_pause_rx(parent->ps2dev.serio);
+       priv->pt_port = serio;
+       serio_continue_rx(parent->ps2dev.serio);
+
+       return 0;
+}
+
+static void synaptics_pt_stop(struct serio *serio)
+{
+       struct psmouse *parent = serio_get_drvdata(serio->parent);
+       struct synaptics_data *priv = parent->private;
+
+       serio_pause_rx(parent->ps2dev.serio);
+       priv->pt_port = NULL;
+       serio_continue_rx(parent->ps2dev.serio);
+}
+
+static int synaptics_is_pt_packet(unsigned char *buf)
 {
        return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
 }
@@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
 
 static void synaptics_pt_activate(struct psmouse *psmouse)
 {
-       struct serio *ptport = psmouse->ps2dev.serio->child;
-       struct psmouse *child = serio_get_drvdata(ptport);
        struct synaptics_data *priv = psmouse->private;
+       struct psmouse *child = serio_get_drvdata(priv->pt_port);
 
        /* adjust the touchpad to child's choice of protocol */
        if (child) {
@@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
        strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
        strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
        serio->write = synaptics_pt_write;
+       serio->start = synaptics_pt_start;
+       serio->stop = synaptics_pt_stop;
        serio->parent = psmouse->ps2dev.serio;
 
        psmouse->pt_activate = synaptics_pt_activate;
@@ -578,9 +601,10 @@ 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) && synaptics_is_pt_packet(psmouse->packet)) {
-                       if (psmouse->ps2dev.serio->child)
-                               synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
+               if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
+                   synaptics_is_pt_packet(psmouse->packet)) {
+                       if (priv->pt_port)
+                               synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
                } else
                        synaptics_process_packet(psmouse);
 
@@ -731,7 +755,7 @@ int synaptics_init(struct psmouse *psmouse)
 
        psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
        if (!priv)
-               return -1;
+               return -ENOMEM;
 
        psmouse_reset(psmouse);
 
index b6aa7d20d8a35c0c2eb3f313c388bd80b8638f1e..613a3652f98f5f787fa619807f10de67aa8634c5 100644 (file)
@@ -110,6 +110,8 @@ struct synaptics_data {
        unsigned char pkt_type;                 /* packet type - old, new, etc */
        unsigned char mode;                     /* current mode byte */
        int scroll;
+
+       struct serio *pt_port;                  /* Pass-through serio port */
 };
 
 void synaptics_module_init(void);
index 0643e49ca6039ea239e777c0b7f695040fed53bb..54b2fa892e1941b76d3d863013b85a4884595770 100644 (file)
@@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
 
        psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
        if (!psmouse->private)
-               return -1;
+               return -ENOMEM;
 
        psmouse->vendor = "IBM";
        psmouse->name = "TrackPoint";
index 31ec7265aac63806060a665278afc70d6e18946f..2a00ddf4f23a3b469c621be77b08c4d6b943c428 100644 (file)
@@ -867,7 +867,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
        spin_lock_init(&mousedev->client_lock);
        mutex_init(&mousedev->mutex);
        lockdep_set_subclass(&mousedev->mutex,
-                            minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
+                            minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0);
        init_waitqueue_head(&mousedev->wait);
 
        if (minor == MOUSEDEV_MIX)
index 3bfe8fafc6adac421577bc17c4da964537a4145f..6256233d2bfb12eef200e310da75f6a4e6468afc 100644 (file)
@@ -226,4 +226,13 @@ config SERIO_AMS_DELTA
          To compile this driver as a module, choose M here;
          the module will be called ams_delta_serio.
 
+config SERIO_PS2MULT
+       tristate "TQC PS/2 multiplexer"
+       help
+         Say Y here if you have the PS/2 line multiplexer like the one
+         present on TQC boads.
+
+         To compile this driver as a module, choose M here: the
+         module will be called ps2mult.
+
 endif
index 84c80bf7185e6b58e992080d952ca2ff6afdc8c4..dbbe37616c92ece327bada8c39cde862aff6e196 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2)    += gscps2.o
 obj-$(CONFIG_HP_SDC)           += hp_sdc.o
 obj-$(CONFIG_HIL_MLC)          += hp_sdc_mlc.o hil_mlc.o
 obj-$(CONFIG_SERIO_PCIPS2)     += pcips2.o
+obj-$(CONFIG_SERIO_PS2MULT)    += ps2mult.o
 obj-$(CONFIG_SERIO_MACEPS2)    += maceps2.o
 obj-$(CONFIG_SERIO_LIBPS2)     += libps2.o
 obj-$(CONFIG_SERIO_RAW)                += serio_raw.o
index f585131604806f531f91c48ebb867919339fd80f..18db5a8c747830516ded46abd66bb822edca2ab5 100644 (file)
@@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state)
 #ifdef CONFIG_X86
 static void i8042_dritek_enable(void)
 {
-       char param = 0x90;
+       unsigned char param = 0x90;
        int error;
 
        error = i8042_command(&param, 0x1059);
diff --git a/drivers/input/serio/ps2mult.c b/drivers/input/serio/ps2mult.c
new file mode 100644 (file)
index 0000000..6bce22e
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * TQC PS/2 Multiplexer driver
+ *
+ * Copyright (C) 2010 Dmitry Eremin-Solenikov
+ *
+ * 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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/serio.h>
+
+MODULE_AUTHOR("Dmitry Eremin-Solenikov <dbaryshkov@gmail.com>");
+MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
+MODULE_LICENSE("GPL");
+
+#define PS2MULT_KB_SELECTOR            0xA0
+#define PS2MULT_MS_SELECTOR            0xA1
+#define PS2MULT_ESCAPE                 0x7D
+#define PS2MULT_BSYNC                  0x7E
+#define PS2MULT_SESSION_START          0x55
+#define PS2MULT_SESSION_END            0x56
+
+struct ps2mult_port {
+       struct serio *serio;
+       unsigned char sel;
+       bool registered;
+};
+
+#define PS2MULT_NUM_PORTS      2
+#define PS2MULT_KBD_PORT       0
+#define PS2MULT_MOUSE_PORT     1
+
+struct ps2mult {
+       struct serio *mx_serio;
+       struct ps2mult_port ports[PS2MULT_NUM_PORTS];
+
+       spinlock_t lock;
+       struct ps2mult_port *in_port;
+       struct ps2mult_port *out_port;
+       bool escape;
+};
+
+/* First MUST come PS2MULT_NUM_PORTS selectors */
+static const unsigned char ps2mult_controls[] = {
+       PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
+       PS2MULT_ESCAPE, PS2MULT_BSYNC,
+       PS2MULT_SESSION_START, PS2MULT_SESSION_END,
+};
+
+static const struct serio_device_id ps2mult_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_PS2MULT,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
+
+static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
+{
+       struct serio *mx_serio = psm->mx_serio;
+
+       serio_write(mx_serio, port->sel);
+       psm->out_port = port;
+       dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
+}
+
+static int ps2mult_serio_write(struct serio *serio, unsigned char data)
+{
+       struct serio *mx_port = serio->parent;
+       struct ps2mult *psm = serio_get_drvdata(mx_port);
+       struct ps2mult_port *port = serio->port_data;
+       bool need_escape;
+       unsigned long flags;
+
+       spin_lock_irqsave(&psm->lock, flags);
+
+       if (psm->out_port != port)
+               ps2mult_select_port(psm, port);
+
+       need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
+
+       dev_dbg(&serio->dev,
+               "write: %s%02x\n", need_escape ? "ESC " : "", data);
+
+       if (need_escape)
+               serio_write(mx_port, PS2MULT_ESCAPE);
+
+       serio_write(mx_port, data);
+
+       spin_unlock_irqrestore(&psm->lock, flags);
+
+       return 0;
+}
+
+static int ps2mult_serio_start(struct serio *serio)
+{
+       struct ps2mult *psm = serio_get_drvdata(serio->parent);
+       struct ps2mult_port *port = serio->port_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&psm->lock, flags);
+       port->registered = true;
+       spin_unlock_irqrestore(&psm->lock, flags);
+
+       return 0;
+}
+
+static void ps2mult_serio_stop(struct serio *serio)
+{
+       struct ps2mult *psm = serio_get_drvdata(serio->parent);
+       struct ps2mult_port *port = serio->port_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&psm->lock, flags);
+       port->registered = false;
+       spin_unlock_irqrestore(&psm->lock, flags);
+}
+
+static int ps2mult_create_port(struct ps2mult *psm, int i)
+{
+       struct serio *mx_serio = psm->mx_serio;
+       struct serio *serio;
+
+       serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!serio)
+               return -ENOMEM;
+
+       strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
+       snprintf(serio->phys, sizeof(serio->phys),
+                "%s/port%d", mx_serio->phys, i);
+       serio->id.type = SERIO_8042;
+       serio->write = ps2mult_serio_write;
+       serio->start = ps2mult_serio_start;
+       serio->stop = ps2mult_serio_stop;
+       serio->parent = psm->mx_serio;
+       serio->port_data = &psm->ports[i];
+
+       psm->ports[i].serio = serio;
+
+       return 0;
+}
+
+static void ps2mult_reset(struct ps2mult *psm)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&psm->lock, flags);
+
+       serio_write(psm->mx_serio, PS2MULT_SESSION_END);
+       serio_write(psm->mx_serio, PS2MULT_SESSION_START);
+
+       ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
+
+       spin_unlock_irqrestore(&psm->lock, flags);
+}
+
+static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct ps2mult *psm;
+       int i;
+       int error;
+
+       if (!serio->write)
+               return -EINVAL;
+
+       psm = kzalloc(sizeof(*psm), GFP_KERNEL);
+       if (!psm)
+               return -ENOMEM;
+
+       spin_lock_init(&psm->lock);
+       psm->mx_serio = serio;
+
+       for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
+               psm->ports[i].sel = ps2mult_controls[i];
+               error = ps2mult_create_port(psm, i);
+               if (error)
+                       goto err_out;
+       }
+
+       psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
+
+       serio_set_drvdata(serio, psm);
+       error = serio_open(serio, drv);
+       if (error)
+               goto err_out;
+
+       ps2mult_reset(psm);
+
+       for (i = 0; i <  PS2MULT_NUM_PORTS; i++) {
+               struct serio *s = psm->ports[i].serio;
+
+               dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
+               serio_register_port(s);
+       }
+
+       return 0;
+
+err_out:
+       while (--i >= 0)
+               kfree(psm->ports[i].serio);
+       kfree(serio);
+       return error;
+}
+
+static void ps2mult_disconnect(struct serio *serio)
+{
+       struct ps2mult *psm = serio_get_drvdata(serio);
+
+       /* Note that serio core already take care of children ports */
+       serio_write(serio, PS2MULT_SESSION_END);
+       serio_close(serio);
+       kfree(psm);
+
+       serio_set_drvdata(serio, NULL);
+}
+
+static int ps2mult_reconnect(struct serio *serio)
+{
+       struct ps2mult *psm = serio_get_drvdata(serio);
+
+       ps2mult_reset(psm);
+
+       return 0;
+}
+
+static irqreturn_t ps2mult_interrupt(struct serio *serio,
+                                    unsigned char data, unsigned int dfl)
+{
+       struct ps2mult *psm = serio_get_drvdata(serio);
+       struct ps2mult_port *in_port;
+       unsigned long flags;
+
+       dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
+
+       spin_lock_irqsave(&psm->lock, flags);
+
+       if (psm->escape) {
+               psm->escape = false;
+               in_port = psm->in_port;
+               if (in_port->registered)
+                       serio_interrupt(in_port->serio, data, dfl);
+               goto out;
+       }
+
+       switch (data) {
+       case PS2MULT_ESCAPE:
+               dev_dbg(&serio->dev, "ESCAPE\n");
+               psm->escape = true;
+               break;
+
+       case PS2MULT_BSYNC:
+               dev_dbg(&serio->dev, "BSYNC\n");
+               psm->in_port = psm->out_port;
+               break;
+
+       case PS2MULT_SESSION_START:
+               dev_dbg(&serio->dev, "SS\n");
+               break;
+
+       case PS2MULT_SESSION_END:
+               dev_dbg(&serio->dev, "SE\n");
+               break;
+
+       case PS2MULT_KB_SELECTOR:
+               dev_dbg(&serio->dev, "KB\n");
+               psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
+               break;
+
+       case PS2MULT_MS_SELECTOR:
+               dev_dbg(&serio->dev, "MS\n");
+               psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
+               break;
+
+       default:
+               in_port = psm->in_port;
+               if (in_port->registered)
+                       serio_interrupt(in_port->serio, data, dfl);
+               break;
+       }
+
+ out:
+       spin_unlock_irqrestore(&psm->lock, flags);
+       return IRQ_HANDLED;
+}
+
+static struct serio_driver ps2mult_drv = {
+       .driver         = {
+               .name   = "ps2mult",
+       },
+       .description    = "TQC PS/2 Multiplexer driver",
+       .id_table       = ps2mult_serio_ids,
+       .interrupt      = ps2mult_interrupt,
+       .connect        = ps2mult_connect,
+       .disconnect     = ps2mult_disconnect,
+       .reconnect      = ps2mult_reconnect,
+};
+
+static int __init ps2mult_init(void)
+{
+       return serio_register_driver(&ps2mult_drv);
+}
+
+static void __exit ps2mult_exit(void)
+{
+       serio_unregister_driver(&ps2mult_drv);
+}
+
+module_init(ps2mult_init);
+module_exit(ps2mult_exit);
index c3b626e9eae7c075cd7597779ddbef4becb731f6..405bf214527c33f08c40c04d60c5e863826dbcc7 100644 (file)
@@ -37,7 +37,6 @@
 #include <linux/slab.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
-#include <linux/freezer.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Serio abstraction core");
@@ -56,7 +55,7 @@ static struct bus_type serio_bus;
 static void serio_add_port(struct serio *serio);
 static int serio_reconnect_port(struct serio *serio);
 static void serio_disconnect_port(struct serio *serio);
-static void serio_reconnect_chain(struct serio *serio);
+static void serio_reconnect_subtree(struct serio *serio);
 static void serio_attach_driver(struct serio_driver *drv);
 
 static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
@@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio)
 enum serio_event_type {
        SERIO_RESCAN_PORT,
        SERIO_RECONNECT_PORT,
-       SERIO_RECONNECT_CHAIN,
+       SERIO_RECONNECT_SUBTREE,
        SERIO_REGISTER_PORT,
        SERIO_ATTACH_DRIVER,
 };
@@ -292,8 +291,8 @@ static void serio_handle_event(void)
                        serio_find_driver(event->object);
                        break;
 
-               case SERIO_RECONNECT_CHAIN:
-                       serio_reconnect_chain(event->object);
+               case SERIO_RECONNECT_SUBTREE:
+                       serio_reconnect_subtree(event->object);
                        break;
 
                case SERIO_ATTACH_DRIVER:
@@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object)
 }
 
 /*
- * Destroy child serio port (if any) that has not been fully registered yet.
+ * Locate child serio port (if any) that has not been fully registered yet.
  *
- * Note that we rely on the fact that port can have only one child and therefore
- * only one child registration request can be pending. Additionally, children
- * are registered by driver's connect() handler so there can't be a grandchild
- * pending registration together with a child.
+ * Children are registered by driver's connect() handler so there can't be a
+ * grandchild pending registration together with a child.
  */
 static struct serio *serio_get_pending_child(struct serio *parent)
 {
@@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
        if (!strncmp(buf, "none", count)) {
                serio_disconnect_port(serio);
        } else if (!strncmp(buf, "reconnect", count)) {
-               serio_reconnect_chain(serio);
+               serio_reconnect_subtree(serio);
        } else if (!strncmp(buf, "rescan", count)) {
                serio_disconnect_port(serio);
                serio_find_driver(serio);
@@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio)
        __module_get(THIS_MODULE);
 
        INIT_LIST_HEAD(&serio->node);
+       INIT_LIST_HEAD(&serio->child_node);
+       INIT_LIST_HEAD(&serio->children);
        spin_lock_init(&serio->lock);
        mutex_init(&serio->drv_mutex);
        device_initialize(&serio->dev);
@@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio)
  */
 static void serio_add_port(struct serio *serio)
 {
+       struct serio *parent = serio->parent;
        int error;
 
-       if (serio->parent) {
-               serio_pause_rx(serio->parent);
-               serio->parent->child = serio;
-               serio_continue_rx(serio->parent);
+       if (parent) {
+               serio_pause_rx(parent);
+               list_add_tail(&serio->child_node, &parent->children);
+               serio_continue_rx(parent);
        }
 
        list_add_tail(&serio->node, &serio_list);
@@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio)
 }
 
 /*
- * serio_destroy_port() completes deregistration process and removes
+ * serio_destroy_port() completes unregistration process and removes
  * port from the system
  */
 static void serio_destroy_port(struct serio *serio)
 {
        struct serio *child;
 
-       child = serio_get_pending_child(serio);
-       if (child) {
+       while ((child = serio_get_pending_child(serio)) != NULL) {
                serio_remove_pending_events(child);
                put_device(&child->dev);
        }
@@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio)
 
        if (serio->parent) {
                serio_pause_rx(serio->parent);
-               serio->parent->child = NULL;
+               list_del_init(&serio->child_node);
                serio_continue_rx(serio->parent);
                serio->parent = NULL;
        }
@@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio)
 }
 
 /*
- * Reconnect serio port and all its children (re-initialize attached devices)
+ * Reconnect serio port and all its children (re-initialize attached
+ * devices).
  */
-static void serio_reconnect_chain(struct serio *serio)
+static void serio_reconnect_subtree(struct serio *root)
 {
+       struct serio *s = root;
+       int error;
+
        do {
-               if (serio_reconnect_port(serio)) {
-                       /* Ok, old children are now gone, we are done */
-                       break;
+               error = serio_reconnect_port(s);
+               if (!error) {
+                       /*
+                        * Reconnect was successful, move on to do the
+                        * first child.
+                        */
+                       if (!list_empty(&s->children)) {
+                               s = list_first_entry(&s->children,
+                                                    struct serio, child_node);
+                               continue;
+                       }
                }
-               serio = serio->child;
-       } while (serio);
+
+               /*
+                * Either it was a leaf node or reconnect failed and it
+                * became a leaf node. Continue reconnecting starting with
+                * the next sibling of the parent node.
+                */
+               while (s != root) {
+                       struct serio *parent = s->parent;
+
+                       if (!list_is_last(&s->child_node, &parent->children)) {
+                               s = list_entry(s->child_node.next,
+                                              struct serio, child_node);
+                               break;
+                       }
+
+                       s = parent;
+               }
+       } while (s != root);
 }
 
 /*
  * serio_disconnect_port() unbinds a port from its driver. As a side effect
- * all child ports are unbound and destroyed.
+ * all children ports are unbound and destroyed.
  */
 static void serio_disconnect_port(struct serio *serio)
 {
-       struct serio *s, *parent;
+       struct serio *s = serio;
+
+       /*
+        * Children ports should be disconnected and destroyed
+        * first; we travel the tree in depth-first order.
+        */
+       while (!list_empty(&serio->children)) {
+
+               /* Locate a leaf */
+               while (!list_empty(&s->children))
+                       s = list_first_entry(&s->children,
+                                            struct serio, child_node);
 
-       if (serio->child) {
                /*
-                * Children ports should be disconnected and destroyed
-                * first, staring with the leaf one, since we don't want
-                * to do recursion
+                * Prune this leaf node unless it is the one we
+                * started with.
                 */
-               for (s = serio; s->child; s = s->child)
-                       /* empty */;
-
-               do {
-                       parent = s->parent;
+               if (s != serio) {
+                       struct serio *parent = s->parent;
 
                        device_release_driver(&s->dev);
                        serio_destroy_port(s);
-               } while ((s = parent) != serio);
+
+                       s = parent;
+               }
        }
 
        /*
-        * Ok, no children left, now disconnect this port
+        * OK, no children left, now disconnect this port.
         */
        device_release_driver(&serio->dev);
 }
@@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan);
 
 void serio_reconnect(struct serio *serio)
 {
-       serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
+       serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
 }
 EXPORT_SYMBOL(serio_reconnect);
 
@@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio)
 EXPORT_SYMBOL(serio_unregister_port);
 
 /*
- * Safely unregisters child port if one is present.
+ * Safely unregisters children ports if they are present.
  */
 void serio_unregister_child_port(struct serio *serio)
 {
+       struct serio *s, *next;
+
        mutex_lock(&serio_mutex);
-       if (serio->child) {
-               serio_disconnect_port(serio->child);
-               serio_destroy_port(serio->child);
+       list_for_each_entry_safe(s, next, &serio->children, child_node) {
+               serio_disconnect_port(s);
+               serio_destroy_port(s);
        }
        mutex_unlock(&serio_mutex);
 }
index 014248344763775b49285db0a480e794f5c76c2e..a29a7812bd46cea26613b813a3091eed476077aa 100644 (file)
@@ -22,6 +22,37 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps");
 MODULE_LICENSE("GPL v2");
 MODULE_VERSION("0.1");
 
+static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
+                                               const struct key_entry *k)
+{
+       struct key_entry *key;
+       unsigned int idx = 0;
+
+       for (key = dev->keycode; key->type != KE_END; key++) {
+               if (key->type == KE_KEY) {
+                       if (key == k)
+                               break;
+                       idx++;
+               }
+       }
+
+       return idx;
+}
+
+static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
+                                                     unsigned int index)
+{
+       struct key_entry *key;
+       unsigned int key_cnt = 0;
+
+       for (key = dev->keycode; key->type != KE_END; key++)
+               if (key->type == KE_KEY)
+                       if (key_cnt++ == index)
+                               return key;
+
+       return NULL;
+}
+
 /**
  * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
  * @dev: Input device using sparse keymap
@@ -64,16 +95,36 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
 }
 EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
 
+static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
+                                       const struct input_keymap_entry *ke)
+{
+       struct key_entry *key;
+       unsigned int scancode;
+
+       if (ke->flags & INPUT_KEYMAP_BY_INDEX)
+               key = sparse_keymap_entry_by_index(dev, ke->index);
+       else if (input_scancode_to_scalar(ke, &scancode) == 0)
+               key = sparse_keymap_entry_from_scancode(dev, scancode);
+       else
+               key = NULL;
+
+       return key;
+}
+
 static int sparse_keymap_getkeycode(struct input_dev *dev,
-                                   unsigned int scancode,
-                                   unsigned int *keycode)
+                                   struct input_keymap_entry *ke)
 {
        const struct key_entry *key;
 
        if (dev->keycode) {
-               key = sparse_keymap_entry_from_scancode(dev, scancode);
+               key = sparse_keymap_locate(dev, ke);
                if (key && key->type == KE_KEY) {
-                       *keycode = key->keycode;
+                       ke->keycode = key->keycode;
+                       if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
+                               ke->index =
+                                       sparse_keymap_get_key_index(dev, key);
+                       ke->len = sizeof(key->code);
+                       memcpy(ke->scancode, &key->code, sizeof(key->code));
                        return 0;
                }
        }
@@ -82,20 +133,19 @@ static int sparse_keymap_getkeycode(struct input_dev *dev,
 }
 
 static int sparse_keymap_setkeycode(struct input_dev *dev,
-                                   unsigned int scancode,
-                                   unsigned int keycode)
+                                   const struct input_keymap_entry *ke,
+                                   unsigned int *old_keycode)
 {
        struct key_entry *key;
-       int old_keycode;
 
        if (dev->keycode) {
-               key = sparse_keymap_entry_from_scancode(dev, scancode);
+               key = sparse_keymap_locate(dev, ke);
                if (key && key->type == KE_KEY) {
-                       old_keycode = key->keycode;
-                       key->keycode = keycode;
-                       set_bit(keycode, dev->keybit);
-                       if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
-                               clear_bit(old_keycode, dev->keybit);
+                       *old_keycode = key->keycode;
+                       key->keycode = ke->keycode;
+                       set_bit(ke->keycode, dev->keybit);
+                       if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
+                               clear_bit(*old_keycode, dev->keybit);
                        return 0;
                }
        }
@@ -159,15 +209,14 @@ int sparse_keymap_setup(struct input_dev *dev,
 
        dev->keycode = map;
        dev->keycodemax = map_size;
-       dev->getkeycode = sparse_keymap_getkeycode;
-       dev->setkeycode = sparse_keymap_setkeycode;
+       dev->getkeycode_new = sparse_keymap_getkeycode;
+       dev->setkeycode_new = sparse_keymap_setkeycode;
 
        return 0;
 
  err_out:
        kfree(map);
        return error;
-
 }
 EXPORT_SYMBOL(sparse_keymap_setup);
 
index effb49ea24aa4c6ec337e14cfa4336e4fd153345..58a87755b936eee386811396b9ba0ae418a3e0ea 100644 (file)
@@ -49,6 +49,17 @@ config TABLET_USB_GTCO
           To compile this driver as a module, choose M here: the
           module will be called gtco.
 
+config TABLET_USB_HANWANG
+       tristate "Hanwang Art Master III tablet support (USB)"
+       depends on USB_ARCH_HAS_HCD
+       select USB
+       help
+         Say Y here if you want to use the USB version of the Hanwang Art
+         Master III tablet.
+
+         To compile this driver as a module, choose M here: the
+         module will be called hanwang.
+
 config TABLET_USB_KBTAB
        tristate "KB Gear JamStudio tablet support (USB)"
        depends on USB_ARCH_HAS_HCD
index ce8b9a9cfa40b2782cea9946fab26d039883a521..3f6c25220638c179de271d1145f25054a3fb137d 100644 (file)
@@ -8,5 +8,6 @@ wacom-objs      := wacom_wac.o wacom_sys.o
 obj-$(CONFIG_TABLET_USB_ACECAD)        += acecad.o
 obj-$(CONFIG_TABLET_USB_AIPTEK)        += aiptek.o
 obj-$(CONFIG_TABLET_USB_GTCO)  += gtco.o
+obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
 obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
 obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
diff --git a/drivers/input/tablet/hanwang.c b/drivers/input/tablet/hanwang.c
new file mode 100644 (file)
index 0000000..6504b62
--- /dev/null
@@ -0,0 +1,446 @@
+/*
+ *  USB Hanwang tablet support
+ *
+ *  Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
+ *
+ */
+
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_AUTHOR   "Xing Wei <weixing@hanwang.com.cn>"
+#define DRIVER_DESC     "USB Hanwang tablet driver"
+#define DRIVER_LICENSE  "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_HANWANG          0x0b57
+#define HANWANG_TABLET_INT_CLASS       0x0003
+#define HANWANG_TABLET_INT_SUB_CLASS   0x0001
+#define HANWANG_TABLET_INT_PROTOCOL    0x0002
+
+#define ART_MASTER_PKGLEN_MAX  10
+
+/* device IDs */
+#define STYLUS_DEVICE_ID       0x02
+#define TOUCH_DEVICE_ID                0x03
+#define CURSOR_DEVICE_ID       0x06
+#define ERASER_DEVICE_ID       0x0A
+#define PAD_DEVICE_ID          0x0F
+
+/* match vendor and interface info  */
+#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \
+       .match_flags = USB_DEVICE_ID_MATCH_VENDOR \
+               | USB_DEVICE_ID_MATCH_INT_INFO, \
+       .idVendor = (vend), \
+       .bInterfaceClass = (cl), \
+       .bInterfaceSubClass = (sc), \
+       .bInterfaceProtocol = (pr)
+
+enum hanwang_tablet_type {
+       HANWANG_ART_MASTER_III,
+       HANWANG_ART_MASTER_HD,
+};
+
+struct hanwang {
+       unsigned char *data;
+       dma_addr_t data_dma;
+       struct input_dev *dev;
+       struct usb_device *usbdev;
+       struct urb *irq;
+       const struct hanwang_features *features;
+       unsigned int current_tool;
+       unsigned int current_id;
+       char name[64];
+       char phys[32];
+};
+
+struct hanwang_features {
+       unsigned short pid;
+       char *name;
+       enum hanwang_tablet_type type;
+       int pkg_len;
+       int max_x;
+       int max_y;
+       int max_tilt_x;
+       int max_tilt_y;
+       int max_pressure;
+};
+
+static const struct hanwang_features features_array[] = {
+       { 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III,
+         ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 },
+       { 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III,
+         ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 },
+       { 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III,
+         ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 },
+       { 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD,
+         ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 },
+};
+
+static const int hw_eventtypes[] = {
+       EV_KEY, EV_ABS, EV_MSC,
+};
+
+static const int hw_absevents[] = {
+       ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL,
+       ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC,
+};
+
+static const int hw_btnevents[] = {
+       BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
+       BTN_TOOL_MOUSE, BTN_TOOL_FINGER,
+       BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8,
+};
+
+static const int hw_mscevents[] = {
+       MSC_SERIAL,
+};
+
+static void hanwang_parse_packet(struct hanwang *hanwang)
+{
+       unsigned char *data = hanwang->data;
+       struct input_dev *input_dev = hanwang->dev;
+       struct usb_device *dev = hanwang->usbdev;
+       enum hanwang_tablet_type type = hanwang->features->type;
+       int i;
+       u16 x, y, p;
+
+       switch (data[0]) {
+       case 0x02:      /* data packet */
+               switch (data[1]) {
+               case 0x80:      /* tool prox out */
+                       hanwang->current_id = 0;
+                       input_report_key(input_dev, hanwang->current_tool, 0);
+                       break;
+
+               case 0xc2:      /* first time tool prox in */
+                       switch (data[3] & 0xf0) {
+                       case 0x20:      /* art_master III */
+                       case 0x30:      /* art_master_HD */
+                               hanwang->current_id = STYLUS_DEVICE_ID;
+                               hanwang->current_tool = BTN_TOOL_PEN;
+                               input_report_key(input_dev, BTN_TOOL_PEN, 1);
+                               break;
+                       case 0xa0:      /* art_master III */
+                       case 0xb0:      /* art_master_HD */
+                               hanwang->current_id = ERASER_DEVICE_ID;
+                               hanwang->current_tool = BTN_TOOL_RUBBER;
+                               input_report_key(input_dev, BTN_TOOL_RUBBER, 1);
+                               break;
+                       default:
+                               hanwang->current_id = 0;
+                               dev_dbg(&dev->dev,
+                                       "unknown tablet tool %02x ", data[0]);
+                               break;
+                       }
+                       break;
+
+               default:        /* tool data packet */
+                       x = (data[2] << 8) | data[3];
+                       y = (data[4] << 8) | data[5];
+
+                       switch (type) {
+                       case HANWANG_ART_MASTER_III:
+                               p = (data[6] << 3) |
+                                   ((data[7] & 0xc0) >> 5) |
+                                   (data[1] & 0x01);
+                               break;
+
+                       case HANWANG_ART_MASTER_HD:
+                               p = (data[7] >> 6) | (data[6] << 2);
+                               break;
+
+                       default:
+                               p = 0;
+                               break;
+                       }
+
+                       input_report_abs(input_dev, ABS_X,
+                                               le16_to_cpup((__le16 *)&x));
+                       input_report_abs(input_dev, ABS_Y,
+                                               le16_to_cpup((__le16 *)&y));
+                       input_report_abs(input_dev, ABS_PRESSURE,
+                                               le16_to_cpup((__le16 *)&p));
+                       input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f);
+                       input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f);
+                       input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02);
+                       input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04);
+                       break;
+               }
+               input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
+               input_event(input_dev, EV_MSC, MSC_SERIAL,
+                               hanwang->features->pid);
+               break;
+
+       case 0x0c:
+               /* roll wheel */
+               hanwang->current_id = PAD_DEVICE_ID;
+
+               switch (type) {
+               case HANWANG_ART_MASTER_III:
+                       input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
+                                                       data[2] || data[3]);
+                       input_report_abs(input_dev, ABS_WHEEL, data[1]);
+                       input_report_key(input_dev, BTN_0, data[2]);
+                       for (i = 0; i < 8; i++)
+                               input_report_key(input_dev,
+                                        BTN_1 + i, data[3] & (1 << i));
+                       break;
+
+               case HANWANG_ART_MASTER_HD:
+                       input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
+                                       data[2] || data[3] || data[4] ||
+                                       data[5] || data[6]);
+                       input_report_abs(input_dev, ABS_RX,
+                                       ((data[1] & 0x1f) << 8) | data[2]);
+                       input_report_abs(input_dev, ABS_RY,
+                                       ((data[3] & 0x1f) << 8) | data[4]);
+                       input_report_key(input_dev, BTN_0, data[5] & 0x01);
+                       for (i = 0; i < 4; i++) {
+                               input_report_key(input_dev,
+                                        BTN_1 + i, data[5] & (1 << i));
+                               input_report_key(input_dev,
+                                        BTN_5 + i, data[6] & (1 << i));
+                       }
+                       break;
+               }
+
+               input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
+               input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff);
+               break;
+
+       default:
+               dev_dbg(&dev->dev, "error packet  %02x ", data[0]);
+               break;
+       }
+
+       input_sync(input_dev);
+}
+
+static void hanwang_irq(struct urb *urb)
+{
+       struct hanwang *hanwang = urb->context;
+       struct usb_device *dev = hanwang->usbdev;
+       int retval;
+
+       switch (urb->status) {
+       case 0:
+               /* success */;
+               hanwang_parse_packet(hanwang);
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated, clean up */
+               dev_err(&dev->dev, "%s - urb shutting down with status: %d",
+                       __func__, urb->status);
+               return;
+       default:
+               dev_err(&dev->dev, "%s - nonzero urb status received: %d",
+                       __func__, urb->status);
+               break;
+       }
+
+       retval = usb_submit_urb(urb, GFP_ATOMIC);
+       if (retval)
+               dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
+                       __func__, retval);
+}
+
+static int hanwang_open(struct input_dev *dev)
+{
+       struct hanwang *hanwang = input_get_drvdata(dev);
+
+       hanwang->irq->dev = hanwang->usbdev;
+       if (usb_submit_urb(hanwang->irq, GFP_KERNEL))
+               return -EIO;
+
+       return 0;
+}
+
+static void hanwang_close(struct input_dev *dev)
+{
+       struct hanwang *hanwang = input_get_drvdata(dev);
+
+       usb_kill_urb(hanwang->irq);
+}
+
+static bool get_features(struct usb_device *dev, struct hanwang *hanwang)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(features_array); i++) {
+               if (le16_to_cpu(dev->descriptor.idProduct) ==
+                               features_array[i].pid) {
+                       hanwang->features = &features_array[i];
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+
+static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       struct usb_endpoint_descriptor *endpoint;
+       struct hanwang *hanwang;
+       struct input_dev *input_dev;
+       int error;
+       int i;
+
+       hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!hanwang || !input_dev) {
+               error = -ENOMEM;
+               goto fail1;
+       }
+
+       if (!get_features(dev, hanwang)) {
+               error = -ENXIO;
+               goto fail1;
+       }
+
+       hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len,
+                                       GFP_KERNEL, &hanwang->data_dma);
+       if (!hanwang->data) {
+               error = -ENOMEM;
+               goto fail1;
+       }
+
+       hanwang->irq = usb_alloc_urb(0, GFP_KERNEL);
+       if (!hanwang->irq) {
+               error = -ENOMEM;
+               goto fail2;
+       }
+
+       hanwang->usbdev = dev;
+       hanwang->dev = input_dev;
+
+       usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys));
+       strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys));
+
+       strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name));
+       input_dev->name = hanwang->name;
+       input_dev->phys = hanwang->phys;
+       usb_to_input_id(dev, &input_dev->id);
+       input_dev->dev.parent = &intf->dev;
+
+       input_set_drvdata(input_dev, hanwang);
+
+       input_dev->open = hanwang_open;
+       input_dev->close = hanwang_close;
+
+       for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
+               __set_bit(hw_eventtypes[i], input_dev->evbit);
+
+       for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
+               __set_bit(hw_absevents[i], input_dev->absbit);
+
+       for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
+               __set_bit(hw_btnevents[i], input_dev->keybit);
+
+       for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
+               __set_bit(hw_mscevents[i], input_dev->mscbit);
+
+       input_set_abs_params(input_dev, ABS_X,
+                            0, hanwang->features->max_x, 4, 0);
+       input_set_abs_params(input_dev, ABS_Y,
+                            0, hanwang->features->max_y, 4, 0);
+       input_set_abs_params(input_dev, ABS_TILT_X,
+                            0, hanwang->features->max_tilt_x, 0, 0);
+       input_set_abs_params(input_dev, ABS_TILT_Y,
+                            0, hanwang->features->max_tilt_y, 0, 0);
+       input_set_abs_params(input_dev, ABS_PRESSURE,
+                            0, hanwang->features->max_pressure, 0, 0);
+
+       endpoint = &intf->cur_altsetting->endpoint[0].desc;
+       usb_fill_int_urb(hanwang->irq, dev,
+                       usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+                       hanwang->data, hanwang->features->pkg_len,
+                       hanwang_irq, hanwang, endpoint->bInterval);
+       hanwang->irq->transfer_dma = hanwang->data_dma;
+       hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+       error = input_register_device(hanwang->dev);
+       if (error)
+               goto fail3;
+
+       usb_set_intfdata(intf, hanwang);
+
+       return 0;
+
+ fail3:        usb_free_urb(hanwang->irq);
+ fail2:        usb_free_coherent(dev, hanwang->features->pkg_len,
+                       hanwang->data, hanwang->data_dma);
+ fail1:        input_free_device(input_dev);
+       kfree(hanwang);
+       return error;
+
+}
+
+static void hanwang_disconnect(struct usb_interface *intf)
+{
+       struct hanwang *hanwang = usb_get_intfdata(intf);
+
+       input_unregister_device(hanwang->dev);
+       usb_free_urb(hanwang->irq);
+       usb_free_coherent(interface_to_usbdev(intf),
+                       hanwang->features->pkg_len, hanwang->data,
+                       hanwang->data_dma);
+       kfree(hanwang);
+       usb_set_intfdata(intf, NULL);
+}
+
+static const struct usb_device_id hanwang_ids[] = {
+       { HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS,
+               HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) },
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, hanwang_ids);
+
+static struct usb_driver hanwang_driver = {
+       .name           = "hanwang",
+       .probe          = hanwang_probe,
+       .disconnect     = hanwang_disconnect,
+       .id_table       = hanwang_ids,
+};
+
+static int __init hanwang_init(void)
+{
+       return usb_register(&hanwang_driver);
+}
+
+static void __exit hanwang_exit(void)
+{
+       usb_deregister(&hanwang_driver);
+}
+
+module_init(hanwang_init);
+module_exit(hanwang_exit);
index 284dfaab6b8cc67d375ca2c1de8aa639bbc6aaae..de5adb109030640d4ccde73787582ccef97d323a 100644 (file)
@@ -118,6 +118,7 @@ struct wacom {
 extern const struct usb_device_id wacom_ids[];
 
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
+void wacom_setup_device_quirks(struct wacom_features *features);
 void wacom_setup_input_capabilities(struct input_dev *input_dev,
                                    struct wacom_wac *wacom_wac);
 #endif
index b35876ee6908c7328f29e2505fd29a94a61b2d61..fc381498b79879551b697e05d24050020b7f220e 100644 (file)
@@ -120,14 +120,16 @@ static int wacom_open(struct input_dev *dev)
 
 out:
        mutex_unlock(&wacom->lock);
-       if (retval)
-               usb_autopm_put_interface(wacom->intf);
+       usb_autopm_put_interface(wacom->intf);
        return retval;
 }
 
 static void wacom_close(struct input_dev *dev)
 {
        struct wacom *wacom = input_get_drvdata(dev);
+       int autopm_error;
+
+       autopm_error = usb_autopm_get_interface(wacom->intf);
 
        mutex_lock(&wacom->lock);
        usb_kill_urb(wacom->irq);
@@ -135,7 +137,8 @@ static void wacom_close(struct input_dev *dev)
        wacom->intf->needs_remote_wakeup = 0;
        mutex_unlock(&wacom->lock);
 
-       usb_autopm_put_interface(wacom->intf);
+       if (!autopm_error)
+               usb_autopm_put_interface(wacom->intf);
 }
 
 static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
@@ -196,17 +199,30 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                                        features->pktlen = WACOM_PKGLEN_TPC2FG;
                                                        features->device_type = BTN_TOOL_TRIPLETAP;
                                                }
-                                               features->x_max =
-                                                       get_unaligned_le16(&report[i + 3]);
-                                               features->x_phy =
-                                                       get_unaligned_le16(&report[i + 6]);
-                                               features->unit = report[i + 9];
-                                               features->unitExpo = report[i + 11];
-                                               i += 12;
+                                               if (features->type == BAMBOO_PT) {
+                                                       /* need to reset back */
+                                                       features->pktlen = WACOM_PKGLEN_BBTOUCH;
+                                                       features->device_type = BTN_TOOL_TRIPLETAP;
+                                                       features->x_phy =
+                                                               get_unaligned_le16(&report[i + 5]);
+                                                       features->x_max =
+                                                               get_unaligned_le16(&report[i + 8]);
+                                                       i += 15;
+                                               } else {
+                                                       features->x_max =
+                                                               get_unaligned_le16(&report[i + 3]);
+                                                       features->x_phy =
+                                                               get_unaligned_le16(&report[i + 6]);
+                                                       features->unit = report[i + 9];
+                                                       features->unitExpo = report[i + 11];
+                                                       i += 12;
+                                               }
                                        } else if (pen) {
                                                /* penabled only accepts exact bytes of data */
                                                if (features->type == TABLETPC2FG)
                                                        features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+                                               if (features->type == BAMBOO_PT)
+                                                       features->pktlen = WACOM_PKGLEN_BBFUN;
                                                features->device_type = BTN_TOOL_PEN;
                                                features->x_max =
                                                        get_unaligned_le16(&report[i + 3]);
@@ -235,6 +251,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                                        features->y_phy =
                                                                get_unaligned_le16(&report[i + 6]);
                                                        i += 7;
+                                               } else if (features->type == BAMBOO_PT) {
+                                                       /* need to reset back */
+                                                       features->pktlen = WACOM_PKGLEN_BBTOUCH;
+                                                       features->device_type = BTN_TOOL_TRIPLETAP;
+                                                       features->y_phy =
+                                                               get_unaligned_le16(&report[i + 3]);
+                                                       features->y_max =
+                                                               get_unaligned_le16(&report[i + 6]);
+                                                       i += 12;
                                                } else {
                                                        features->y_max =
                                                                features->x_max;
@@ -246,6 +271,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
                                                /* penabled only accepts exact bytes of data */
                                                if (features->type == TABLETPC2FG)
                                                        features->pktlen = WACOM_PKGLEN_GRAPHIRE;
+                                               if (features->type == BAMBOO_PT)
+                                                       features->pktlen = WACOM_PKGLEN_BBFUN;
                                                features->device_type = BTN_TOOL_PEN;
                                                features->y_max =
                                                        get_unaligned_le16(&report[i + 3]);
@@ -296,8 +323,9 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
        if (!rep_data)
                return error;
 
-       /* ask to report tablet data if it is 2FGT or not a Tablet PC */
-       if (features->device_type == BTN_TOOL_TRIPLETAP) {
+       /* ask to report tablet data if it is 2FGT Tablet PC or
+        * not a Tablet PC */
+       if (features->type == TABLETPC2FG) {
                do {
                        rep_data[0] = 3;
                        rep_data[1] = 4;
@@ -309,7 +337,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
                                        WAC_HID_FEATURE_REPORT, report_id,
                                        rep_data, 3);
                } while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
-       } else if (features->type != TABLETPC && features->type != TABLETPC2FG) {
+       } else if (features->type != TABLETPC) {
                do {
                        rep_data[0] = 2;
                        rep_data[1] = 2;
@@ -334,11 +362,16 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
        struct usb_host_interface *interface = intf->cur_altsetting;
        struct hid_descriptor *hid_desc;
 
-       /* default device to penabled */
+       /* default features */
        features->device_type = BTN_TOOL_PEN;
-
-       /* only Tablet PCs need to retrieve the info */
-       if ((features->type != TABLETPC) && (features->type != TABLETPC2FG))
+       features->x_fuzz = 4;
+       features->y_fuzz = 4;
+       features->pressure_fuzz = 0;
+       features->distance_fuzz = 0;
+
+       /* only Tablet PCs and Bamboo P&T need to retrieve the info */
+       if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
+           (features->type != BAMBOO_PT))
                goto out;
 
        if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
@@ -353,12 +386,6 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
        if (error)
                goto out;
 
-       /* touch device found but size is not defined. use default */
-       if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
-               features->x_max = 1023;
-               features->y_max = 1023;
-       }
-
  out:
        return error;
 }
@@ -494,9 +521,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
        if (error)
                goto fail2;
 
+       wacom_setup_device_quirks(features);
+
        strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
 
-       if (features->type == TABLETPC || features->type == TABLETPC2FG) {
+       if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
                /* Append the device type to the name */
                strlcat(wacom_wac->name,
                        features->device_type == BTN_TOOL_PEN ?
index 47fd7a041c52e1898a8727c5386569fb3caed45c..b3252ef1e2797e6b7f640322bb8cbf4a6ecc61bd 100644 (file)
@@ -857,6 +857,134 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
        return retval;
 }
 
+static int wacom_bpt_touch(struct wacom_wac *wacom)
+{
+       struct wacom_features *features = &wacom->features;
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int sp = 0, sx = 0, sy = 0, count = 0;
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               int p = data[9 * i + 2];
+               input_mt_slot(input, i);
+               /*
+                * Touch events need to be disabled while stylus is
+                * in proximity because user's hand is resting on touchpad
+                * and sending unwanted events.  User expects tablet buttons
+                * to continue working though.
+                */
+               if (p && !wacom->shared->stylus_in_proximity) {
+                       int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
+                       int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
+                       if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
+                               x <<= 5;
+                               y <<= 5;
+                       }
+                       input_report_abs(input, ABS_MT_PRESSURE, p);
+                       input_report_abs(input, ABS_MT_POSITION_X, x);
+                       input_report_abs(input, ABS_MT_POSITION_Y, y);
+                       if (wacom->id[i] < 0)
+                               wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID;
+                       if (!count++)
+                               sp = p, sx = x, sy = y;
+               } else {
+                       wacom->id[i] = -1;
+               }
+               input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]);
+       }
+
+       input_report_key(input, BTN_TOUCH, count > 0);
+       input_report_key(input, BTN_TOOL_FINGER, count == 1);
+       input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2);
+
+       input_report_abs(input, ABS_PRESSURE, sp);
+       input_report_abs(input, ABS_X, sx);
+       input_report_abs(input, ABS_Y, sy);
+
+       input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
+       input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
+       input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
+       input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
+
+       input_sync(input);
+
+       return 0;
+}
+
+static int wacom_bpt_pen(struct wacom_wac *wacom)
+{
+       struct input_dev *input = wacom->input;
+       unsigned char *data = wacom->data;
+       int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
+
+       /*
+        * Similar to Graphire protocol, data[1] & 0x20 is proximity and
+        * data[1] & 0x18 is tool ID.  0x30 is safety check to ignore
+        * 2 unused tool ID's.
+        */
+       prox = (data[1] & 0x30) == 0x30;
+
+       /*
+        * All reports shared between PEN and RUBBER tool must be
+        * forced to a known starting value (zero) when transitioning to
+        * out-of-prox.
+        *
+        * If not reset then, to userspace, it will look like lost events
+        * if new tool comes in-prox with same values as previous tool sent.
+        *
+        * Hardware does report zero in most out-of-prox cases but not all.
+        */
+       if (prox) {
+               if (!wacom->shared->stylus_in_proximity) {
+                       if (data[1] & 0x08) {
+                               wacom->tool[0] = BTN_TOOL_RUBBER;
+                               wacom->id[0] = ERASER_DEVICE_ID;
+                       } else {
+                               wacom->tool[0] = BTN_TOOL_PEN;
+                               wacom->id[0] = STYLUS_DEVICE_ID;
+                       }
+                       wacom->shared->stylus_in_proximity = true;
+               }
+               x = le16_to_cpup((__le16 *)&data[2]);
+               y = le16_to_cpup((__le16 *)&data[4]);
+               p = le16_to_cpup((__le16 *)&data[6]);
+               d = data[8];
+               pen = data[1] & 0x01;
+               btn1 = data[1] & 0x02;
+               btn2 = data[1] & 0x04;
+       }
+
+       input_report_key(input, BTN_TOUCH, pen);
+       input_report_key(input, BTN_STYLUS, btn1);
+       input_report_key(input, BTN_STYLUS2, btn2);
+
+       input_report_abs(input, ABS_X, x);
+       input_report_abs(input, ABS_Y, y);
+       input_report_abs(input, ABS_PRESSURE, p);
+       input_report_abs(input, ABS_DISTANCE, d);
+
+       if (!prox) {
+               wacom->id[0] = 0;
+               wacom->shared->stylus_in_proximity = false;
+       }
+
+       input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
+       input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
+
+       return 1;
+}
+
+static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
+{
+       if (len == WACOM_PKGLEN_BBTOUCH)
+               return wacom_bpt_touch(wacom);
+       else if (len == WACOM_PKGLEN_BBFUN)
+               return wacom_bpt_pen(wacom);
+
+       return 0;
+}
+
 void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
 {
        bool sync;
@@ -902,6 +1030,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                sync = wacom_tpc_irq(wacom_wac, len);
                break;
 
+       case BAMBOO_PT:
+               sync = wacom_bpt_irq(wacom_wac, len);
+               break;
+
        default:
                sync = false;
                break;
@@ -911,26 +1043,17 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
                input_sync(wacom_wac->input);
 }
 
-static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
+static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
 {
        struct input_dev *input_dev = wacom_wac->input;
 
        input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
-       input_set_capability(input_dev, EV_REL, REL_WHEEL);
-
-       __set_bit(BTN_LEFT, input_dev->keybit);
-       __set_bit(BTN_RIGHT, input_dev->keybit);
-       __set_bit(BTN_MIDDLE, input_dev->keybit);
-       __set_bit(BTN_SIDE, input_dev->keybit);
-       __set_bit(BTN_EXTRA, input_dev->keybit);
 
        __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
        __set_bit(BTN_TOOL_PEN, input_dev->keybit);
-       __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
        __set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
        __set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
        __set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
-       __set_bit(BTN_TOOL_LENS, input_dev->keybit);
        __set_bit(BTN_STYLUS, input_dev->keybit);
        __set_bit(BTN_STYLUS2, input_dev->keybit);
 
@@ -939,10 +1062,55 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
        input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
        input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
        input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+}
+
+static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
+{
+       struct input_dev *input_dev = wacom_wac->input;
+
+       input_set_capability(input_dev, EV_REL, REL_WHEEL);
+
+       wacom_setup_cintiq(wacom_wac);
+
+       __set_bit(BTN_LEFT, input_dev->keybit);
+       __set_bit(BTN_RIGHT, input_dev->keybit);
+       __set_bit(BTN_MIDDLE, input_dev->keybit);
+       __set_bit(BTN_SIDE, input_dev->keybit);
+       __set_bit(BTN_EXTRA, input_dev->keybit);
+       __set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
+       __set_bit(BTN_TOOL_LENS, input_dev->keybit);
+
        input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
        input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
 }
 
+void wacom_setup_device_quirks(struct wacom_features *features)
+{
+
+       /* touch device found but size is not defined. use default */
+       if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
+               features->x_max = 1023;
+               features->y_max = 1023;
+       }
+
+       /* these device have multiple inputs */
+       if (features->type == TABLETPC || features->type == TABLETPC2FG ||
+           features->type == BAMBOO_PT)
+               features->quirks |= WACOM_QUIRK_MULTI_INPUT;
+
+       /* quirks for bamboo touch */
+       if (features->type == BAMBOO_PT &&
+           features->device_type == BTN_TOOL_TRIPLETAP) {
+               features->x_max <<= 5;
+               features->y_max <<= 5;
+               features->x_fuzz <<= 5;
+               features->y_fuzz <<= 5;
+               features->pressure_max = 256;
+               features->pressure_fuzz = 16;
+               features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
+       }
+}
+
 void wacom_setup_input_capabilities(struct input_dev *input_dev,
                                    struct wacom_wac *wacom_wac)
 {
@@ -953,9 +1121,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        __set_bit(BTN_TOUCH, input_dev->keybit);
 
-       input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
-       input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
-       input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
+       input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
+                            features->x_fuzz, 0);
+       input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
+                            features->y_fuzz, 0);
+       input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
+                            features->pressure_fuzz, 0);
 
        __set_bit(ABS_MISC, input_dev->absbit);
 
@@ -1005,9 +1176,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
                __set_bit(BTN_9, input_dev->keybit);
                /* fall through */
 
+       case CINTIQ:
+               for (i = 0; i < 8; i++)
+                       __set_bit(BTN_0 + i, input_dev->keybit);
+               __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+
+               input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
+               input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
+               input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
+               wacom_setup_cintiq(wacom_wac);
+               break;
+
        case INTUOS3:
        case INTUOS3L:
-       case CINTIQ:
                __set_bit(BTN_4, input_dev->keybit);
                __set_bit(BTN_5, input_dev->keybit);
                __set_bit(BTN_6, input_dev->keybit);
@@ -1078,6 +1259,38 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
        case PENPARTNER:
                __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
                break;
+
+       case BAMBOO_PT:
+               __clear_bit(ABS_MISC, input_dev->absbit);
+
+               if (features->device_type == BTN_TOOL_TRIPLETAP) {
+                       __set_bit(BTN_LEFT, input_dev->keybit);
+                       __set_bit(BTN_FORWARD, input_dev->keybit);
+                       __set_bit(BTN_BACK, input_dev->keybit);
+                       __set_bit(BTN_RIGHT, input_dev->keybit);
+
+                       __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+                       __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+
+                       input_mt_create_slots(input_dev, 2);
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+                                            0, features->x_max,
+                                            features->x_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+                                            0, features->y_max,
+                                            features->y_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+                                            0, features->pressure_max,
+                                            features->pressure_fuzz, 0);
+                       input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
+                                            MAX_TRACKING_ID, 0, 0);
+               } else if (features->device_type == BTN_TOOL_PEN) {
+                       __set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
+                       __set_bit(BTN_TOOL_PEN, input_dev->keybit);
+                       __set_bit(BTN_STYLUS, input_dev->keybit);
+                       __set_bit(BTN_STYLUS2, input_dev->keybit);
+               }
+               break;
        }
 }
 
@@ -1215,6 +1428,14 @@ static const struct wacom_features wacom_features_0xE3 =
        { "Wacom ISDv4 E3",       WACOM_PKGLEN_TPC2FG,    26202, 16325,  255,  0, TABLETPC2FG };
 static const struct wacom_features wacom_features_0x47 =
        { "Wacom Intuos2 6x8",    WACOM_PKGLEN_INTUOS,    20320, 16240, 1023, 31, INTUOS };
+static struct wacom_features wacom_features_0xD0 =
+       { "Wacom Bamboo 2FG",     WACOM_PKGLEN_BBFUN,     14720,  9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD1 =
+       { "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN,     14720,  9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD2 =
+       { "Wacom Bamboo Craft",   WACOM_PKGLEN_BBFUN,     14720,  9200, 1023, 63, BAMBOO_PT };
+static struct wacom_features wacom_features_0xD3 =
+       { "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN,     21648, 13530, 1023, 63, BAMBOO_PT };
 
 #define USB_DEVICE_WACOM(prod)                                 \
        USB_DEVICE(USB_VENDOR_ID_WACOM, prod),                  \
@@ -1279,6 +1500,10 @@ const struct usb_device_id wacom_ids[] = {
        { USB_DEVICE_WACOM(0xC6) },
        { USB_DEVICE_WACOM(0xC7) },
        { USB_DEVICE_WACOM(0xCE) },
+       { USB_DEVICE_WACOM(0xD0) },
+       { USB_DEVICE_WACOM(0xD1) },
+       { USB_DEVICE_WACOM(0xD2) },
+       { USB_DEVICE_WACOM(0xD3) },
        { USB_DEVICE_WACOM(0xF0) },
        { USB_DEVICE_WACOM(0xCC) },
        { USB_DEVICE_WACOM(0x90) },
index 99e1a54cd305fa3dce303670f44973dd82d273f9..00ca01541d89e86e210c34efc184e3b385dee096 100644 (file)
@@ -21,6 +21,7 @@
 #define WACOM_PKGLEN_INTUOS    10
 #define WACOM_PKGLEN_TPC1FG     5
 #define WACOM_PKGLEN_TPC2FG    14
+#define WACOM_PKGLEN_BBTOUCH   20
 
 /* device IDs */
 #define STYLUS_DEVICE_ID       0x02
 #define WACOM_REPORT_TPC1FG            6
 #define WACOM_REPORT_TPC2FG            13
 
+/* device quirks */
+#define WACOM_QUIRK_MULTI_INPUT                0x0001
+#define WACOM_QUIRK_BBTOUCH_LOWRES     0x0002
+
+/* largest reported tracking id */
+#define MAX_TRACKING_ID                        0xfff
+
 enum {
        PENPARTNER = 0,
        GRAPHIRE,
@@ -44,6 +52,7 @@ enum {
        PTU,
        PL,
        DTU,
+       BAMBOO_PT,
        INTUOS,
        INTUOS3S,
        INTUOS3,
@@ -73,6 +82,11 @@ struct wacom_features {
        int y_phy;
        unsigned char unit;
        unsigned char unitExpo;
+       int x_fuzz;
+       int y_fuzz;
+       int pressure_fuzz;
+       int distance_fuzz;
+       unsigned quirks;
 };
 
 struct wacom_shared {
@@ -86,6 +100,7 @@ struct wacom_wac {
        int id[3];
        __u32 serial[2];
        int last_finger;
+       int trk_id;
        struct wacom_features features;
        struct wacom_shared *shared;
        struct input_dev *input;
index 0069d9703fda82c94409bae95e64e6be2faac4ac..0ea361f23fa7c1201e0d2e76a61e14e62ed58920 100644 (file)
@@ -98,6 +98,18 @@ config TOUCHSCREEN_BITSY
          To compile this driver as a module, choose M here: the
          module will be called h3600_ts_input.
 
+config TOUCHSCREEN_BU21013
+       tristate "BU21013 based touch panel controllers"
+       depends on I2C
+       help
+         Say Y here if you have a bu21013 touchscreen connected to
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called bu21013_ts.
+
 config TOUCHSCREEN_CY8CTMG110
        tristate "cy8ctmg110 touchscreen"
        depends on I2C
@@ -214,6 +226,16 @@ config TOUCHSCREEN_WACOM_W8001
          To compile this driver as a module, choose M here: the
          module will be called wacom_w8001.
 
+config TOUCHSCREEN_LPC32XX
+       tristate "LPC32XX touchscreen controller"
+       depends on ARCH_LPC32XX
+       help
+         Say Y here if you have a LPC32XX device and want
+         to support the built-in touchscreen.
+
+         To compile this driver as a module, choose M here: the
+         module will be called lpc32xx_ts.
+
 config TOUCHSCREEN_MCS5000
        tristate "MELFAS MCS-5000 touchscreen"
        depends on I2C
@@ -250,6 +272,18 @@ config TOUCHSCREEN_INEXIO
          To compile this driver as a module, choose M here: the
          module will be called inexio.
 
+config TOUCHSCREEN_INTEL_MID
+       tristate "Intel MID platform resistive touchscreen"
+       depends on INTEL_SCU_IPC
+       help
+         Say Y here if you have a Intel MID based touchscreen in
+         your system.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called intel_mid_touch.
+
 config TOUCHSCREEN_MK712
        tristate "ICS MicroClock MK712 touchscreen"
        help
index 28217e1dcafd9687e8e46fa4ab41e6c374f8706b..99b353c4e9aebd08fc4ea7974bb6537304377c4f 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI)  += ad7879-spi.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)      += ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)                += h3600_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013)       += bu21013_ts.o
 obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110)   += cy8ctmg110_ts.o
 obj-$(CONFIG_TOUCHSCREEN_DA9034)       += da9034-ts.o
 obj-$(CONFIG_TOUCHSCREEN_DYNAPRO)      += dynapro.o
@@ -23,6 +24,8 @@ obj-$(CONFIG_TOUCHSCREEN_EETI)                += eeti_ts.o
 obj-$(CONFIG_TOUCHSCREEN_ELO)          += elo.o
 obj-$(CONFIG_TOUCHSCREEN_FUJITSU)      += fujitsu_ts.o
 obj-$(CONFIG_TOUCHSCREEN_INEXIO)       += inexio.o
+obj-$(CONFIG_TOUCHSCREEN_INTEL_MID)    += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_LPC32XX)      += lpc32xx_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MC13783)      += mc13783_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MCS5000)      += mcs5000_ts.o
 obj-$(CONFIG_TOUCHSCREEN_MIGOR)                += migor_ts.o
index 5f0221cffef9bf12821ed9fd38d63bac0df720dc..a1952fcc083e14a68694b5f69c64359588818b2c 100644 (file)
@@ -191,13 +191,12 @@ struct ad7877 {
        struct spi_message      msg;
 
        struct mutex            mutex;
-       unsigned                disabled:1;     /* P: mutex */
-       unsigned                gpio3:1;        /* P: mutex */
-       unsigned                gpio4:1;        /* P: mutex */
+       bool                    disabled;       /* P: mutex */
+       bool                    gpio3;          /* P: mutex */
+       bool                    gpio4;          /* P: mutex */
 
        spinlock_t              lock;
        struct timer_list       timer;          /* P: lock */
-       unsigned                pending:1;      /* P: lock */
 
        /*
         * DMA (thus cache coherency maintenance) requires the
@@ -206,8 +205,8 @@ struct ad7877 {
        u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
 };
 
-static int gpio3;
-module_param(gpio3, int, 0);
+static bool gpio3;
+module_param(gpio3, bool, 0);
 MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
 
 /*
@@ -230,6 +229,7 @@ static int ad7877_read(struct spi_device *spi, u16 reg)
                        AD7877_READADD(reg));
        req->xfer[0].tx_buf = &req->command;
        req->xfer[0].len = 2;
+       req->xfer[0].cs_change = 1;
 
        req->xfer[1].rx_buf = &req->sample;
        req->xfer[1].len = 2;
@@ -295,20 +295,25 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
 
        req->xfer[0].tx_buf = &req->reset;
        req->xfer[0].len = 2;
+       req->xfer[0].cs_change = 1;
 
        req->xfer[1].tx_buf = &req->ref_on;
        req->xfer[1].len = 2;
        req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+       req->xfer[1].cs_change = 1;
 
        req->xfer[2].tx_buf = &req->command;
        req->xfer[2].len = 2;
        req->xfer[2].delay_usecs = ts->vref_delay_usecs;
+       req->xfer[2].cs_change = 1;
 
        req->xfer[3].rx_buf = &req->sample;
        req->xfer[3].len = 2;
+       req->xfer[3].cs_change = 1;
 
        req->xfer[4].tx_buf = &ts->cmd_crtl2;   /*REF OFF*/
        req->xfer[4].len = 2;
+       req->xfer[4].cs_change = 1;
 
        req->xfer[5].tx_buf = &ts->cmd_crtl1;   /*DEFAULT*/
        req->xfer[5].len = 2;
@@ -327,7 +332,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
        return status ? : sample;
 }
 
-static void ad7877_rx(struct ad7877 *ts)
+static int ad7877_process_data(struct ad7877 *ts)
 {
        struct input_dev *input_dev = ts->input;
        unsigned Rt;
@@ -354,11 +359,25 @@ static void ad7877_rx(struct ad7877 *ts)
                Rt /= z1;
                Rt = (Rt + 2047) >> 12;
 
+               /*
+                * Sample found inconsistent, pressure is beyond
+                * the maximum. Don't report it to user space.
+                */
+               if (Rt > ts->pressure_max)
+                       return -EINVAL;
+
+               if (!timer_pending(&ts->timer))
+                       input_report_key(input_dev, BTN_TOUCH, 1);
+
                input_report_abs(input_dev, ABS_X, x);
                input_report_abs(input_dev, ABS_Y, y);
                input_report_abs(input_dev, ABS_PRESSURE, Rt);
                input_sync(input_dev);
+
+               return 0;
        }
+
+       return -EINVAL;
 }
 
 static inline void ad7877_ts_event_release(struct ad7877 *ts)
@@ -366,72 +385,56 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts)
        struct input_dev *input_dev = ts->input;
 
        input_report_abs(input_dev, ABS_PRESSURE, 0);
+       input_report_key(input_dev, BTN_TOUCH, 0);
        input_sync(input_dev);
 }
 
 static void ad7877_timer(unsigned long handle)
 {
        struct ad7877 *ts = (void *)handle;
+       unsigned long flags;
 
+       spin_lock_irqsave(&ts->lock, flags);
        ad7877_ts_event_release(ts);
+       spin_unlock_irqrestore(&ts->lock, flags);
 }
 
 static irqreturn_t ad7877_irq(int irq, void *handle)
 {
        struct ad7877 *ts = handle;
        unsigned long flags;
-       int status;
+       int error;
 
-       /*
-        * The repeated conversion sequencer controlled by TMR kicked off
-        * too fast. We ignore the last and process the sample sequence
-        * currently in the queue. It can't be older than 9.4ms, and we
-        * need to avoid that ts->msg doesn't get issued twice while in work.
-        */
+       error = spi_sync(ts->spi, &ts->msg);
+       if (error) {
+               dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
+               goto out;
+       }
 
        spin_lock_irqsave(&ts->lock, flags);
-       if (!ts->pending) {
-               ts->pending = 1;
-
-               status = spi_async(ts->spi, &ts->msg);
-               if (status)
-                       dev_err(&ts->spi->dev, "spi_sync --> %d\n", status);
-       }
+       error = ad7877_process_data(ts);
+       if (!error)
+               mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
        spin_unlock_irqrestore(&ts->lock, flags);
 
+out:
        return IRQ_HANDLED;
 }
 
-static void ad7877_callback(void *_ts)
-{
-       struct ad7877 *ts = _ts;
-
-       spin_lock_irq(&ts->lock);
-
-       ad7877_rx(ts);
-       ts->pending = 0;
-       mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
-
-       spin_unlock_irq(&ts->lock);
-}
-
 static void ad7877_disable(struct ad7877 *ts)
 {
        mutex_lock(&ts->mutex);
 
        if (!ts->disabled) {
-               ts->disabled = 1;
+               ts->disabled = true;
                disable_irq(ts->spi->irq);
 
-               /* Wait for spi_async callback */
-               while (ts->pending)
-                       msleep(1);
-
                if (del_timer_sync(&ts->timer))
                        ad7877_ts_event_release(ts);
        }
 
-       /* we know the chip's in lowpower mode since we always
+       /*
+        * We know the chip's in lowpower mode since we always
         * leave it that way after every request
         */
 
@@ -443,7 +446,7 @@ static void ad7877_enable(struct ad7877 *ts)
        mutex_lock(&ts->mutex);
 
        if (ts->disabled) {
-               ts->disabled = 0;
+               ts->disabled = false;
                enable_irq(ts->spi->irq);
        }
 
@@ -453,7 +456,7 @@ static void ad7877_enable(struct ad7877 *ts)
 #define SHOW(name) static ssize_t \
 name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
 { \
-       struct ad7877   *ts = dev_get_drvdata(dev); \
+       struct ad7877 *ts = dev_get_drvdata(dev); \
        ssize_t v = ad7877_read_adc(ts->spi, \
                        AD7877_READ_CHAN(name)); \
        if (v < 0) \
@@ -473,7 +476,7 @@ SHOW(temp2)
 static ssize_t ad7877_disable_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct ad7877   *ts = dev_get_drvdata(dev);
+       struct ad7877 *ts = dev_get_drvdata(dev);
 
        return sprintf(buf, "%u\n", ts->disabled);
 }
@@ -503,7 +506,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
 static ssize_t ad7877_dac_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct ad7877   *ts = dev_get_drvdata(dev);
+       struct ad7877 *ts = dev_get_drvdata(dev);
 
        return sprintf(buf, "%u\n", ts->dac);
 }
@@ -533,7 +536,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
 static ssize_t ad7877_gpio3_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct ad7877   *ts = dev_get_drvdata(dev);
+       struct ad7877 *ts = dev_get_drvdata(dev);
 
        return sprintf(buf, "%u\n", ts->gpio3);
 }
@@ -564,7 +567,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
 static ssize_t ad7877_gpio4_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct ad7877   *ts = dev_get_drvdata(dev);
+       struct ad7877 *ts = dev_get_drvdata(dev);
 
        return sprintf(buf, "%u\n", ts->gpio4);
 }
@@ -597,16 +600,35 @@ static struct attribute *ad7877_attributes[] = {
        &dev_attr_temp2.attr,
        &dev_attr_aux1.attr,
        &dev_attr_aux2.attr,
+       &dev_attr_aux3.attr,
        &dev_attr_bat1.attr,
        &dev_attr_bat2.attr,
        &dev_attr_disable.attr,
        &dev_attr_dac.attr,
+       &dev_attr_gpio3.attr,
        &dev_attr_gpio4.attr,
        NULL
 };
 
+static mode_t ad7877_attr_is_visible(struct kobject *kobj,
+                                    struct attribute *attr, int n)
+{
+       mode_t mode = attr->mode;
+
+       if (attr == &dev_attr_aux3.attr) {
+               if (gpio3)
+                       mode = 0;
+       } else if (attr == &dev_attr_gpio3.attr) {
+               if (!gpio3)
+                       mode = 0;
+       }
+
+       return mode;
+}
+
 static const struct attribute_group ad7877_attr_group = {
-       .attrs = ad7877_attributes,
+       .is_visible     = ad7877_attr_is_visible,
+       .attrs          = ad7877_attributes,
 };
 
 static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
@@ -635,22 +657,25 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
 
        spi_message_init(m);
 
-       m->complete = ad7877_callback;
        m->context = ts;
 
        ts->xfer[0].tx_buf = &ts->cmd_crtl1;
        ts->xfer[0].len = 2;
+       ts->xfer[0].cs_change = 1;
 
        spi_message_add_tail(&ts->xfer[0], m);
 
        ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
        ts->xfer[1].len = 2;
+       ts->xfer[1].cs_change = 1;
 
        spi_message_add_tail(&ts->xfer[1], m);
 
-       for (i = 0; i < 11; i++) {
+       for (i = 0; i < AD7877_NR_SENSE; i++) {
                ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
                ts->xfer[i + 2].len = 2;
+               if (i < (AD7877_NR_SENSE - 1))
+                       ts->xfer[i + 2].cs_change = 1;
                spi_message_add_tail(&ts->xfer[i + 2], m);
        }
 }
@@ -718,6 +743,8 @@ static int __devinit ad7877_probe(struct spi_device *spi)
        input_dev->phys = ts->phys;
        input_dev->dev.parent = &spi->dev;
 
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(BTN_TOUCH, input_dev->keybit);
        __set_bit(EV_ABS, input_dev->evbit);
        __set_bit(ABS_X, input_dev->absbit);
        __set_bit(ABS_Y, input_dev->absbit);
@@ -752,8 +779,9 @@ static int __devinit ad7877_probe(struct spi_device *spi)
 
        /* Request AD7877 /DAV GPIO interrupt */
 
-       err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING,
-                       spi->dev.driver->name, ts);
+       err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
+                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                  spi->dev.driver->name, ts);
        if (err) {
                dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
                goto err_free_mem;
@@ -763,20 +791,12 @@ static int __devinit ad7877_probe(struct spi_device *spi)
        if (err)
                goto err_free_irq;
 
-       err = device_create_file(&spi->dev,
-                                gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
-       if (err)
-               goto err_remove_attr_group;
-
        err = input_register_device(input_dev);
        if (err)
-               goto err_remove_attr;
+               goto err_remove_attr_group;
 
        return 0;
 
-err_remove_attr:
-       device_remove_file(&spi->dev,
-                          gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
 err_remove_attr_group:
        sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
 err_free_irq:
@@ -790,11 +810,9 @@ err_free_mem:
 
 static int __devexit ad7877_remove(struct spi_device *spi)
 {
-       struct ad7877           *ts = dev_get_drvdata(&spi->dev);
+       struct ad7877 *ts = dev_get_drvdata(&spi->dev);
 
        sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
-       device_remove_file(&spi->dev,
-                          gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
 
        ad7877_disable(ts);
        free_irq(ts->spi->irq, ts);
index 16031933a8f621266a555b09ff63979c0359ca0b..14ea54b78e4648b0ac8a3403440f6f5d120dfdd9 100644 (file)
  *  it under the terms of the GNU General Public License version 2 as
  *  published by the Free Software Foundation.
  */
+#include <linux/types.h>
 #include <linux/hwmon.h>
 #include <linux/init.h>
 #include <linux/err.h>
+#include <linux/sched.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/interrupt.h>
  * files.
  */
 
-#define TS_POLL_DELAY  (1 * 1000000)   /* ns delay before the first sample */
-#define TS_POLL_PERIOD (5 * 1000000)   /* ns delay between samples */
+#define TS_POLL_DELAY  1       /* ms delay before the first sample */
+#define TS_POLL_PERIOD 5       /* ms delay between samples */
 
 /* this driver doesn't aim at the peak continuous sample rate */
 #define        SAMPLE_BITS     (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)
 
 struct ts_event {
-       /* For portability, we can't read 12 bit values using SPI (which
-        * would make the controller deliver them as native byteorder u16
+       /*
+        * For portability, we can't read 12 bit values using SPI (which
+        * would make the controller deliver them as native byte order u16
         * with msbs zeroed).  Instead, we read them as two 8-bit values,
         * *** WHICH NEED BYTESWAPPING *** and range adjustment.
         */
        u16     x;
        u16     y;
        u16     z1, z2;
-       int     ignore;
+       bool    ignore;
        u8      x_buf[3];
        u8      y_buf[3];
 };
@@ -110,8 +113,11 @@ struct ads7846 {
 
        struct spi_transfer     xfer[18];
        struct spi_message      msg[5];
-       struct spi_message      *last_msg;
-       int                     msg_idx;
+       int                     msg_count;
+       wait_queue_head_t       wait;
+
+       bool                    pendown;
+
        int                     read_cnt;
        int                     read_rep;
        int                     last_read;
@@ -122,14 +128,10 @@ struct ads7846 {
 
        u16                     penirq_recheck_delay_usecs;
 
-       spinlock_t              lock;
-       struct hrtimer          timer;
-       unsigned                pendown:1;      /* P: lock */
-       unsigned                pending:1;      /* P: lock */
-// FIXME remove "irq_disabled"
-       unsigned                irq_disabled:1; /* P: lock */
-       unsigned                disabled:1;
-       unsigned                is_suspended:1;
+       struct mutex            lock;
+       bool                    stopped;        /* P: lock */
+       bool                    disabled;       /* P: lock */
+       bool                    suspended;      /* P: lock */
 
        int                     (*filter)(void *data, int data_idx, int *val);
        void                    *filter_data;
@@ -165,7 +167,7 @@ struct ads7846 {
 #define        ADS_12_BIT              (0 << 3)
 #define        ADS_SER                 (1 << 2)        /* non-differential */
 #define        ADS_DFR                 (0 << 2)        /* differential */
-#define        ADS_PD10_PDOWN          (0 << 0)        /* lowpower mode + penirq */
+#define        ADS_PD10_PDOWN          (0 << 0)        /* low power mode + penirq */
 #define        ADS_PD10_ADC_ON         (1 << 0)        /* ADC on */
 #define        ADS_PD10_REF_ON         (2 << 0)        /* vREF on + penirq */
 #define        ADS_PD10_ALL_ON         (3 << 0)        /* ADC + vREF on */
@@ -193,6 +195,78 @@ struct ads7846 {
 #define        REF_ON  (READ_12BIT_DFR(x, 1, 1))
 #define        REF_OFF (READ_12BIT_DFR(y, 0, 0))
 
+/* Must be called with ts->lock held */
+static void ads7846_stop(struct ads7846 *ts)
+{
+       if (!ts->disabled && !ts->suspended) {
+               /* Signal IRQ thread to stop polling and disable the handler. */
+               ts->stopped = true;
+               mb();
+               wake_up(&ts->wait);
+               disable_irq(ts->spi->irq);
+       }
+}
+
+/* Must be called with ts->lock held */
+static void ads7846_restart(struct ads7846 *ts)
+{
+       if (!ts->disabled && !ts->suspended) {
+               /* Tell IRQ thread that it may poll the device. */
+               ts->stopped = false;
+               mb();
+               enable_irq(ts->spi->irq);
+       }
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_disable(struct ads7846 *ts)
+{
+       ads7846_stop(ts);
+       regulator_disable(ts->reg);
+
+       /*
+        * We know the chip's in low power mode since we always
+        * leave it that way after every request
+        */
+}
+
+/* Must be called with ts->lock held */
+static void __ads7846_enable(struct ads7846 *ts)
+{
+       regulator_enable(ts->reg);
+       ads7846_restart(ts);
+}
+
+static void ads7846_disable(struct ads7846 *ts)
+{
+       mutex_lock(&ts->lock);
+
+       if (!ts->disabled) {
+
+               if  (!ts->suspended)
+                       __ads7846_disable(ts);
+
+               ts->disabled = true;
+       }
+
+       mutex_unlock(&ts->lock);
+}
+
+static void ads7846_enable(struct ads7846 *ts)
+{
+       mutex_lock(&ts->lock);
+
+       if (ts->disabled) {
+
+               ts->disabled = false;
+
+               if (!ts->suspended)
+                       __ads7846_enable(ts);
+       }
+
+       mutex_unlock(&ts->lock);
+}
+
 /*--------------------------------------------------------------------------*/
 
 /*
@@ -219,23 +293,15 @@ struct ads7845_ser_req {
        struct spi_transfer     xfer[2];
 };
 
-static void ads7846_enable(struct ads7846 *ts);
-static void ads7846_disable(struct ads7846 *ts);
-
-static int device_suspended(struct device *dev)
-{
-       struct ads7846 *ts = dev_get_drvdata(dev);
-       return ts->is_suspended || ts->disabled;
-}
-
 static int ads7846_read12_ser(struct device *dev, unsigned command)
 {
-       struct spi_device       *spi = to_spi_device(dev);
-       struct ads7846          *ts = dev_get_drvdata(dev);
-       struct ser_req          *req = kzalloc(sizeof *req, GFP_KERNEL);
-       int                     status;
-       int                     use_internal;
+       struct spi_device *spi = to_spi_device(dev);
+       struct ads7846 *ts = dev_get_drvdata(dev);
+       struct ser_req *req;
+       int status;
+       int use_internal;
 
+       req = kzalloc(sizeof *req, GFP_KERNEL);
        if (!req)
                return -ENOMEM;
 
@@ -282,11 +348,11 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        CS_CHANGE(req->xfer[5]);
        spi_message_add_tail(&req->xfer[5], &req->msg);
 
-       ts->irq_disabled = 1;
-       disable_irq(spi->irq);
+       mutex_lock(&ts->lock);
+       ads7846_stop(ts);
        status = spi_sync(spi, &req->msg);
-       ts->irq_disabled = 0;
-       enable_irq(spi->irq);
+       ads7846_restart(ts);
+       mutex_unlock(&ts->lock);
 
        if (status == 0) {
                /* on-wire is a must-ignore bit, a BE12 value, then padding */
@@ -301,11 +367,12 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 
 static int ads7845_read12_ser(struct device *dev, unsigned command)
 {
-       struct spi_device       *spi = to_spi_device(dev);
-       struct ads7846          *ts = dev_get_drvdata(dev);
-       struct ads7845_ser_req  *req = kzalloc(sizeof *req, GFP_KERNEL);
-       int                     status;
+       struct spi_device *spi = to_spi_device(dev);
+       struct ads7846 *ts = dev_get_drvdata(dev);
+       struct ads7845_ser_req *req;
+       int status;
 
+       req = kzalloc(sizeof *req, GFP_KERNEL);
        if (!req)
                return -ENOMEM;
 
@@ -317,11 +384,11 @@ static int ads7845_read12_ser(struct device *dev, unsigned command)
        req->xfer[0].len = 3;
        spi_message_add_tail(&req->xfer[0], &req->msg);
 
-       ts->irq_disabled = 1;
-       disable_irq(spi->irq);
+       mutex_lock(&ts->lock);
+       ads7846_stop(ts);
        status = spi_sync(spi, &req->msg);
-       ts->irq_disabled = 0;
-       enable_irq(spi->irq);
+       ads7846_restart(ts);
+       mutex_unlock(&ts->lock);
 
        if (status == 0) {
                /* BE12 value, then padding */
@@ -374,6 +441,7 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
        /* external resistors may scale vAUX into 0..vREF */
        retval *= ts->vref_mv;
        retval = retval >> 12;
+
        return retval;
 }
 
@@ -384,13 +452,13 @@ static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v)
        /* ads7846 has a resistor ladder to scale this signal down */
        if (ts->model == 7846)
                retval *= 4;
+
        return retval;
 }
 
 SHOW(in0_input, vaux, vaux_adjust)
 SHOW(in1_input, vbatt, vbatt_adjust)
 
-
 static struct attribute *ads7846_attributes[] = {
        &dev_attr_temp0.attr,
        &dev_attr_temp1.attr,
@@ -498,17 +566,12 @@ static inline void ads784x_hwmon_unregister(struct spi_device *spi,
 }
 #endif
 
-static int is_pen_down(struct device *dev)
-{
-       struct ads7846  *ts = dev_get_drvdata(dev);
-
-       return ts->pendown;
-}
-
 static ssize_t ads7846_pen_down_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
-       return sprintf(buf, "%u\n", is_pen_down(dev));
+       struct ads7846 *ts = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", ts->pendown);
 }
 
 static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
@@ -516,7 +579,7 @@ static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL);
 static ssize_t ads7846_disable_show(struct device *dev,
                                     struct device_attribute *attr, char *buf)
 {
-       struct ads7846  *ts = dev_get_drvdata(dev);
+       struct ads7846 *ts = dev_get_drvdata(dev);
 
        return sprintf(buf, "%u\n", ts->disabled);
 }
@@ -531,15 +594,11 @@ static ssize_t ads7846_disable_store(struct device *dev,
        if (strict_strtoul(buf, 10, &i))
                return -EINVAL;
 
-       spin_lock_irq(&ts->lock);
-
        if (i)
                ads7846_disable(ts);
        else
                ads7846_enable(ts);
 
-       spin_unlock_irq(&ts->lock);
-
        return count;
 }
 
@@ -569,23 +628,141 @@ static void null_wait_for_sync(void)
 {
 }
 
-/*
- * PENIRQ only kicks the timer.  The timer only reissues the SPI transfer,
- * to retrieve touchscreen status.
- *
- * The SPI transfer completion callback does the real work.  It reports
- * touchscreen events and reactivates the timer (or IRQ) as appropriate.
- */
+static int ads7846_debounce_filter(void *ads, int data_idx, int *val)
+{
+       struct ads7846 *ts = ads;
+
+       if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
+               /* Start over collecting consistent readings. */
+               ts->read_rep = 0;
+               /*
+                * Repeat it, if this was the first read or the read
+                * wasn't consistent enough.
+                */
+               if (ts->read_cnt < ts->debounce_max) {
+                       ts->last_read = *val;
+                       ts->read_cnt++;
+                       return ADS7846_FILTER_REPEAT;
+               } else {
+                       /*
+                        * Maximum number of debouncing reached and still
+                        * not enough number of consistent readings. Abort
+                        * the whole sample, repeat it in the next sampling
+                        * period.
+                        */
+                       ts->read_cnt = 0;
+                       return ADS7846_FILTER_IGNORE;
+               }
+       } else {
+               if (++ts->read_rep > ts->debounce_rep) {
+                       /*
+                        * Got a good reading for this coordinate,
+                        * go for the next one.
+                        */
+                       ts->read_cnt = 0;
+                       ts->read_rep = 0;
+                       return ADS7846_FILTER_OK;
+               } else {
+                       /* Read more values that are consistent. */
+                       ts->read_cnt++;
+                       return ADS7846_FILTER_REPEAT;
+               }
+       }
+}
+
+static int ads7846_no_filter(void *ads, int data_idx, int *val)
+{
+       return ADS7846_FILTER_OK;
+}
+
+static int ads7846_get_value(struct ads7846 *ts, struct spi_message *m)
+{
+       struct spi_transfer *t =
+               list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+       if (ts->model == 7845) {
+               return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
+       } else {
+               /*
+                * adjust:  on-wire is a must-ignore bit, a BE12 value, then
+                * padding; built from two 8 bit values written msb-first.
+                */
+               return be16_to_cpup((__be16 *)t->rx_buf) >> 3;
+       }
+}
+
+static void ads7846_update_value(struct spi_message *m, int val)
+{
+       struct spi_transfer *t =
+               list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
+
+       *(u16 *)t->rx_buf = val;
+}
+
+static void ads7846_read_state(struct ads7846 *ts)
+{
+       struct ads7846_packet *packet = ts->packet;
+       struct spi_message *m;
+       int msg_idx = 0;
+       int val;
+       int action;
+       int error;
+
+       while (msg_idx < ts->msg_count) {
+
+               ts->wait_for_sync();
+
+               m = &ts->msg[msg_idx];
+               error = spi_sync(ts->spi, m);
+               if (error) {
+                       dev_err(&ts->spi->dev, "spi_async --> %d\n", error);
+                       packet->tc.ignore = true;
+                       return;
+               }
+
+               /*
+                * Last message is power down request, no need to convert
+                * or filter the value.
+                */
+               if (msg_idx < ts->msg_count - 1) {
 
-static void ads7846_rx(void *ads)
+                       val = ads7846_get_value(ts, m);
+
+                       action = ts->filter(ts->filter_data, msg_idx, &val);
+                       switch (action) {
+                       case ADS7846_FILTER_REPEAT:
+                               continue;
+
+                       case ADS7846_FILTER_IGNORE:
+                               packet->tc.ignore = true;
+                               msg_idx = ts->msg_count - 1;
+                               continue;
+
+                       case ADS7846_FILTER_OK:
+                               ads7846_update_value(m, val);
+                               packet->tc.ignore = false;
+                               msg_idx++;
+                               break;
+
+                       default:
+                               BUG();
+                       }
+               } else {
+                       msg_idx++;
+               }
+       }
+}
+
+static void ads7846_report_state(struct ads7846 *ts)
 {
-       struct ads7846          *ts = ads;
-       struct ads7846_packet   *packet = ts->packet;
-       unsigned                Rt;
-       u16                     x, y, z1, z2;
+       struct ads7846_packet *packet = ts->packet;
+       unsigned int Rt;
+       u16 x, y, z1, z2;
 
-       /* ads7846_rx_val() did in-place conversion (including byteswap) from
-        * on-the-wire format as part of debouncing to get stable readings.
+       /*
+        * ads7846_get_value() does in-place conversion (including byte swap)
+        * from on-the-wire format as part of debouncing to get stable
+        * readings.
         */
        if (ts->model == 7845) {
                x = *(u16 *)packet->tc.x_buf;
@@ -623,19 +800,19 @@ static void ads7846_rx(void *ads)
                Rt = 0;
        }
 
-       /* Sample found inconsistent by debouncing or pressure is beyond
+       /*
+        * 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
         */
        if (packet->tc.ignore || Rt > ts->pressure_max) {
                dev_vdbg(&ts->spi->dev, "ignored %d pressure %d\n",
                         packet->tc.ignore, Rt);
-               hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
-                             HRTIMER_MODE_REL);
                return;
        }
 
-       /* Maybe check the pendown state before reporting. This discards
+       /*
+        * Maybe check the pendown state before reporting. This discards
         * false readings when the pen is lifted.
         */
        if (ts->penirq_recheck_delay_usecs) {
@@ -644,8 +821,9 @@ static void ads7846_rx(void *ads)
                        Rt = 0;
        }
 
-       /* NOTE: We can't rely on the pressure to determine the pen down
-        * state, even this controller has a pressure sensor.  The pressure
+       /*
+        * NOTE: We can't rely on the pressure to determine the pen down
+        * state, even this controller has a pressure sensor. The pressure
         * value can fluctuate for quite a while after lifting the pen and
         * in some cases may not even settle at the expected value.
         *
@@ -655,15 +833,15 @@ static void ads7846_rx(void *ads)
        if (Rt) {
                struct input_dev *input = ts->input;
 
+               if (ts->swap_xy)
+                       swap(x, y);
+
                if (!ts->pendown) {
                        input_report_key(input, BTN_TOUCH, 1);
-                       ts->pendown = 1;
+                       ts->pendown = true;
                        dev_vdbg(&ts->spi->dev, "DOWN\n");
                }
 
-               if (ts->swap_xy)
-                       swap(x, y);
-
                input_report_abs(input, ABS_X, x);
                input_report_abs(input, ABS_Y, y);
                input_report_abs(input, ABS_PRESSURE, ts->pressure_max - Rt);
@@ -671,246 +849,94 @@ static void ads7846_rx(void *ads)
                input_sync(input);
                dev_vdbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt);
        }
-
-       hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
-                       HRTIMER_MODE_REL);
-}
-
-static int ads7846_debounce(void *ads, int data_idx, int *val)
-{
-       struct ads7846          *ts = ads;
-
-       if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) {
-               /* Start over collecting consistent readings. */
-               ts->read_rep = 0;
-               /* Repeat it, if this was the first read or the read
-                * wasn't consistent enough. */
-               if (ts->read_cnt < ts->debounce_max) {
-                       ts->last_read = *val;
-                       ts->read_cnt++;
-                       return ADS7846_FILTER_REPEAT;
-               } else {
-                       /* Maximum number of debouncing reached and still
-                        * not enough number of consistent readings. Abort
-                        * the whole sample, repeat it in the next sampling
-                        * period.
-                        */
-                       ts->read_cnt = 0;
-                       return ADS7846_FILTER_IGNORE;
-               }
-       } else {
-               if (++ts->read_rep > ts->debounce_rep) {
-                       /* Got a good reading for this coordinate,
-                        * go for the next one. */
-                       ts->read_cnt = 0;
-                       ts->read_rep = 0;
-                       return ADS7846_FILTER_OK;
-               } else {
-                       /* Read more values that are consistent. */
-                       ts->read_cnt++;
-                       return ADS7846_FILTER_REPEAT;
-               }
-       }
 }
 
-static int ads7846_no_filter(void *ads, int data_idx, int *val)
+static irqreturn_t ads7846_hard_irq(int irq, void *handle)
 {
-       return ADS7846_FILTER_OK;
-}
-
-static void ads7846_rx_val(void *ads)
-{
-       struct ads7846 *ts = ads;
-       struct ads7846_packet *packet = ts->packet;
-       struct spi_message *m;
-       struct spi_transfer *t;
-       int val;
-       int action;
-       int status;
-
-       m = &ts->msg[ts->msg_idx];
-       t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
-
-       if (ts->model == 7845) {
-               val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
-       } else {
-               /* adjust:  on-wire is a must-ignore bit, a BE12 value, then
-                * padding; built from two 8 bit values written msb-first.
-                */
-               val = be16_to_cpup((__be16 *)t->rx_buf) >> 3;
-       }
+       struct ads7846 *ts = handle;
 
-       action = ts->filter(ts->filter_data, ts->msg_idx, &val);
-       switch (action) {
-       case ADS7846_FILTER_REPEAT:
-               break;
-       case ADS7846_FILTER_IGNORE:
-               packet->tc.ignore = 1;
-               /* Last message will contain ads7846_rx() as the
-                * completion function.
-                */
-               m = ts->last_msg;
-               break;
-       case ADS7846_FILTER_OK:
-               *(u16 *)t->rx_buf = val;
-               packet->tc.ignore = 0;
-               m = &ts->msg[++ts->msg_idx];
-               break;
-       default:
-               BUG();
-       }
-       ts->wait_for_sync();
-       status = spi_async(ts->spi, m);
-       if (status)
-               dev_err(&ts->spi->dev, "spi_async --> %d\n",
-                               status);
+       return get_pendown_state(ts) ? IRQ_WAKE_THREAD : IRQ_HANDLED;
 }
 
-static enum hrtimer_restart ads7846_timer(struct hrtimer *handle)
-{
-       struct ads7846  *ts = container_of(handle, struct ads7846, timer);
-       int             status = 0;
-
-       spin_lock(&ts->lock);
-
-       if (unlikely(!get_pendown_state(ts) ||
-                    device_suspended(&ts->spi->dev))) {
-               if (ts->pendown) {
-                       struct input_dev *input = ts->input;
-
-                       input_report_key(input, BTN_TOUCH, 0);
-                       input_report_abs(input, ABS_PRESSURE, 0);
-                       input_sync(input);
-
-                       ts->pendown = 0;
-                       dev_vdbg(&ts->spi->dev, "UP\n");
-               }
-
-               /* measurement cycle ended */
-               if (!device_suspended(&ts->spi->dev)) {
-                       ts->irq_disabled = 0;
-                       enable_irq(ts->spi->irq);
-               }
-               ts->pending = 0;
-       } else {
-               /* pen is still down, continue with the measurement */
-               ts->msg_idx = 0;
-               ts->wait_for_sync();
-               status = spi_async(ts->spi, &ts->msg[0]);
-               if (status)
-                       dev_err(&ts->spi->dev, "spi_async --> %d\n", status);
-       }
-
-       spin_unlock(&ts->lock);
-       return HRTIMER_NORESTART;
-}
 
 static irqreturn_t ads7846_irq(int irq, void *handle)
 {
        struct ads7846 *ts = handle;
-       unsigned long flags;
-
-       spin_lock_irqsave(&ts->lock, flags);
-       if (likely(get_pendown_state(ts))) {
-               if (!ts->irq_disabled) {
-                       /* The ARM do_simple_IRQ() dispatcher doesn't act
-                        * like the other dispatchers:  it will report IRQs
-                        * even after they've been disabled.  We work around
-                        * that here.  (The "generic irq" framework may help...)
-                        */
-                       ts->irq_disabled = 1;
-                       disable_irq_nosync(ts->spi->irq);
-                       ts->pending = 1;
-                       hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
-                                       HRTIMER_MODE_REL);
-               }
-       }
-       spin_unlock_irqrestore(&ts->lock, flags);
 
-       return IRQ_HANDLED;
-}
+       /* Start with a small delay before checking pendown state */
+       msleep(TS_POLL_DELAY);
 
-/*--------------------------------------------------------------------------*/
+       while (!ts->stopped && get_pendown_state(ts)) {
 
-/* Must be called with ts->lock held */
-static void ads7846_disable(struct ads7846 *ts)
-{
-       if (ts->disabled)
-               return;
+               /* pen is down, continue with the measurement */
+               ads7846_read_state(ts);
 
-       ts->disabled = 1;
+               if (!ts->stopped)
+                       ads7846_report_state(ts);
 
-       /* are we waiting for IRQ, or polling? */
-       if (!ts->pending) {
-               ts->irq_disabled = 1;
-               disable_irq(ts->spi->irq);
-       } else {
-               /* the timer will run at least once more, and
-                * leave everything in a clean state, IRQ disabled
-                */
-               while (ts->pending) {
-                       spin_unlock_irq(&ts->lock);
-                       msleep(1);
-                       spin_lock_irq(&ts->lock);
-               }
+               wait_event_timeout(ts->wait, ts->stopped,
+                                  msecs_to_jiffies(TS_POLL_PERIOD));
        }
 
-       regulator_disable(ts->reg);
-
-       /* we know the chip's in lowpower mode since we always
-        * leave it that way after every request
-        */
-}
+       if (ts->pendown) {
+               struct input_dev *input = ts->input;
 
-/* Must be called with ts->lock held */
-static void ads7846_enable(struct ads7846 *ts)
-{
-       if (!ts->disabled)
-               return;
+               input_report_key(input, BTN_TOUCH, 0);
+               input_report_abs(input, ABS_PRESSURE, 0);
+               input_sync(input);
 
-       regulator_enable(ts->reg);
+               ts->pendown = false;
+               dev_vdbg(&ts->spi->dev, "UP\n");
+       }
 
-       ts->disabled = 0;
-       ts->irq_disabled = 0;
-       enable_irq(ts->spi->irq);
+       return IRQ_HANDLED;
 }
 
 static int ads7846_suspend(struct spi_device *spi, pm_message_t message)
 {
        struct ads7846 *ts = dev_get_drvdata(&spi->dev);
 
-       spin_lock_irq(&ts->lock);
+       mutex_lock(&ts->lock);
 
-       ts->is_suspended = 1;
-       ads7846_disable(ts);
+       if (!ts->suspended) {
 
-       spin_unlock_irq(&ts->lock);
+               if (!ts->disabled)
+                       __ads7846_disable(ts);
 
-       if (device_may_wakeup(&ts->spi->dev))
-               enable_irq_wake(ts->spi->irq);
+               if (device_may_wakeup(&ts->spi->dev))
+                       enable_irq_wake(ts->spi->irq);
 
-       return 0;
+               ts->suspended = true;
+       }
+
+       mutex_unlock(&ts->lock);
 
+       return 0;
 }
 
 static int ads7846_resume(struct spi_device *spi)
 {
        struct ads7846 *ts = dev_get_drvdata(&spi->dev);
 
-       if (device_may_wakeup(&ts->spi->dev))
-               disable_irq_wake(ts->spi->irq);
+       mutex_lock(&ts->lock);
+
+       if (ts->suspended) {
 
-       spin_lock_irq(&ts->lock);
+               ts->suspended = false;
 
-       ts->is_suspended = 0;
-       ads7846_enable(ts);
+               if (device_may_wakeup(&ts->spi->dev))
+                       disable_irq_wake(ts->spi->irq);
 
-       spin_unlock_irq(&ts->lock);
+               if (!ts->disabled)
+                       __ads7846_enable(ts);
+       }
+
+       mutex_unlock(&ts->lock);
 
        return 0;
 }
 
-static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
+static int __devinit ads7846_setup_pendown(struct spi_device *spi, struct ads7846 *ts)
 {
        struct ads7846_platform_data *pdata = spi->dev.platform_data;
        int err;
@@ -932,146 +958,40 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
        err = gpio_request(pdata->gpio_pendown, "ads7846_pendown");
        if (err) {
                dev_err(&spi->dev, "failed to request pendown GPIO%d\n",
-                               pdata->gpio_pendown);
+                       pdata->gpio_pendown);
                return err;
        }
 
        ts->gpio_pendown = pdata->gpio_pendown;
+
        return 0;
 }
 
-static int __devinit ads7846_probe(struct spi_device *spi)
+/*
+ * Set up the transfers to read touchscreen state; this assumes we
+ * use formula #2 for pressure, not #3.
+ */
+static void __devinit ads7846_setup_spi_msg(struct ads7846 *ts,
+                               const struct ads7846_platform_data *pdata)
 {
-       struct ads7846 *ts;
-       struct ads7846_packet *packet;
-       struct input_dev *input_dev;
-       const struct ads7846_platform_data *pdata = spi->dev.platform_data;
-       struct spi_message *m;
-       struct spi_transfer *x;
-       unsigned long irq_flags;
-       int vref;
-       int err;
-
-       if (!spi->irq) {
-               dev_dbg(&spi->dev, "no IRQ?\n");
-               return -ENODEV;
-       }
-
-       if (!pdata) {
-               dev_dbg(&spi->dev, "no platform data?\n");
-               return -ENODEV;
-       }
-
-       /* don't exceed max specified sample rate */
-       if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
-               dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
-                               (spi->max_speed_hz/SAMPLE_BITS)/1000);
-               return -EINVAL;
-       }
-
-       /* We'd set TX wordsize 8 bits and RX wordsize to 13 bits ... except
-        * that even if the hardware can do that, the SPI controller driver
-        * may not.  So we stick to very-portable 8 bit words, both RX and TX.
-        */
-       spi->bits_per_word = 8;
-       spi->mode = SPI_MODE_0;
-       err = spi_setup(spi);
-       if (err < 0)
-               return err;
-
-       ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
-       packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
-       input_dev = input_allocate_device();
-       if (!ts || !packet || !input_dev) {
-               err = -ENOMEM;
-               goto err_free_mem;
-       }
-
-       dev_set_drvdata(&spi->dev, ts);
-
-       ts->packet = packet;
-       ts->spi = spi;
-       ts->input = input_dev;
-       ts->vref_mv = pdata->vref_mv;
-       ts->swap_xy = pdata->swap_xy;
-
-       hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-       ts->timer.function = ads7846_timer;
-
-       spin_lock_init(&ts->lock);
-
-       ts->model = pdata->model ? : 7846;
-       ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
-       ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
-       ts->pressure_max = pdata->pressure_max ? : ~0;
-
-       if (pdata->filter != NULL) {
-               if (pdata->filter_init != NULL) {
-                       err = pdata->filter_init(pdata, &ts->filter_data);
-                       if (err < 0)
-                               goto err_free_mem;
-               }
-               ts->filter = pdata->filter;
-               ts->filter_cleanup = pdata->filter_cleanup;
-       } else if (pdata->debounce_max) {
-               ts->debounce_max = pdata->debounce_max;
-               if (ts->debounce_max < 2)
-                       ts->debounce_max = 2;
-               ts->debounce_tol = pdata->debounce_tol;
-               ts->debounce_rep = pdata->debounce_rep;
-               ts->filter = ads7846_debounce;
-               ts->filter_data = ts;
-       } else
-               ts->filter = ads7846_no_filter;
-
-       err = setup_pendown(spi, ts);
-       if (err)
-               goto err_cleanup_filter;
-
-       if (pdata->penirq_recheck_delay_usecs)
-               ts->penirq_recheck_delay_usecs =
-                               pdata->penirq_recheck_delay_usecs;
-
-       ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
-
-       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
-       snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
-
-       input_dev->name = ts->name;
-       input_dev->phys = ts->phys;
-       input_dev->dev.parent = &spi->dev;
-
-       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_abs_params(input_dev, ABS_X,
-                       pdata->x_min ? : 0,
-                       pdata->x_max ? : MAX_12BIT,
-                       0, 0);
-       input_set_abs_params(input_dev, ABS_Y,
-                       pdata->y_min ? : 0,
-                       pdata->y_max ? : MAX_12BIT,
-                       0, 0);
-       input_set_abs_params(input_dev, ABS_PRESSURE,
-                       pdata->pressure_min, pdata->pressure_max, 0, 0);
-
-       vref = pdata->keep_vref_on;
+       struct spi_message *m = &ts->msg[0];
+       struct spi_transfer *x = ts->xfer;
+       struct ads7846_packet *packet = ts->packet;
+       int vref = pdata->keep_vref_on;
 
        if (ts->model == 7873) {
-               /* The AD7873 is almost identical to the ADS7846
+               /*
+                * The AD7873 is almost identical to the ADS7846
                 * keep VREF off during differential/ratiometric
-                * conversion modes
+                * conversion modes.
                 */
                ts->model = 7846;
                vref = 0;
        }
 
-       /* set up the transfers to read touchscreen state; this assumes we
-        * use formula #2 for pressure, not #3.
-        */
-       m = &ts->msg[0];
-       x = ts->xfer;
-
+       ts->msg_count = 1;
        spi_message_init(m);
+       m->context = ts;
 
        if (ts->model == 7845) {
                packet->read_y_cmd[0] = READ_Y(vref);
@@ -1094,7 +1014,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                spi_message_add_tail(x, m);
        }
 
-       /* the first sample after switching drivers can be low quality;
+       /*
+        * The first sample after switching drivers can be low quality;
         * optionally discard it, using a second one after the signals
         * have had enough time to stabilize.
         */
@@ -1112,11 +1033,10 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                spi_message_add_tail(x, m);
        }
 
-       m->complete = ads7846_rx_val;
-       m->context = ts;
-
+       ts->msg_count++;
        m++;
        spi_message_init(m);
+       m->context = ts;
 
        if (ts->model == 7845) {
                x++;
@@ -1156,13 +1076,12 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                spi_message_add_tail(x, m);
        }
 
-       m->complete = ads7846_rx_val;
-       m->context = ts;
-
        /* turn y+ off, x- on; we'll use formula #2 */
        if (ts->model == 7846) {
+               ts->msg_count++;
                m++;
                spi_message_init(m);
+               m->context = ts;
 
                x++;
                packet->read_z1 = READ_Z1(vref);
@@ -1190,11 +1109,10 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                        spi_message_add_tail(x, m);
                }
 
-               m->complete = ads7846_rx_val;
-               m->context = ts;
-
+               ts->msg_count++;
                m++;
                spi_message_init(m);
+               m->context = ts;
 
                x++;
                packet->read_z2 = READ_Z2(vref);
@@ -1221,14 +1139,13 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                        x->len = 2;
                        spi_message_add_tail(x, m);
                }
-
-               m->complete = ads7846_rx_val;
-               m->context = ts;
        }
 
        /* power down */
+       ts->msg_count++;
        m++;
        spi_message_init(m);
+       m->context = ts;
 
        if (ts->model == 7845) {
                x++;
@@ -1251,11 +1168,119 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
        CS_CHANGE(*x);
        spi_message_add_tail(x, m);
+}
 
-       m->complete = ads7846_rx;
-       m->context = ts;
+static int __devinit ads7846_probe(struct spi_device *spi)
+{
+       struct ads7846 *ts;
+       struct ads7846_packet *packet;
+       struct input_dev *input_dev;
+       struct ads7846_platform_data *pdata = spi->dev.platform_data;
+       unsigned long irq_flags;
+       int err;
+
+       if (!spi->irq) {
+               dev_dbg(&spi->dev, "no IRQ?\n");
+               return -ENODEV;
+       }
+
+       if (!pdata) {
+               dev_dbg(&spi->dev, "no platform data?\n");
+               return -ENODEV;
+       }
+
+       /* don't exceed max specified sample rate */
+       if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
+               dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
+                               (spi->max_speed_hz/SAMPLE_BITS)/1000);
+               return -EINVAL;
+       }
+
+       /* We'd set TX word size 8 bits and RX word size to 13 bits ... except
+        * that even if the hardware can do that, the SPI controller driver
+        * may not.  So we stick to very-portable 8 bit words, both RX and TX.
+        */
+       spi->bits_per_word = 8;
+       spi->mode = SPI_MODE_0;
+       err = spi_setup(spi);
+       if (err < 0)
+               return err;
 
-       ts->last_msg = m;
+       ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
+       packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!ts || !packet || !input_dev) {
+               err = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       dev_set_drvdata(&spi->dev, ts);
+
+       ts->packet = packet;
+       ts->spi = spi;
+       ts->input = input_dev;
+       ts->vref_mv = pdata->vref_mv;
+       ts->swap_xy = pdata->swap_xy;
+
+       mutex_init(&ts->lock);
+       init_waitqueue_head(&ts->wait);
+
+       ts->model = pdata->model ? : 7846;
+       ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
+       ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+       ts->pressure_max = pdata->pressure_max ? : ~0;
+
+       if (pdata->filter != NULL) {
+               if (pdata->filter_init != NULL) {
+                       err = pdata->filter_init(pdata, &ts->filter_data);
+                       if (err < 0)
+                               goto err_free_mem;
+               }
+               ts->filter = pdata->filter;
+               ts->filter_cleanup = pdata->filter_cleanup;
+       } else if (pdata->debounce_max) {
+               ts->debounce_max = pdata->debounce_max;
+               if (ts->debounce_max < 2)
+                       ts->debounce_max = 2;
+               ts->debounce_tol = pdata->debounce_tol;
+               ts->debounce_rep = pdata->debounce_rep;
+               ts->filter = ads7846_debounce_filter;
+               ts->filter_data = ts;
+       } else {
+               ts->filter = ads7846_no_filter;
+       }
+
+       err = ads7846_setup_pendown(spi, ts);
+       if (err)
+               goto err_cleanup_filter;
+
+       if (pdata->penirq_recheck_delay_usecs)
+               ts->penirq_recheck_delay_usecs =
+                               pdata->penirq_recheck_delay_usecs;
+
+       ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
+
+       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+       snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
+
+       input_dev->name = ts->name;
+       input_dev->phys = ts->phys;
+       input_dev->dev.parent = &spi->dev;
+
+       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_abs_params(input_dev, ABS_X,
+                       pdata->x_min ? : 0,
+                       pdata->x_max ? : MAX_12BIT,
+                       0, 0);
+       input_set_abs_params(input_dev, ABS_Y,
+                       pdata->y_min ? : 0,
+                       pdata->y_max ? : MAX_12BIT,
+                       0, 0);
+       input_set_abs_params(input_dev, ABS_PRESSURE,
+                       pdata->pressure_min, pdata->pressure_max, 0, 0);
+
+       ads7846_setup_spi_msg(ts, pdata);
 
        ts->reg = regulator_get(&spi->dev, "vcc");
        if (IS_ERR(ts->reg)) {
@@ -1271,16 +1296,17 @@ static int __devinit ads7846_probe(struct spi_device *spi)
        }
 
        irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
+       irq_flags |= IRQF_ONESHOT;
 
-       err = request_irq(spi->irq, ads7846_irq, irq_flags,
-                         spi->dev.driver->name, ts);
-
+       err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
+                                  irq_flags, spi->dev.driver->name, ts);
        if (err && !pdata->irq_flags) {
                dev_info(&spi->dev,
                        "trying pin change workaround on irq %d\n", spi->irq);
-               err = request_irq(spi->irq, ads7846_irq,
-                                 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                                 spi->dev.driver->name, ts);
+               irq_flags |= IRQF_TRIGGER_RISING;
+               err = request_threaded_irq(spi->irq,
+                                 ads7846_hard_irq, ads7846_irq,
+                                 irq_flags, spi->dev.driver->name, ts);
        }
 
        if (err) {
@@ -1294,7 +1320,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
        dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
 
-       /* take a first sample, leaving nPENIRQ active and vREF off; avoid
+       /*
+        * Take a first sample, leaving nPENIRQ active and vREF off; avoid
         * the touchscreen, in case it's not connected.
         */
        if (ts->model == 7845)
@@ -1340,20 +1367,18 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
 static int __devexit ads7846_remove(struct spi_device *spi)
 {
-       struct ads7846          *ts = dev_get_drvdata(&spi->dev);
+       struct ads7846 *ts = dev_get_drvdata(&spi->dev);
 
        device_init_wakeup(&spi->dev, false);
 
-       ads784x_hwmon_unregister(spi, ts);
-       input_unregister_device(ts->input);
-
-       ads7846_suspend(spi, PMSG_SUSPEND);
-
        sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
 
+       ads7846_disable(ts);
        free_irq(ts->spi->irq, ts);
-       /* suspend left the IRQ disabled */
-       enable_irq(ts->spi->irq);
+
+       input_unregister_device(ts->input);
+
+       ads784x_hwmon_unregister(spi, ts);
 
        regulator_disable(ts->reg);
        regulator_put(ts->reg);
@@ -1368,6 +1393,7 @@ static int __devexit ads7846_remove(struct spi_device *spi)
        kfree(ts);
 
        dev_dbg(&spi->dev, "unregistered touchscreen\n");
+
        return 0;
 }
 
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
new file mode 100644 (file)
index 0000000..ccde586
--- /dev/null
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/input/bu21013.h>
+#include <linux/slab.h>
+
+#define PEN_DOWN_INTR  0
+#define MAX_FINGERS    2
+#define RESET_DELAY    30
+#define PENUP_TIMEOUT  (10)
+#define DELTA_MIN      16
+#define MASK_BITS      0x03
+#define SHIFT_8                8
+#define SHIFT_2                2
+#define LENGTH_OF_BUFFER       11
+#define I2C_RETRY_COUNT        5
+
+#define BU21013_SENSORS_BTN_0_7_REG    0x70
+#define BU21013_SENSORS_BTN_8_15_REG   0x71
+#define BU21013_SENSORS_BTN_16_23_REG  0x72
+#define BU21013_X1_POS_MSB_REG         0x73
+#define BU21013_X1_POS_LSB_REG         0x74
+#define BU21013_Y1_POS_MSB_REG         0x75
+#define BU21013_Y1_POS_LSB_REG         0x76
+#define BU21013_X2_POS_MSB_REG         0x77
+#define BU21013_X2_POS_LSB_REG         0x78
+#define BU21013_Y2_POS_MSB_REG         0x79
+#define BU21013_Y2_POS_LSB_REG         0x7A
+#define BU21013_INT_CLR_REG            0xE8
+#define BU21013_INT_MODE_REG           0xE9
+#define BU21013_GAIN_REG               0xEA
+#define BU21013_OFFSET_MODE_REG                0xEB
+#define BU21013_XY_EDGE_REG            0xEC
+#define BU21013_RESET_REG              0xED
+#define BU21013_CALIB_REG              0xEE
+#define BU21013_DONE_REG               0xEF
+#define BU21013_SENSOR_0_7_REG         0xF0
+#define BU21013_SENSOR_8_15_REG                0xF1
+#define BU21013_SENSOR_16_23_REG       0xF2
+#define BU21013_POS_MODE1_REG          0xF3
+#define BU21013_POS_MODE2_REG          0xF4
+#define BU21013_CLK_MODE_REG           0xF5
+#define BU21013_IDLE_REG               0xFA
+#define BU21013_FILTER_REG             0xFB
+#define BU21013_TH_ON_REG              0xFC
+#define BU21013_TH_OFF_REG             0xFD
+
+
+#define BU21013_RESET_ENABLE           0x01
+
+#define BU21013_SENSORS_EN_0_7         0x3F
+#define BU21013_SENSORS_EN_8_15                0xFC
+#define BU21013_SENSORS_EN_16_23       0x1F
+
+#define BU21013_POS_MODE1_0            0x02
+#define BU21013_POS_MODE1_1            0x04
+#define BU21013_POS_MODE1_2            0x08
+
+#define BU21013_POS_MODE2_ZERO         0x01
+#define BU21013_POS_MODE2_AVG1         0x02
+#define BU21013_POS_MODE2_AVG2         0x04
+#define BU21013_POS_MODE2_EN_XY                0x08
+#define BU21013_POS_MODE2_EN_RAW       0x10
+#define BU21013_POS_MODE2_MULTI                0x80
+
+#define BU21013_CLK_MODE_DIV           0x01
+#define BU21013_CLK_MODE_EXT           0x02
+#define BU21013_CLK_MODE_CALIB         0x80
+
+#define BU21013_IDLET_0                        0x01
+#define BU21013_IDLET_1                        0x02
+#define BU21013_IDLET_2                        0x04
+#define BU21013_IDLET_3                        0x08
+#define BU21013_IDLE_INTERMIT_EN       0x10
+
+#define BU21013_DELTA_0_6      0x7F
+#define BU21013_FILTER_EN      0x80
+
+#define BU21013_INT_MODE_LEVEL 0x00
+#define BU21013_INT_MODE_EDGE  0x01
+
+#define BU21013_GAIN_0         0x01
+#define BU21013_GAIN_1         0x02
+#define BU21013_GAIN_2         0x04
+
+#define BU21013_OFFSET_MODE_DEFAULT    0x00
+#define BU21013_OFFSET_MODE_MOVE       0x01
+#define BU21013_OFFSET_MODE_DISABLE    0x02
+
+#define BU21013_TH_ON_0                0x01
+#define BU21013_TH_ON_1                0x02
+#define BU21013_TH_ON_2                0x04
+#define BU21013_TH_ON_3                0x08
+#define BU21013_TH_ON_4                0x10
+#define BU21013_TH_ON_5                0x20
+#define BU21013_TH_ON_6                0x40
+#define BU21013_TH_ON_7                0x80
+#define BU21013_TH_ON_MAX      0xFF
+
+#define BU21013_TH_OFF_0       0x01
+#define BU21013_TH_OFF_1       0x02
+#define BU21013_TH_OFF_2       0x04
+#define BU21013_TH_OFF_3       0x08
+#define BU21013_TH_OFF_4       0x10
+#define BU21013_TH_OFF_5       0x20
+#define BU21013_TH_OFF_6       0x40
+#define BU21013_TH_OFF_7       0x80
+#define BU21013_TH_OFF_MAX     0xFF
+
+#define BU21013_X_EDGE_0       0x01
+#define BU21013_X_EDGE_1       0x02
+#define BU21013_X_EDGE_2       0x04
+#define BU21013_X_EDGE_3       0x08
+#define BU21013_Y_EDGE_0       0x10
+#define BU21013_Y_EDGE_1       0x20
+#define BU21013_Y_EDGE_2       0x40
+#define BU21013_Y_EDGE_3       0x80
+
+#define BU21013_DONE   0x01
+#define BU21013_NUMBER_OF_X_SENSORS    (6)
+#define BU21013_NUMBER_OF_Y_SENSORS    (11)
+
+#define DRIVER_TP      "bu21013_tp"
+
+/**
+ * struct bu21013_ts_data - touch panel data structure
+ * @client: pointer to the i2c client
+ * @wait: variable to wait_queue_head_t structure
+ * @touch_stopped: touch stop flag
+ * @chip: pointer to the touch panel controller
+ * @in_dev: pointer to the input device structure
+ * @intr_pin: interrupt pin value
+ *
+ * Touch panel device data structure
+ */
+struct bu21013_ts_data {
+       struct i2c_client *client;
+       wait_queue_head_t wait;
+       bool touch_stopped;
+       const struct bu21013_platform_device *chip;
+       struct input_dev *in_dev;
+       unsigned int intr_pin;
+};
+
+/**
+ * bu21013_read_block_data(): read the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ * @buf: byte pointer
+ *
+ * Read the touch co-ordinates using i2c read block into buffer
+ * and returns integer.
+ */
+static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
+{
+       int ret, i;
+
+       for (i = 0; i < I2C_RETRY_COUNT; i++) {
+               ret = i2c_smbus_read_i2c_block_data
+                       (data->client, BU21013_SENSORS_BTN_0_7_REG,
+                               LENGTH_OF_BUFFER, buf);
+               if (ret == LENGTH_OF_BUFFER)
+                       return 0;
+       }
+       return -EINVAL;
+}
+
+/**
+ * bu21013_do_touch_report(): Get the touch co-ordinates
+ * @data: bu21013_ts_data structure pointer
+ *
+ * Get the touch co-ordinates from touch sensor registers and writes
+ * into device structure and returns integer.
+ */
+static int bu21013_do_touch_report(struct bu21013_ts_data *data)
+{
+       u8      buf[LENGTH_OF_BUFFER];
+       unsigned int pos_x[2], pos_y[2];
+       bool    has_x_sensors, has_y_sensors;
+       int     finger_down_count = 0;
+       int     i;
+
+       if (data == NULL)
+               return -EINVAL;
+
+       if (bu21013_read_block_data(data, buf) < 0)
+               return -EINVAL;
+
+       has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
+       has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
+               ((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
+       if (!has_x_sensors || !has_y_sensors)
+               return 0;
+
+       for (i = 0; i < MAX_FINGERS; i++) {
+               const u8 *p = &buf[4 * i + 3];
+               unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
+               unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
+               if (x == 0 || y == 0)
+                       continue;
+               pos_x[finger_down_count] = x;
+               pos_y[finger_down_count] = y;
+               finger_down_count++;
+       }
+
+       if (finger_down_count) {
+               if (finger_down_count == 2 &&
+                   (abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
+                    abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
+                       return 0;
+               }
+
+               for (i = 0; i < finger_down_count; i++) {
+                       if (data->chip->x_flip)
+                               pos_x[i] = data->chip->touch_x_max - pos_x[i];
+                       if (data->chip->y_flip)
+                               pos_y[i] = data->chip->touch_y_max - pos_y[i];
+
+                       input_report_abs(data->in_dev,
+                                        ABS_MT_POSITION_X, pos_x[i]);
+                       input_report_abs(data->in_dev,
+                                        ABS_MT_POSITION_Y, pos_y[i]);
+                       input_mt_sync(data->in_dev);
+               }
+       } else
+               input_mt_sync(data->in_dev);
+
+       input_sync(data->in_dev);
+
+       return 0;
+}
+/**
+ * bu21013_gpio_irq() - gpio thread function for touch interrupt
+ * @irq: irq value
+ * @device_data: void pointer
+ *
+ * This gpio thread function for touch interrupt
+ * and returns irqreturn_t.
+ */
+static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
+{
+       struct bu21013_ts_data *data = device_data;
+       struct i2c_client *i2c = data->client;
+       int retval;
+
+       do {
+               retval = bu21013_do_touch_report(data);
+               if (retval < 0) {
+                       dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
+                       return IRQ_NONE;
+               }
+
+               data->intr_pin = data->chip->irq_read_val();
+               if (data->intr_pin == PEN_DOWN_INTR)
+                       wait_event_timeout(data->wait, data->touch_stopped,
+                                          msecs_to_jiffies(2));
+       } while (!data->intr_pin && !data->touch_stopped);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * bu21013_init_chip() - power on sequence for the bu21013 controller
+ * @data: device structure pointer
+ *
+ * This function is used to power on
+ * the bu21013 controller and returns integer.
+ */
+static int bu21013_init_chip(struct bu21013_ts_data *data)
+{
+       int retval;
+       struct i2c_client *i2c = data->client;
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
+                                       BU21013_RESET_ENABLE);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
+               return retval;
+       }
+       msleep(RESET_DELAY);
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
+                                       BU21013_SENSORS_EN_0_7);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
+                                               BU21013_SENSORS_EN_8_15);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
+                                               BU21013_SENSORS_EN_16_23);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
+                               (BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
+                       (BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
+                       BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
+                       BU21013_POS_MODE2_MULTI));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
+               return retval;
+       }
+
+       if (data->chip->ext_clk)
+               retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+                       (BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
+       else
+               retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
+                       (BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
+                               (BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
+                                               BU21013_INT_MODE_LEVEL);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
+                                               (BU21013_DELTA_0_6 |
+                                                       BU21013_FILTER_EN));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
+                                       BU21013_TH_ON_5);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
+                               BU21013_TH_OFF_4 || BU21013_TH_OFF_3);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
+                                       (BU21013_GAIN_0 | BU21013_GAIN_1));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
+                                       BU21013_OFFSET_MODE_DEFAULT);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
+                               (BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
+                               BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
+               return retval;
+       }
+
+       retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
+                                                       BU21013_DONE);
+       if (retval < 0) {
+               dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
+               return retval;
+       }
+
+       return 0;
+}
+
+/**
+ * bu21013_free_irq() - frees IRQ registered for touchscreen
+ * @bu21013_data: device structure pointer
+ *
+ * This function signals interrupt thread to stop processing and
+ * frees interrupt.
+ */
+static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
+{
+       bu21013_data->touch_stopped = true;
+       wake_up(&bu21013_data->wait);
+       free_irq(bu21013_data->chip->irq, bu21013_data);
+}
+
+/**
+ * bu21013_probe() - initializes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ * @id: i2c device id pointer
+ *
+ * This function used to initializes the i2c-client touchscreen
+ * driver and returns integer.
+ */
+static int __devinit bu21013_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct bu21013_ts_data *bu21013_data;
+       struct input_dev *in_dev;
+       const struct bu21013_platform_device *pdata =
+                                       client->dev.platform_data;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter,
+                                       I2C_FUNC_SMBUS_BYTE_DATA)) {
+               dev_err(&client->dev, "i2c smbus byte data not supported\n");
+               return -EIO;
+       }
+
+       if (!pdata) {
+               dev_err(&client->dev, "platform data not defined\n");
+               return -EINVAL;
+       }
+
+       bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
+       in_dev = input_allocate_device();
+       if (!bu21013_data || !in_dev) {
+               dev_err(&client->dev, "device memory alloc failed\n");
+               error = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       bu21013_data->in_dev = in_dev;
+       bu21013_data->chip = pdata;
+       bu21013_data->client = client;
+       bu21013_data->touch_stopped = false;
+       init_waitqueue_head(&bu21013_data->wait);
+
+       /* configure the gpio pins */
+       if (pdata->cs_en) {
+               error = pdata->cs_en(pdata->cs_pin);
+               if (error < 0) {
+                       dev_err(&client->dev, "chip init failed\n");
+                       goto err_free_mem;
+               }
+       }
+
+       /* configure the touch panel controller */
+       error = bu21013_init_chip(bu21013_data);
+       if (error) {
+               dev_err(&client->dev, "error in bu21013 config\n");
+               goto err_cs_disable;
+       }
+
+       /* register the device to input subsystem */
+       in_dev->name = DRIVER_TP;
+       in_dev->id.bustype = BUS_I2C;
+       in_dev->dev.parent = &client->dev;
+
+       __set_bit(EV_SYN, in_dev->evbit);
+       __set_bit(EV_KEY, in_dev->evbit);
+       __set_bit(EV_ABS, in_dev->evbit);
+
+       input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
+                                               pdata->x_max_res, 0, 0);
+       input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
+                                               pdata->y_max_res, 0, 0);
+       input_set_drvdata(in_dev, bu21013_data);
+
+       error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq,
+                                    IRQF_TRIGGER_FALLING | IRQF_SHARED,
+                                    DRIVER_TP, bu21013_data);
+       if (error) {
+               dev_err(&client->dev, "request irq %d failed\n", pdata->irq);
+               goto err_cs_disable;
+       }
+
+       error = input_register_device(in_dev);
+       if (error) {
+               dev_err(&client->dev, "failed to register input device\n");
+               goto err_free_irq;
+       }
+
+       device_init_wakeup(&client->dev, pdata->wakeup);
+       i2c_set_clientdata(client, bu21013_data);
+
+       return 0;
+
+err_free_irq:
+       bu21013_free_irq(bu21013_data);
+err_cs_disable:
+       pdata->cs_dis(pdata->cs_pin);
+err_free_mem:
+       input_free_device(bu21013_data->in_dev);
+       kfree(bu21013_data);
+
+       return error;
+}
+/**
+ * bu21013_remove() - removes the i2c-client touchscreen driver
+ * @client: i2c client structure pointer
+ *
+ * This function uses to remove the i2c-client
+ * touchscreen driver and returns integer.
+ */
+static int __devexit bu21013_remove(struct i2c_client *client)
+{
+       struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
+
+       bu21013_free_irq(bu21013_data);
+
+       bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin);
+
+       input_unregister_device(bu21013_data->in_dev);
+       kfree(bu21013_data);
+
+       device_init_wakeup(&client->dev, false);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * bu21013_suspend() - suspend the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to suspend the
+ * touch panel controller and returns integer
+ */
+static int bu21013_suspend(struct device *dev)
+{
+       struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+       struct i2c_client *client = bu21013_data->client;
+
+       bu21013_data->touch_stopped = true;
+       if (device_may_wakeup(&client->dev))
+               enable_irq_wake(bu21013_data->chip->irq);
+       else
+               disable_irq(bu21013_data->chip->irq);
+
+       return 0;
+}
+
+/**
+ * bu21013_resume() - resume the touch screen controller
+ * @dev: pointer to device structure
+ *
+ * This function is used to resume the touch panel
+ * controller and returns integer.
+ */
+static int bu21013_resume(struct device *dev)
+{
+       struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
+       struct i2c_client *client = bu21013_data->client;
+       int retval;
+
+       retval = bu21013_init_chip(bu21013_data);
+       if (retval < 0) {
+               dev_err(&client->dev, "bu21013 controller config failed\n");
+               return retval;
+       }
+
+       bu21013_data->touch_stopped = false;
+
+       if (device_may_wakeup(&client->dev))
+               disable_irq_wake(bu21013_data->chip->irq);
+       else
+               enable_irq(bu21013_data->chip->irq);
+
+       return 0;
+}
+
+static const struct dev_pm_ops bu21013_dev_pm_ops = {
+       .suspend = bu21013_suspend,
+       .resume  = bu21013_resume,
+};
+#endif
+
+static const struct i2c_device_id bu21013_id[] = {
+       { DRIVER_TP, 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, bu21013_id);
+
+static struct i2c_driver bu21013_driver = {
+       .driver = {
+               .name   =       DRIVER_TP,
+               .owner  =       THIS_MODULE,
+#ifdef CONFIG_PM
+               .pm     =       &bu21013_dev_pm_ops,
+#endif
+       },
+       .probe          =       bu21013_probe,
+       .remove         =       __devexit_p(bu21013_remove),
+       .id_table       =       bu21013_id,
+};
+
+/**
+ * bu21013_init() - initializes the bu21013 touchscreen driver
+ *
+ * This function used to initializes the bu21013
+ * touchscreen driver and returns integer.
+ */
+static int __init bu21013_init(void)
+{
+       return i2c_add_driver(&bu21013_driver);
+}
+
+/**
+ * bu21013_exit() - de-initializes the bu21013 touchscreen driver
+ *
+ * This function uses to de-initializes the bu21013
+ * touchscreen driver and returns none.
+ */
+static void __exit bu21013_exit(void)
+{
+       i2c_del_driver(&bu21013_driver);
+}
+
+module_init(bu21013_init);
+module_exit(bu21013_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
+MODULE_DESCRIPTION("bu21013 touch screen controller driver");
index 5ec0946938fe71e105dc519191debb8cc5c607c1..d0c3a7229adf3f9f1ee575d853a90fb3df54facf 100644 (file)
@@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,
        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
 
        input_set_abs_params(input_dev, ABS_X,
-                       CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0);
+                       CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
        input_set_abs_params(input_dev, ABS_Y,
-                       CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0);
+                       CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
 
        if (ts->reset_pin) {
                err = gpio_request(ts->reset_pin, NULL);
index a89700e7ace42138e10a6a47e085a4b90a6f9c15..498bd62af09a284a8b8c8eb3bdc703af3a07e014 100644 (file)
@@ -107,8 +107,7 @@ static int __init hp680_ts_init(void)
        return 0;
 
  fail2:        free_irq(HP680_TS_IRQ, NULL);
-       cancel_delayed_work(&work);
-       flush_scheduled_work();
+       cancel_delayed_work_sync(&work);
  fail1:        input_free_device(hp680_ts_dev);
        return err;
 }
@@ -116,8 +115,7 @@ static int __init hp680_ts_init(void)
 static void __exit hp680_ts_exit(void)
 {
        free_irq(HP680_TS_IRQ, NULL);
-       cancel_delayed_work(&work);
-       flush_scheduled_work();
+       cancel_delayed_work_sync(&work);
        input_unregister_device(hp680_ts_dev);
 }
 
diff --git a/drivers/input/touchscreen/intel-mid-touch.c b/drivers/input/touchscreen/intel-mid-touch.c
new file mode 100644 (file)
index 0000000..c0307b2
--- /dev/null
@@ -0,0 +1,687 @@
+/*
+ * Intel MID Resistive Touch Screen Driver
+ *
+ * Copyright (C) 2008 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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; version 2 of the License.
+ *
+ * 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.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
+ *                         Ramesh Agarwal (ramesh.agarwal@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * TODO:
+ *     review conversion of r/m/w sequences
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/param.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <asm/intel_scu_ipc.h>
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_ID1           0x00 /* PMIC ID1 register */
+
+/* PMIC Interrupt registers */
+#define PMIC_REG_INT           0x04 /* PMIC interrupt register */
+#define PMIC_REG_MINT          0x05 /* PMIC interrupt mask register */
+
+/* ADC Interrupt registers */
+#define PMIC_REG_ADCINT                0x5F /* ADC interrupt register */
+#define PMIC_REG_MADCINT       0x60 /* ADC interrupt mask register */
+
+/* ADC Control registers */
+#define PMIC_REG_ADCCNTL1      0x61 /* ADC control register */
+
+/* ADC Channel Selection registers */
+#define PMICADDR0              0xA4
+#define END_OF_CHANNEL         0x1F
+
+/* ADC Result register */
+#define PMIC_REG_ADCSNS0H      0x64
+
+/* ADC channels for touch screen */
+#define MRST_TS_CHAN10         0xA /* Touch screen X+ connection */
+#define MRST_TS_CHAN11         0xB /* Touch screen X- connection */
+#define MRST_TS_CHAN12         0xC /* Touch screen Y+ connection */
+#define MRST_TS_CHAN13         0xD /* Touch screen Y- connection */
+
+/* Touch screen channel BIAS constants */
+#define MRST_XBIAS             0x20
+#define MRST_YBIAS             0x40
+#define MRST_ZBIAS             0x80
+
+/* Touch screen coordinates */
+#define MRST_X_MIN             10
+#define MRST_X_MAX             1024
+#define MRST_X_FUZZ            5
+#define MRST_Y_MIN             10
+#define MRST_Y_MAX             1024
+#define MRST_Y_FUZZ            5
+#define MRST_PRESSURE_MIN      0
+#define MRST_PRESSURE_NOMINAL  50
+#define MRST_PRESSURE_MAX      100
+
+#define WAIT_ADC_COMPLETION    10 /* msec */
+
+/* PMIC ADC round robin delays */
+#define ADC_LOOP_DELAY0                0x0 /* Continuous loop */
+#define ADC_LOOP_DELAY1                0x1 /* 4.5  ms approximate */
+
+/* PMIC Vendor Identifiers */
+#define PMIC_VENDOR_FS         0 /* PMIC vendor FreeScale */
+#define PMIC_VENDOR_MAXIM      1 /* PMIC vendor MAXIM */
+#define PMIC_VENDOR_NEC                2 /* PMIC vendor NEC */
+#define MRSTOUCH_MAX_CHANNELS  32 /* Maximum ADC channels */
+
+/* Touch screen device structure */
+struct mrstouch_dev {
+       struct device *dev; /* device associated with touch screen */
+       struct input_dev *input;
+       char phys[32];
+       u16 asr;                /* Address selection register */
+       int irq;
+       unsigned int vendor;    /* PMIC vendor */
+       unsigned int rev;       /* PMIC revision */
+
+       int (*read_prepare)(struct mrstouch_dev *tsdev);
+       int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
+       int (*read_finish)(struct mrstouch_dev *tsdev);
+};
+
+
+/*************************** NEC and Maxim Interface ************************/
+
+static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+       return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
+}
+
+/*
+ * Enables PENDET interrupt.
+ */
+static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+       int err;
+
+       err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
+       if (!err)
+               err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
+
+       return err;
+}
+
+/*
+ * Reads PMIC ADC touch screen result
+ * Reads ADC storage registers for higher 7 and lower 3 bits and
+ * converts the two readings into a single value and turns off gain bit
+ */
+static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
+{
+       int err;
+       u16 result;
+       u32 res;
+
+       result = PMIC_REG_ADCSNS0H + offset;
+
+       if (chan == MRST_TS_CHAN12)
+               result += 4;
+
+       err = intel_scu_ipc_ioread32(result, &res);
+       if (err)
+               return err;
+
+       /* Mash the bits up */
+
+       *vp = (res & 0xFF) << 3;        /* Highest 7 bits */
+       *vp |= (res >> 8) & 0x07;       /* Lower 3 bits */
+       *vp &= 0x3FF;
+
+       res >>= 16;
+
+       *vm = (res & 0xFF) << 3;        /* Highest 7 bits */
+       *vm |= (res >> 8) & 0x07;       /* Lower 3 bits */
+       *vm &= 0x3FF;
+
+       return 0;
+}
+
+/*
+ * Enables X, Y and Z bias values
+ * Enables YPYM for X channels and XPXM for Y channels
+ */
+static int mrstouch_ts_bias_set(uint offset, uint bias)
+{
+       int count;
+       u16 chan, start;
+       u16 reg[4];
+       u8 data[4];
+
+       chan = PMICADDR0 + offset;
+       start = MRST_TS_CHAN10;
+
+       for (count = 0; count <= 3; count++) {
+               reg[count] = chan++;
+               data[count] = bias | (start + count);
+       }
+
+       return intel_scu_ipc_writev(reg, data, 4);
+}
+
+/* To read touch screen channel values */
+static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
+                                u16 *x, u16 *y, u16 *z)
+{
+       int err;
+       u16 xm, ym, zm;
+
+       /* configure Y bias for X channels */
+       err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
+       if (err)
+               goto ipc_error;
+
+       msleep(WAIT_ADC_COMPLETION);
+
+       /* read x+ and x- channels */
+       err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
+       if (err)
+               goto ipc_error;
+
+       /* configure x bias for y channels */
+       err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
+       if (err)
+               goto ipc_error;
+
+       msleep(WAIT_ADC_COMPLETION);
+
+       /* read y+ and y- channels */
+       err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
+       if (err)
+               goto ipc_error;
+
+       /* configure z bias for x and y channels */
+       err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
+       if (err)
+               goto ipc_error;
+
+       msleep(WAIT_ADC_COMPLETION);
+
+       /* read z+ and z- channels */
+       err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
+       if (err)
+               goto ipc_error;
+
+       return 0;
+
+ipc_error:
+       dev_err(tsdev->dev, "ipc error during adc read\n");
+       return err;
+}
+
+
+/*************************** Freescale Interface ************************/
+
+static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
+{
+       int err, count;
+       u16 chan;
+       u16 reg[5];
+       u8 data[5];
+
+       /* Stop the ADC */
+       err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
+       if (err)
+               goto ipc_error;
+
+       chan = PMICADDR0 + tsdev->asr;
+
+       /* Set X BIAS */
+       for (count = 0; count <= 3; count++) {
+               reg[count] = chan++;
+               data[count] = 0x2A;
+       }
+       reg[count] =  chan++; /* Dummy */
+       data[count] = 0;
+
+       err = intel_scu_ipc_writev(reg, data, 5);
+       if (err)
+               goto ipc_error;
+
+       msleep(WAIT_ADC_COMPLETION);
+
+       /* Set Y BIAS */
+       for (count = 0; count <= 3; count++) {
+               reg[count] = chan++;
+               data[count] = 0x4A;
+       }
+       reg[count] = chan++; /* Dummy */
+       data[count] = 0;
+
+       err = intel_scu_ipc_writev(reg, data, 5);
+       if (err)
+               goto ipc_error;
+
+       msleep(WAIT_ADC_COMPLETION);
+
+       /* Set Z BIAS */
+       err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
+       if (err)
+               goto ipc_error;
+
+       msleep(WAIT_ADC_COMPLETION);
+
+       return 0;
+
+ipc_error:
+       dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+       return err;
+}
+
+static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
+                               u16 *x, u16 *y, u16 *z)
+{
+       int err;
+       u16 result;
+       u16 reg[4];
+       u8 data[4];
+
+       result = PMIC_REG_ADCSNS0H + tsdev->asr;
+
+       reg[0] = result + 4;
+       reg[1] = result + 5;
+       reg[2] = result + 16;
+       reg[3] = result + 17;
+
+       err = intel_scu_ipc_readv(reg, data, 4);
+       if (err)
+               goto ipc_error;
+
+       *x = data[0] << 3; /* Higher 7 bits */
+       *x |= data[1] & 0x7; /* Lower 3 bits */
+       *x &= 0x3FF;
+
+       *y = data[2] << 3; /* Higher 7 bits */
+       *y |= data[3] & 0x7; /* Lower 3 bits */
+       *y &= 0x3FF;
+
+       /* Read Z value */
+       reg[0] = result + 28;
+       reg[1] = result + 29;
+
+       err = intel_scu_ipc_readv(reg, data, 4);
+       if (err)
+               goto ipc_error;
+
+       *z = data[0] << 3; /* Higher 7 bits */
+       *z |= data[1] & 0x7; /* Lower 3 bits */
+       *z &= 0x3FF;
+
+       return 0;
+
+ipc_error:
+       dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+       return err;
+}
+
+static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
+{
+       int err, count;
+       u16 chan;
+       u16 reg[5];
+       u8 data[5];
+
+       /* Clear all TS channels */
+       chan = PMICADDR0 + tsdev->asr;
+       for (count = 0; count <= 4; count++) {
+               reg[count] = chan++;
+               data[count] = 0;
+       }
+       err = intel_scu_ipc_writev(reg, data, 5);
+       if (err)
+               goto ipc_error;
+
+       for (count = 0; count <= 4; count++) {
+               reg[count] = chan++;
+               data[count] = 0;
+       }
+       err = intel_scu_ipc_writev(reg, data, 5);
+       if (err)
+               goto ipc_error;
+
+       err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
+       if (err)
+               goto ipc_error;
+
+       /* Start ADC */
+       err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
+       if (err)
+               goto ipc_error;
+
+       return 0;
+
+ipc_error:
+       dev_err(tsdev->dev, "ipc error during %s\n", __func__);
+       return err;
+}
+
+static void mrstouch_report_event(struct input_dev *input,
+                       unsigned int x, unsigned int y, unsigned int z)
+{
+       if (z > MRST_PRESSURE_NOMINAL) {
+               /* Pen touched, report button touch and coordinates */
+               input_report_key(input, BTN_TOUCH, 1);
+               input_report_abs(input, ABS_X, x);
+               input_report_abs(input, ABS_Y, y);
+       } else {
+               input_report_key(input, BTN_TOUCH, 0);
+       }
+
+       input_report_abs(input, ABS_PRESSURE, z);
+       input_sync(input);
+}
+
+/* PENDET interrupt handler */
+static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
+{
+       struct mrstouch_dev *tsdev = dev_id;
+       u16 x, y, z;
+
+       /*
+        * Should we lower thread priority? Probably not, since we are
+        * not spinning but sleeping...
+        */
+
+       if (tsdev->read_prepare(tsdev))
+               goto out;
+
+       do {
+               if (tsdev->read(tsdev, &x, &y, &z))
+                       break;
+
+               mrstouch_report_event(tsdev->input, x, y, z);
+       } while (z > MRST_PRESSURE_NOMINAL);
+
+       tsdev->read_finish(tsdev);
+
+out:
+       return IRQ_HANDLED;
+}
+
+/* Utility to read PMIC ID */
+static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)
+{
+       int err;
+       u8 r;
+
+       err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
+       if (err)
+               return err;
+
+       *vendor = r & 0x7;
+       *rev = (r >> 3) & 0x7;
+
+       return 0;
+}
+
+/*
+ * Parse ADC channels to find end of the channel configured by other ADC user
+ * NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
+ */
+static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
+{
+       int err, i, found;
+       u8 r8;
+
+       found = -1;
+
+       for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
+               if (found >= 0)
+                       break;
+
+               err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
+               if (err)
+                       return err;
+
+               if (r8 == END_OF_CHANNEL) {
+                       found = i;
+                       break;
+               }
+       }
+       if (found < 0)
+               return 0;
+
+       if (tsdev->vendor == PMIC_VENDOR_FS) {
+               if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
+                       return -ENOSPC;
+       } else {
+               if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
+                       return -ENOSPC;
+       }
+       return found;
+}
+
+
+/*
+ * Writes touch screen channels to ADC address selection registers
+ */
+static int __devinit mrstouch_ts_chan_set(uint offset)
+{
+       u16 chan;
+
+       int ret, count;
+
+       chan = PMICADDR0 + offset;
+       for (count = 0; count <= 3; count++) {
+               ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
+               if (ret)
+                       return ret;
+       }
+       return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
+}
+
+/* Initialize ADC */
+static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)
+{
+       int err, start;
+       u8 ra, rm;
+
+       err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
+       if (err) {
+               dev_err(tsdev->dev, "Unable to read PMIC id\n");
+               return err;
+       }
+
+       switch (tsdev->vendor) {
+       case PMIC_VENDOR_NEC:
+       case PMIC_VENDOR_MAXIM:
+               tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
+               tsdev->read = mrstouch_nec_adc_read;
+               tsdev->read_finish = mrstouch_nec_adc_read_finish;
+               break;
+
+       case PMIC_VENDOR_FS:
+               tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
+               tsdev->read = mrstouch_fs_adc_read;
+               tsdev->read_finish = mrstouch_fs_adc_read_finish;
+               break;
+
+       default:
+               dev_err(tsdev->dev,
+                       "Unsupported touchscreen: %d\n", tsdev->vendor);
+               return -ENXIO;
+       }
+
+       start = mrstouch_chan_parse(tsdev);
+       if (start < 0) {
+               dev_err(tsdev->dev, "Unable to parse channels\n");
+               return start;
+       }
+
+       tsdev->asr = start;
+
+       /*
+        * ADC power on, start, enable PENDET and set loop delay
+        * ADC loop delay is set to 4.5 ms approximately
+        * Loop delay more than this results in jitter in adc readings
+        * Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
+        * interrupt generation sometimes.
+        */
+
+       if (tsdev->vendor == PMIC_VENDOR_FS) {
+               ra = 0xE0 | ADC_LOOP_DELAY0;
+               rm = 0x5;
+       } else {
+               /* NEC and MAXIm not consistent with loop delay 0 */
+               ra = 0xE0 | ADC_LOOP_DELAY1;
+               rm = 0x0;
+
+               /* configure touch screen channels */
+               err = mrstouch_ts_chan_set(tsdev->asr);
+               if (err)
+                       return err;
+       }
+
+       err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
+       if (err)
+               return err;
+
+       err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
+       if (err)
+               return err;
+
+       return 0;
+}
+
+
+/* Probe function for touch screen driver */
+static int __devinit mrstouch_probe(struct platform_device *pdev)
+{
+       struct mrstouch_dev *tsdev;
+       struct input_dev *input;
+       int err;
+       int irq;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no interrupt assigned\n");
+               return -EINVAL;
+       }
+
+       tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
+       input = input_allocate_device();
+       if (!tsdev || !input) {
+               dev_err(&pdev->dev, "unable to allocate memory\n");
+               err = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       tsdev->dev = &pdev->dev;
+       tsdev->input = input;
+       tsdev->irq = irq;
+
+       snprintf(tsdev->phys, sizeof(tsdev->phys),
+                "%s/input0", dev_name(tsdev->dev));
+
+       err = mrstouch_adc_init(tsdev);
+       if (err) {
+               dev_err(&pdev->dev, "ADC initialization failed\n");
+               goto err_free_mem;
+       }
+
+       input->name = "mrst_touchscreen";
+       input->phys = tsdev->phys;
+       input->dev.parent = tsdev->dev;
+
+       input->id.vendor = tsdev->vendor;
+       input->id.version = tsdev->rev;
+
+       input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+       input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+       input_set_abs_params(tsdev->input, ABS_X,
+                            MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
+       input_set_abs_params(tsdev->input, ABS_Y,
+                            MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
+       input_set_abs_params(tsdev->input, ABS_PRESSURE,
+                            MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
+
+       err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq,
+                                  0, "mrstouch", tsdev);
+       if (err) {
+               dev_err(tsdev->dev, "unable to allocate irq\n");
+               goto err_free_mem;
+       }
+
+       err = input_register_device(tsdev->input);
+       if (err) {
+               dev_err(tsdev->dev, "unable to register input device\n");
+               goto err_free_irq;
+       }
+
+       platform_set_drvdata(pdev, tsdev);
+       return 0;
+
+err_free_irq:
+       free_irq(tsdev->irq, tsdev);
+err_free_mem:
+       input_free_device(input);
+       kfree(tsdev);
+       return err;
+}
+
+static int __devexit mrstouch_remove(struct platform_device *pdev)
+{
+       struct mrstouch_dev *tsdev = platform_get_drvdata(pdev);
+
+       free_irq(tsdev->irq, tsdev);
+       input_unregister_device(tsdev->input);
+       kfree(tsdev);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver mrstouch_driver = {
+       .driver = {
+               .name   = "pmic_touch",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = mrstouch_probe,
+       .remove         = __devexit_p(mrstouch_remove),
+};
+
+static int __init mrstouch_init(void)
+{
+       return platform_driver_register(&mrstouch_driver);
+}
+module_init(mrstouch_init);
+
+static void __exit mrstouch_exit(void)
+{
+       platform_driver_unregister(&mrstouch_driver);
+}
+module_exit(mrstouch_exit);
+
+MODULE_AUTHOR("Sreedhara Murthy. D.S, sreedhara.ds@intel.com");
+MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/lpc32xx_ts.c b/drivers/input/touchscreen/lpc32xx_ts.c
new file mode 100644 (file)
index 0000000..