Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Linus Torvalds [Mon, 13 Apr 2009 18:37:23 +0000 (11:37 -0700)]
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (22 commits)
  Input: i8042 - add HP DV9700 to the noloop list
  Input: arrange drivers/input/misc/Makefile in alphabetical order
  Input: add AD7879 Touchscreen driver
  Input: add AD7877 touchscreen driver
  Input: bf54x-keys - fix typo in warning
  Input: add driver for S1 button of rb532
  Input: generic driver for rotary encoders on GPIOs
  Input: hilkbd - fix crash when removing hilkbd module
  Input: atkbd - add quirk for Fujitsu Siemens Amilo PA 1510
  Input: atkbd - consolidate force release quirk setup
  Input: add accelerated touchscreen support for Marvell Zylonite
  Input: ucb1400_ts, mainstone-wm97xx - add BTN_TOUCH events
  Input: wm97xx - use disable_irq_nosync() for Mainstone
  Input: wm97xx - add BTN_TOUCH event to wm97xx to use it with Android
  Input: fix polling of /proc/bus/input/devices
  Input: psmouse - add newline to OLPC HGPK touchpad debugging
  Input: ati_remote2 - check module params
  Input: ati_remote2 - add per device attrs
  Input: ati_remote2 - complete suspend support
  Input: stop autorepeat timer on key release
  ...

28 files changed:
Documentation/input/rotary-encoder.txt [new file with mode: 0644]
arch/mips/include/asm/mach-rc32434/gpio.h
arch/mips/rb532/devices.c
drivers/input/input.c
drivers/input/keyboard/atkbd.c
drivers/input/keyboard/bf54x-keys.c
drivers/input/keyboard/hilkbd.c
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/ati_remote2.c
drivers/input/misc/rb532_button.c [new file with mode: 0644]
drivers/input/misc/rotary_encoder.c [new file with mode: 0644]
drivers/input/mouse/Kconfig
drivers/input/mouse/Makefile
drivers/input/mouse/hgpk.c
drivers/input/mouse/maplemouse.c [new file with mode: 0644]
drivers/input/mouse/pc110pad.c
drivers/input/serio/i8042-x86ia64io.h
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ad7877.c [new file with mode: 0644]
drivers/input/touchscreen/ad7879.c [new file with mode: 0644]
drivers/input/touchscreen/mainstone-wm97xx.c
drivers/input/touchscreen/ucb1400_ts.c
drivers/input/touchscreen/wm97xx-core.c
drivers/input/touchscreen/zylonite-wm97xx.c [new file with mode: 0644]
include/linux/rotary_encoder.h [new file with mode: 0644]
include/linux/spi/ad7879.h [new file with mode: 0644]

diff --git a/Documentation/input/rotary-encoder.txt b/Documentation/input/rotary-encoder.txt
new file mode 100644 (file)
index 0000000..435102a
--- /dev/null
@@ -0,0 +1,101 @@
+rotary-encoder - a generic driver for GPIO connected devices
+Daniel Mack <daniel@caiaq.de>, Feb 2009
+
+0. Function
+-----------
+
+Rotary encoders are devices which are connected to the CPU or other
+peripherals with two wires. The outputs are phase-shifted by 90 degrees
+and by triggering on falling and rising edges, the turn direction can
+be determined.
+
+The phase diagram of these two outputs look like this:
+
+                  _____       _____       _____
+                 |     |     |     |     |     |
+  Channel A  ____|     |_____|     |_____|     |____
+
+                 :  :  :  :  :  :  :  :  :  :  :  :
+            __       _____       _____       _____
+              |     |     |     |     |     |     |
+  Channel B   |_____|     |_____|     |_____|     |__
+
+                 :  :  :  :  :  :  :  :  :  :  :  :
+  Event          a  b  c  d  a  b  c  d  a  b  c  d
+
+                |<-------->|
+                 one step
+
+
+For more information, please see
+       http://en.wikipedia.org/wiki/Rotary_encoder
+
+
+1. Events / state machine
+-------------------------
+
+a) Rising edge on channel A, channel B in low state
+       This state is used to recognize a clockwise turn
+
+b) Rising edge on channel B, channel A in high state
+       When entering this state, the encoder is put into 'armed' state,
+       meaning that there it has seen half the way of a one-step transition.
+
+c) Falling edge on channel A, channel B in high state
+       This state is used to recognize a counter-clockwise turn
+
+d) Falling edge on channel B, channel A in low state
+       Parking position. If the encoder enters this state, a full transition
+       should have happend, unless it flipped back on half the way. The
+       'armed' state tells us about that.
+
+2. Platform requirements
+------------------------
+
+As there is no hardware dependent call in this driver, the platform it is
+used with must support gpiolib. Another requirement is that IRQs must be
+able to fire on both edges.
+
+
+3. Board integration
+--------------------
+
+To use this driver in your system, register a platform_device with the
+name 'rotary-encoder' and associate the IRQs and some specific platform
+data with it.
+
+struct rotary_encoder_platform_data is declared in
+include/linux/rotary-encoder.h and needs to be filled with the number of
+steps the encoder has and can carry information about externally inverted
+signals (because of used invertig buffer or other reasons).
+
+Because GPIO to IRQ mapping is platform specific, this information must
+be given in seperately to the driver. See the example below.
+
+---------<snip>---------
+
+/* board support file example */
+
+#include <linux/input.h>
+#include <linux/rotary_encoder.h>
+
+#define GPIO_ROTARY_A 1
+#define GPIO_ROTARY_B 2
+
+static struct rotary_encoder_platform_data my_rotary_encoder_info = {
+       .steps          = 24,
+       .axis           = ABS_X,
+       .gpio_a         = GPIO_ROTARY_A,
+       .gpio_b         = GPIO_ROTARY_B,
+       .inverted_a     = 0,
+       .inverted_b     = 0,
+};
+
+static struct platform_device rotary_encoder_device = {
+       .name           = "rotary-encoder",
+       .id             = 0,
+       .dev            = {
+               .platform_data = &my_rotary_encoder_info,
+       }
+};
+
index 3cb50d1..12ee8d5 100644 (file)
@@ -80,6 +80,9 @@ struct rb532_gpio_reg {
 /* Compact Flash GPIO pin */
 #define CF_GPIO_NUM            13
 
+/* S1 button GPIO (shared with UART0_SIN) */
+#define GPIO_BTN_S1            1
+
 extern void rb532_gpio_set_ilevel(int bit, unsigned gpio);
 extern void rb532_gpio_set_istat(int bit, unsigned gpio);
 extern void rb532_gpio_set_func(unsigned gpio);
index 4a5f05b..9f40e1f 100644 (file)
@@ -200,26 +200,9 @@ static struct platform_device rb532_led = {
        .id = -1,
 };
 
-static struct gpio_keys_button rb532_gpio_btn[] = {
-       {
-               .gpio = 1,
-               .code = BTN_0,
-               .desc = "S1",
-               .active_low = 1,
-       }
-};
-
-static struct gpio_keys_platform_data rb532_gpio_btn_data = {
-       .buttons = rb532_gpio_btn,
-       .nbuttons = ARRAY_SIZE(rb532_gpio_btn),
-};
-
 static struct platform_device rb532_button = {
-       .name   = "gpio-keys",
+       .name   = "rb532-button",
        .id     = -1,
-       .dev    = {
-               .platform_data = &rb532_gpio_btn_data,
-       }
 };
 
 static struct resource rb532_wdt_res[] = {
index ec3db3a..d44065d 100644 (file)
@@ -132,6 +132,11 @@ static void input_start_autorepeat(struct input_dev *dev, int code)
        }
 }
 
+static void input_stop_autorepeat(struct input_dev *dev)
+{
+       del_timer(&dev->timer);
+}
+
 #define INPUT_IGNORE_EVENT     0
 #define INPUT_PASS_TO_HANDLERS 1
 #define INPUT_PASS_TO_DEVICE   2
@@ -167,6 +172,8 @@ static void input_handle_event(struct input_dev *dev,
                                __change_bit(code, dev->key);
                                if (value)
                                        input_start_autorepeat(dev, code);
+                               else
+                                       input_stop_autorepeat(dev);
                        }
 
                        disposition = INPUT_PASS_TO_HANDLERS;
@@ -737,11 +744,11 @@ static inline void input_wakeup_procfs_readers(void)
 
 static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
 {
-       int state = input_devices_state;
-
        poll_wait(file, &input_devices_poll_wait, wait);
-       if (state != input_devices_state)
+       if (file->f_version != input_devices_state) {
+               file->f_version = input_devices_state;
                return POLLIN | POLLRDNORM;
+       }
 
        return 0;
 }
index 45470f1..f999dc6 100644 (file)
@@ -229,7 +229,8 @@ struct atkbd {
 /*
  * System-specific ketymap fixup routine
  */
-static void (*atkbd_platform_fixup)(struct atkbd *);
+static void (*atkbd_platform_fixup)(struct atkbd *, const void *data);
+static void *atkbd_platform_fixup_data;
 
 static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
                                ssize_t (*handler)(struct atkbd *, char *));
@@ -834,87 +835,64 @@ static void atkbd_disconnect(struct serio *serio)
 }
 
 /*
- * Most special keys (Fn+F?) on Dell laptops do not generate release
- * events so we have to do it ourselves.
+ * generate release events for the keycodes given in data
  */
-static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd)
+static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd,
+                                               const void *data)
 {
-       static const unsigned int forced_release_keys[] = {
-               0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93,
-       };
-       int i;
+       const unsigned int *keys = data;
+       unsigned int i;
 
        if (atkbd->set == 2)
-               for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
-                       __set_bit(forced_release_keys[i],
-                                 atkbd->force_release_mask);
+               for (i = 0; keys[i] != -1U; i++)
+                       __set_bit(keys[i], atkbd->force_release_mask);
 }
 
 /*
+ * Most special keys (Fn+F?) on Dell laptops do not generate release
+ * events so we have to do it ourselves.
+ */
+static unsigned int atkbd_dell_laptop_forced_release_keys[] = {
+       0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, -1U
+};
+
+/*
  * Perform fixup for HP system that doesn't generate release
  * for its video switch
  */
-static void atkbd_hp_keymap_fixup(struct atkbd *atkbd)
-{
-       static const unsigned int forced_release_keys[] = {
-               0x94,
-       };
-       int i;
-
-       if (atkbd->set == 2)
-               for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
-                       __set_bit(forced_release_keys[i],
-                                       atkbd->force_release_mask);
-}
+static unsigned int atkbd_hp_forced_release_keys[] = {
+       0x94, -1U
+};
 
 /*
  * Inventec system with broken key release on volume keys
  */
-static void atkbd_inventec_keymap_fixup(struct atkbd *atkbd)
-{
-       const unsigned int forced_release_keys[] = {
-               0xae, 0xb0,
-       };
-       int i;
-
-       if (atkbd->set == 2)
-               for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
-                       __set_bit(forced_release_keys[i],
-                                 atkbd->force_release_mask);
-}
+static unsigned int atkbd_inventec_forced_release_keys[] = {
+       0xae, 0xb0, -1U
+};
 
 /*
  * Perform fixup for HP Pavilion ZV6100 laptop that doesn't generate release
  * for its volume buttons
  */
-static void atkbd_hp_zv6100_keymap_fixup(struct atkbd *atkbd)
-{
-       const unsigned int forced_release_keys[] = {
-               0xae, 0xb0,
-       };
-       int i;
-
-       if (atkbd->set == 2)
-               for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
-                       __set_bit(forced_release_keys[i],
-                                       atkbd->force_release_mask);
-}
+static unsigned int atkbd_hp_zv6100_forced_release_keys[] = {
+       0xae, 0xb0, -1U
+};
 
 /*
  * Samsung NC10 with Fn+F? key release not working
  */
-static void atkbd_samsung_keymap_fixup(struct atkbd *atkbd)
-{
-       const unsigned int forced_release_keys[] = {
-               0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9,
-       };
-       int i;
+static unsigned int atkbd_samsung_forced_release_keys[] = {
+       0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, -1U
+};
 
-       if (atkbd->set == 2)
-               for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
-                       __set_bit(forced_release_keys[i],
-                                 atkbd->force_release_mask);
-}
+/*
+ * The volume up and volume down special keys on a Fujitsu Amilo PA 1510 laptop
+ * do not generate release events so we have to do it ourselves.
+ */
+static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = {
+       0xb0, 0xae, -1U
+};
 
 /*
  * atkbd_set_keycode_table() initializes keyboard's keycode table
@@ -967,7 +945,7 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
  * Perform additional fixups
  */
        if (atkbd_platform_fixup)
-               atkbd_platform_fixup(atkbd);
+               atkbd_platform_fixup(atkbd, atkbd_platform_fixup_data);
 }
 
 /*
@@ -1492,9 +1470,11 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf)
        return sprintf(buf, "%lu\n", atkbd->err_count);
 }
 
-static int __init atkbd_setup_fixup(const struct dmi_system_id *id)
+static int __init atkbd_setup_forced_release(const struct dmi_system_id *id)
 {
-       atkbd_platform_fixup = id->driver_data;
+       atkbd_platform_fixup = atkbd_apply_forced_release_keylist;
+       atkbd_platform_fixup_data = id->driver_data;
+
        return 0;
 }
 
@@ -1505,8 +1485,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
                },
-               .callback = atkbd_setup_fixup,
-               .driver_data = atkbd_dell_laptop_keymap_fixup,
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_dell_laptop_forced_release_keys,
        },
        {
                .ident = "Dell Laptop",
@@ -1514,8 +1494,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
                        DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
                },
-               .callback = atkbd_setup_fixup,
-               .driver_data = atkbd_dell_laptop_keymap_fixup,
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_dell_laptop_forced_release_keys,
        },
        {
                .ident = "HP 2133",
@@ -1523,8 +1503,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"),
                },
-               .callback = atkbd_setup_fixup,
-               .driver_data = atkbd_hp_keymap_fixup,
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_hp_forced_release_keys,
        },
        {
                .ident = "HP Pavilion ZV6100",
@@ -1532,8 +1512,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"),
                },
-               .callback = atkbd_setup_fixup,
-               .driver_data = atkbd_hp_zv6100_keymap_fixup,
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_hp_zv6100_forced_release_keys,
        },
        {
                .ident = "Inventec Symphony",
@@ -1541,8 +1521,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
                        DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"),
                },
-               .callback = atkbd_setup_fixup,
-               .driver_data = atkbd_inventec_keymap_fixup,
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_inventec_forced_release_keys,
        },
        {
                .ident = "Samsung NC10",
@@ -1550,8 +1530,17 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
                        DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
                },
-               .callback = atkbd_setup_fixup,
-               .driver_data = atkbd_samsung_keymap_fixup,
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_samsung_forced_release_keys,
+       },
+       {
+               .ident = "Fujitsu Amilo PA 1510",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"),
+               },
+               .callback = atkbd_setup_forced_release,
+               .driver_data = atkbd_amilo_pa1510_forced_release_keys,
        },
        { }
 };
index ee855c5..e94b7d7 100644 (file)
@@ -211,8 +211,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
 
        if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
            !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
-               printk(KERN_ERR DRV_NAME
-                       ": Invalid Debounce/Columdrive Time from pdata\n");
+               printk(KERN_WARNING DRV_NAME
+                       ": Invalid Debounce/Columndrive Time in platform data\n");
                bfin_write_KPAD_MSEL(0xFF0);    /* Default MSEL */
        } else {
                bfin_write_KPAD_MSEL(
index aacf71f..e9d639e 100644 (file)
@@ -198,45 +198,28 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
 }
 
 
-/* initialise HIL */
-static int __init
-hil_keyb_init(void)
+/* initialize HIL */
+static int __devinit hil_keyb_init(void)
 {
        unsigned char c;
        unsigned int i, kbid;
        wait_queue_head_t hil_wait;
        int err;
 
-       if (hil_dev.dev) {
+       if (hil_dev.dev)
                return -ENODEV; /* already initialized */
-       }
 
+       init_waitqueue_head(&hil_wait);
        spin_lock_init(&hil_dev.lock);
+
        hil_dev.dev = input_allocate_device();
        if (!hil_dev.dev)
                return -ENOMEM;
 
-#if defined(CONFIG_HP300)
-       if (!MACH_IS_HP300) {
-               err = -ENODEV;
-               goto err1;
-       }
-       if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
-               printk(KERN_ERR "HIL: hardware register was not found\n");
-               err = -ENODEV;
-               goto err1;
-       }
-       if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
-               printk(KERN_ERR "HIL: IOPORT region already used\n");
-               err = -EIO;
-               goto err1;
-       }
-#endif
-
        err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
        if (err) {
                printk(KERN_ERR "HIL: Can't get IRQ\n");
-               goto err2;
+               goto err1;
        }
 
        /* Turn on interrupts */
@@ -246,11 +229,9 @@ hil_keyb_init(void)
        hil_dev.valid = 0;      /* clear any pending data */
        hil_do(HIL_READKBDSADR, NULL, 0);
 
-       init_waitqueue_head(&hil_wait);
-       wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
-       if (!hil_dev.valid) {
+       wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ);
+       if (!hil_dev.valid)
                printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
-       }
 
        c = hil_dev.c;
        hil_dev.valid = 0;
@@ -268,7 +249,7 @@ hil_keyb_init(void)
 
        for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
                if (hphilkeyb_keycode[i] != KEY_RESERVED)
-                       set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
+                       __set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
 
        hil_dev.dev->evbit[0]   = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
        hil_dev.dev->ledbit[0]  = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
@@ -287,34 +268,45 @@ hil_keyb_init(void)
        err = input_register_device(hil_dev.dev);
        if (err) {
                printk(KERN_ERR "HIL: Can't register device\n");
-               goto err3;
+               goto err2;
        }
+
        printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
               hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
 
        return 0;
 
-err3:
+err2:
        hil_do(HIL_INTOFF, NULL, 0);
-       disable_irq(HIL_IRQ);
        free_irq(HIL_IRQ, hil_dev.dev_id);
-err2:
-#if defined(CONFIG_HP300)
-       release_region(HILBASE + HIL_DATA, 2);
 err1:
-#endif
        input_free_device(hil_dev.dev);
        hil_dev.dev = NULL;
        return err;
 }
 
+static void __devexit hil_keyb_exit(void)
+{
+       if (HIL_IRQ)
+               free_irq(HIL_IRQ, hil_dev.dev_id);
+
+       /* Turn off interrupts */
+       hil_do(HIL_INTOFF, NULL, 0);
+
+       input_unregister_device(hil_dev.dev);
+       hil_dev.dev = NULL;
+}
 
 #if defined(CONFIG_PARISC)
-static int __init
-hil_init_chip(struct parisc_device *dev)
+static int __devinit hil_probe_chip(struct parisc_device *dev)
 {
+       /* Only allow one HIL keyboard */
+       if (hil_dev.dev)
+               return -ENODEV;
+
        if (!dev->irq) {
-               printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa.start);
+               printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n",
+                       (void *)dev->hpa.start);
                return -ENODEV;
        }
 
@@ -327,51 +319,79 @@ hil_init_chip(struct parisc_device *dev)
        return hil_keyb_init();
 }
 
+static int __devexit hil_remove_chip(struct parisc_device *dev)
+{
+       hil_keyb_exit();
+
+       return 0;
+}
+
 static struct parisc_device_id hil_tbl[] = {
        { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
        { 0, }
 };
 
+#if 0
+/* Disabled to avoid conflicts with the HP SDC HIL drivers */
 MODULE_DEVICE_TABLE(parisc, hil_tbl);
+#endif
 
 static struct parisc_driver hil_driver = {
-       .name =         "hil",
-       .id_table =     hil_tbl,
-       .probe =        hil_init_chip,
+       .name           = "hil",
+       .id_table       = hil_tbl,
+       .probe          = hil_probe_chip,
+       .remove         = __devexit_p(hil_remove_chip),
 };
-#endif /* CONFIG_PARISC */
-
 
 static int __init hil_init(void)
 {
-#if defined(CONFIG_PARISC)
        return register_parisc_driver(&hil_driver);
-#else
-       return hil_keyb_init();
-#endif
 }
 
-
 static void __exit hil_exit(void)
 {
-       if (HIL_IRQ) {
-               disable_irq(HIL_IRQ);
-               free_irq(HIL_IRQ, hil_dev.dev_id);
+       unregister_parisc_driver(&hil_driver);
+}
+
+#else /* !CONFIG_PARISC */
+
+static int __init hil_init(void)
+{
+       int error;
+
+       /* Only allow one HIL keyboard */
+       if (hil_dev.dev)
+               return -EBUSY;
+
+       if (!MACH_IS_HP300)
+               return -ENODEV;
+
+       if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
+               printk(KERN_ERR "HIL: hardware register was not found\n");
+               return -ENODEV;
        }
 
-       /* Turn off interrupts */
-       hil_do(HIL_INTOFF, NULL, 0);
+       if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
+               printk(KERN_ERR "HIL: IOPORT region already used\n");
+               return -EIO;
+       }
 
-       input_unregister_device(hil_dev.dev);
+       error = hil_keyb_init();
+       if (error) {
+               release_region(HILBASE + HIL_DATA, 2);
+               return error;
+       }
 
-       hil_dev.dev = NULL;
+       return 0;
+}
 
-#if defined(CONFIG_PARISC)
-       unregister_parisc_driver(&hil_driver);
-#else
-       release_region(HILBASE+HIL_DATA, 2);
-#endif
+static void __exit hil_exit(void)
+{
+       hil_keyb_exit();
+       release_region(HILBASE + HIL_DATA, 2);
 }
 
+#endif /* CONFIG_PARISC */
+
 module_init(hil_init);
 module_exit(hil_exit);
index 67e5553..203abac 100644 (file)
@@ -227,4 +227,27 @@ config INPUT_PCF50633_PMU
         Say Y to include support for delivering  PMU events via  input
         layer on NXP PCF50633.
 
+config INPUT_GPIO_ROTARY_ENCODER
+       tristate "Rotary encoders connected to GPIO pins"
+       depends on GPIOLIB && GENERIC_GPIO
+       help
+         Say Y here to add support for rotary encoders connected to GPIO lines.
+         Check file:Documentation/incput/rotary_encoder.txt for more
+         information.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rotary_encoder.
+
+config INPUT_RB532_BUTTON
+       tristate "Mikrotik Routerboard 532 button interface"
+       depends on MIKROTIK_RB532
+       depends on GPIOLIB && GENERIC_GPIO
+       select INPUT_POLLDEV
+       help
+         Say Y here if you want support for the S1 button built into
+         Mikrotik's Routerboard 532.
+
+         To compile this driver as a module, choose M here: the
+         module will be called rb532_button.
+
 endif
index bb62e6e..eb3f407 100644 (file)
@@ -4,21 +4,23 @@
 
 # Each configuration option enables a list of files.
 
-obj-$(CONFIG_INPUT_SPARCSPKR)          += sparcspkr.o
-obj-$(CONFIG_INPUT_PCSPKR)             += pcspkr.o
-obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
-obj-$(CONFIG_INPUT_IXP4XX_BEEPER)      += ixp4xx-beeper.o
-obj-$(CONFIG_INPUT_COBALT_BTNS)                += cobalt_btns.o
-obj-$(CONFIG_INPUT_WISTRON_BTNS)       += wistron_btns.o
-obj-$(CONFIG_INPUT_ATLAS_BTNS)         += atlas_btns.o
+obj-$(CONFIG_INPUT_APANEL)             += apanel.o
 obj-$(CONFIG_INPUT_ATI_REMOTE)         += ati_remote.o
 obj-$(CONFIG_INPUT_ATI_REMOTE2)                += ati_remote2.o
-obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o
-obj-$(CONFIG_INPUT_POWERMATE)          += powermate.o
-obj-$(CONFIG_INPUT_YEALINK)            += yealink.o
+obj-$(CONFIG_INPUT_ATLAS_BTNS)         += atlas_btns.o
 obj-$(CONFIG_INPUT_CM109)              += cm109.o
+obj-$(CONFIG_INPUT_COBALT_BTNS)                += cobalt_btns.o
 obj-$(CONFIG_HP_SDC_RTC)               += hp_sdc_rtc.o
-obj-$(CONFIG_INPUT_UINPUT)             += uinput.o
-obj-$(CONFIG_INPUT_APANEL)             += apanel.o
-obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
+obj-$(CONFIG_INPUT_IXP4XX_BEEPER)      += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_KEYSPAN_REMOTE)     += keyspan_remote.o
+obj-$(CONFIG_INPUT_M68K_BEEP)          += m68kspkr.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
+obj-$(CONFIG_INPUT_PCSPKR)             += pcspkr.o
+obj-$(CONFIG_INPUT_POWERMATE)          += powermate.o
+obj-$(CONFIG_INPUT_RB532_BUTTON)       += rb532_button.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER)        += rotary_encoder.o
+obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
+obj-$(CONFIG_INPUT_SPARCSPKR)          += sparcspkr.o
+obj-$(CONFIG_INPUT_UINPUT)             += uinput.o
+obj-$(CONFIG_INPUT_WISTRON_BTNS)       += wistron_btns.o
+obj-$(CONFIG_INPUT_YEALINK)            += yealink.o
index 3c9988d..922c051 100644 (file)
@@ -31,12 +31,73 @@ MODULE_LICENSE("GPL");
  * newly configured "channel".
  */
 
-static unsigned int channel_mask = 0xFFFF;
-module_param(channel_mask, uint, 0644);
+enum {
+       ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF,
+       ATI_REMOTE2_MAX_MODE_MASK = 0x1F,
+};
+
+static int ati_remote2_set_mask(const char *val,
+                               struct kernel_param *kp, unsigned int max)
+{
+       unsigned long mask;
+       int ret;
+
+       if (!val)
+               return -EINVAL;
+
+       ret = strict_strtoul(val, 0, &mask);
+       if (ret)
+               return ret;
+
+       if (mask & ~max)
+               return -EINVAL;
+
+       *(unsigned int *)kp->arg = mask;
+
+       return 0;
+}
+
+static int ati_remote2_set_channel_mask(const char *val,
+                                       struct kernel_param *kp)
+{
+       pr_debug("%s()\n", __func__);
+
+       return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK);
+}
+
+static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp)
+{
+       pr_debug("%s()\n", __func__);
+
+       return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
+}
+
+static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp)
+{
+       pr_debug("%s()\n", __func__);
+
+       return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK);
+}
+
+static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp)
+{
+       pr_debug("%s()\n", __func__);
+
+       return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
+}
+
+static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
+#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int)
+#define param_set_channel_mask ati_remote2_set_channel_mask
+#define param_get_channel_mask ati_remote2_get_channel_mask
+module_param(channel_mask, channel_mask, 0644);
 MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
 
-static unsigned int mode_mask = 0x1F;
-module_param(mode_mask, uint, 0644);
+static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK;
+#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int)
+#define param_set_mode_mask ati_remote2_set_mode_mask
+#define param_get_mode_mask ati_remote2_get_mode_mask
+module_param(mode_mask, mode_mask, 0644);
 MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
 
 static struct usb_device_id ati_remote2_id_table[] = {
@@ -133,12 +194,18 @@ struct ati_remote2 {
        u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
 
        unsigned int flags;
+
+       unsigned int channel_mask;
+       unsigned int mode_mask;
 };
 
 static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
 static void ati_remote2_disconnect(struct usb_interface *interface);
 static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
 static int ati_remote2_resume(struct usb_interface *interface);
+static int ati_remote2_reset_resume(struct usb_interface *interface);
+static int ati_remote2_pre_reset(struct usb_interface *interface);
+static int ati_remote2_post_reset(struct usb_interface *interface);
 
 static struct usb_driver ati_remote2_driver = {
        .name       = "ati_remote2",
@@ -147,6 +214,9 @@ static struct usb_driver ati_remote2_driver = {
        .id_table   = ati_remote2_id_table,
        .suspend    = ati_remote2_suspend,
        .resume     = ati_remote2_resume,
+       .reset_resume = ati_remote2_reset_resume,
+       .pre_reset  = ati_remote2_pre_reset,
+       .post_reset = ati_remote2_post_reset,
        .supports_autosuspend = 1,
 };
 
@@ -238,7 +308,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
 
        channel = data[0] >> 4;
 
-       if (!((1 << channel) & channel_mask))
+       if (!((1 << channel) & ar2->channel_mask))
                return;
 
        mode = data[0] & 0x0F;
@@ -250,7 +320,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
                return;
        }
 
-       if (!((1 << mode) & mode_mask))
+       if (!((1 << mode) & ar2->mode_mask))
                return;
 
        input_event(idev, EV_REL, REL_X, (s8) data[1]);
@@ -277,7 +347,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
 
        channel = data[0] >> 4;
 
-       if (!((1 << channel) & channel_mask))
+       if (!((1 << channel) & ar2->channel_mask))
                return;
 
        mode = data[0] & 0x0F;
@@ -305,7 +375,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
                        ar2->mode = mode;
        }
 
-       if (!((1 << mode) & mode_mask))
+       if (!((1 << mode) & ar2->mode_mask))
                return;
 
        index = ati_remote2_lookup(hw_code);
@@ -410,7 +480,7 @@ static int ati_remote2_getkeycode(struct input_dev *idev,
        int index, mode;
 
        mode = scancode >> 8;
-       if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
+       if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
                return -EINVAL;
 
        index = ati_remote2_lookup(scancode & 0xFF);
@@ -427,7 +497,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc
        int index, mode, old_keycode;
 
        mode = scancode >> 8;
-       if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
+       if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
                return -EINVAL;
 
        index = ati_remote2_lookup(scancode & 0xFF);
@@ -550,7 +620,7 @@ static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
        }
 }
 
-static int ati_remote2_setup(struct ati_remote2 *ar2)
+static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask)
 {
        int r, i, channel;
 
@@ -565,8 +635,8 @@ static int ati_remote2_setup(struct ati_remote2 *ar2)
 
        channel = 0;
        for (i = 0; i < 16; i++) {
-               if ((1 << i) & channel_mask) {
-                       if (!(~(1 << i) & 0xFFFF & channel_mask))
+               if ((1 << i) & ch_mask) {
+                       if (!(~(1 << i) & ch_mask))
                                channel = i + 1;
                        break;
                }
@@ -585,6 +655,99 @@ static int ati_remote2_setup(struct ati_remote2 *ar2)
        return 0;
 }
 
+static ssize_t ati_remote2_show_channel_mask(struct device *dev,
+                                            struct device_attribute *attr,
+                                            char *buf)
+{
+       struct usb_device *udev = to_usb_device(dev);
+       struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+       struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+
+       return sprintf(buf, "0x%04x\n", ar2->channel_mask);
+}
+
+static ssize_t ati_remote2_store_channel_mask(struct device *dev,
+                                             struct device_attribute *attr,
+                                             const char *buf, size_t count)
+{
+       struct usb_device *udev = to_usb_device(dev);
+       struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+       struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+       unsigned long mask;
+       int r;
+
+       if (strict_strtoul(buf, 0, &mask))
+               return -EINVAL;
+
+       if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK)
+               return -EINVAL;
+
+       r = usb_autopm_get_interface(ar2->intf[0]);
+       if (r) {
+               dev_err(&ar2->intf[0]->dev,
+                       "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+               return r;
+       }
+
+       mutex_lock(&ati_remote2_mutex);
+
+       if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask))
+               ar2->channel_mask = mask;
+
+       mutex_unlock(&ati_remote2_mutex);
+
+       usb_autopm_put_interface(ar2->intf[0]);
+
+       return count;
+}
+
+static ssize_t ati_remote2_show_mode_mask(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       struct usb_device *udev = to_usb_device(dev);
+       struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+       struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+
+       return sprintf(buf, "0x%02x\n", ar2->mode_mask);
+}
+
+static ssize_t ati_remote2_store_mode_mask(struct device *dev,
+                                          struct device_attribute *attr,
+                                          const char *buf, size_t count)
+{
+       struct usb_device *udev = to_usb_device(dev);
+       struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+       struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+       unsigned long mask;
+
+       if (strict_strtoul(buf, 0, &mask))
+               return -EINVAL;
+
+       if (mask & ~ATI_REMOTE2_MAX_MODE_MASK)
+               return -EINVAL;
+
+       ar2->mode_mask = mask;
+
+       return count;
+}
+
+static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask,
+                  ati_remote2_store_channel_mask);
+
+static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask,
+                  ati_remote2_store_mode_mask);
+
+static struct attribute *ati_remote2_attrs[] = {
+       &dev_attr_channel_mask.attr,
+       &dev_attr_mode_mask.attr,
+       NULL,
+};
+
+static struct attribute_group ati_remote2_attr_group = {
+       .attrs = ati_remote2_attrs,
+};
+
 static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
 {
        struct usb_device *udev = interface_to_usbdev(interface);
@@ -615,7 +778,10 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
        if (r)
                goto fail2;
 
-       r = ati_remote2_setup(ar2);
+       ar2->channel_mask = channel_mask;
+       ar2->mode_mask = mode_mask;
+
+       r = ati_remote2_setup(ar2, ar2->channel_mask);
        if (r)
                goto fail2;
 
@@ -624,19 +790,24 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
 
        strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
 
-       r = ati_remote2_input_init(ar2);
+       r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
        if (r)
                goto fail2;
 
+       r = ati_remote2_input_init(ar2);
+       if (r)
+               goto fail3;
+
        usb_set_intfdata(interface, ar2);
 
        interface->needs_remote_wakeup = 1;
 
        return 0;
 
+ fail3:
+       sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
  fail2:
        ati_remote2_urb_cleanup(ar2);
-
        usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
  fail1:
        kfree(ar2);
@@ -657,6 +828,8 @@ static void ati_remote2_disconnect(struct usb_interface *interface)
 
        input_unregister_device(ar2->idev);
 
+       sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group);
+
        ati_remote2_urb_cleanup(ar2);
 
        usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
@@ -715,6 +888,78 @@ static int ati_remote2_resume(struct usb_interface *interface)
        return r;
 }
 
+static int ati_remote2_reset_resume(struct usb_interface *interface)
+{
+       struct ati_remote2 *ar2;
+       struct usb_host_interface *alt = interface->cur_altsetting;
+       int r = 0;
+
+       if (alt->desc.bInterfaceNumber)
+               return 0;
+
+       ar2 = usb_get_intfdata(interface);
+
+       dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+       mutex_lock(&ati_remote2_mutex);
+
+       r = ati_remote2_setup(ar2, ar2->channel_mask);
+       if (r)
+               goto out;
+
+       if (ar2->flags & ATI_REMOTE2_OPENED)
+               r = ati_remote2_submit_urbs(ar2);
+
+       if (!r)
+               ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
+
+ out:
+       mutex_unlock(&ati_remote2_mutex);
+
+       return r;
+}
+
+static int ati_remote2_pre_reset(struct usb_interface *interface)
+{
+       struct ati_remote2 *ar2;
+       struct usb_host_interface *alt = interface->cur_altsetting;
+
+       if (alt->desc.bInterfaceNumber)
+               return 0;
+
+       ar2 = usb_get_intfdata(interface);
+
+       dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+       mutex_lock(&ati_remote2_mutex);
+
+       if (ar2->flags == ATI_REMOTE2_OPENED)
+               ati_remote2_kill_urbs(ar2);
+
+       return 0;
+}
+
+static int ati_remote2_post_reset(struct usb_interface *interface)
+{
+       struct ati_remote2 *ar2;
+       struct usb_host_interface *alt = interface->cur_altsetting;
+       int r = 0;
+
+       if (alt->desc.bInterfaceNumber)
+               return 0;
+
+       ar2 = usb_get_intfdata(interface);
+
+       dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+       if (ar2->flags == ATI_REMOTE2_OPENED)
+               r = ati_remote2_submit_urbs(ar2);
+
+       mutex_unlock(&ati_remote2_mutex);
+
+       return r;
+}
+
 static int __init ati_remote2_init(void)
 {
        int r;
diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c
new file mode 100644 (file)
index 0000000..e2c7f62
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Support for the S1 button on Routerboard 532
+ *
+ * Copyright (C) 2009  Phil Sutter <n0-1@freewrt.org>
+ */
+
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-rc32434/gpio.h>
+#include <asm/mach-rc32434/rb.h>
+
+#define DRV_NAME "rb532-button"
+
+#define RB532_BTN_RATE 100 /* msec */
+#define RB532_BTN_KSYM BTN_0
+
+/* The S1 button state is provided by GPIO pin 1. But as this
+ * pin is also used for uart input as alternate function, the
+ * operational modes must be switched first:
+ * 1) disable uart using set_latch_u5()
+ * 2) turn off alternate function implicitly through
+ *    gpio_direction_input()
+ * 3) read the GPIO's current value
+ * 4) undo step 2 by enabling alternate function (in this
+ *    mode the GPIO direction is fixed, so no change needed)
+ * 5) turn on uart again
+ * The GPIO value occurs to be inverted, so pin high means
+ * button is not pressed.
+ */
+static bool rb532_button_pressed(void)
+{
+       int val;
+
+       set_latch_u5(0, LO_FOFF);
+       gpio_direction_input(GPIO_BTN_S1);
+
+       val = gpio_get_value(GPIO_BTN_S1);
+
+       rb532_gpio_set_func(GPIO_BTN_S1);
+       set_latch_u5(LO_FOFF, 0);
+
+       return !val;
+}
+
+static void rb532_button_poll(struct input_polled_dev *poll_dev)
+{
+       input_report_key(poll_dev->input, RB532_BTN_KSYM,
+                        rb532_button_pressed());
+       input_sync(poll_dev->input);
+}
+
+static int __devinit rb532_button_probe(struct platform_device *pdev)
+{
+       struct input_polled_dev *poll_dev;
+       int error;
+
+       poll_dev = input_allocate_polled_device();
+       if (!poll_dev)
+               return -ENOMEM;
+
+       poll_dev->poll = rb532_button_poll;
+       poll_dev->poll_interval = RB532_BTN_RATE;
+
+       poll_dev->input->name = "rb532 button";
+       poll_dev->input->phys = "rb532/button0";
+       poll_dev->input->id.bustype = BUS_HOST;
+       poll_dev->input->dev.parent = &pdev->dev;
+
+       dev_set_drvdata(&pdev->dev, poll_dev);
+
+       input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM);
+
+       error = input_register_polled_device(poll_dev);
+       if (error) {
+               input_free_polled_device(poll_dev);
+               return error;
+       }
+
+       return 0;
+}
+
+static int __devexit rb532_button_remove(struct platform_device *pdev)
+{
+       struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev);
+
+       input_unregister_polled_device(poll_dev);
+       input_free_polled_device(poll_dev);
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver rb532_button_driver = {
+       .probe = rb532_button_probe,
+       .remove = __devexit_p(rb532_button_remove),
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init rb532_button_init(void)
+{
+       return platform_driver_register(&rb532_button_driver);
+}
+
+static void __exit rb532_button_exit(void)
+{
+       platform_driver_unregister(&rb532_button_driver);
+}
+
+module_init(rb532_button_init);
+module_exit(rb532_button_exit);
+
+MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Support for S1 button on Routerboard 532");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644 (file)
index 0000000..5bb3ab5
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary_encoder.txt for more information
+ *
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary_encoder.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+       unsigned int irq_a;
+       unsigned int irq_b;
+       unsigned int pos;
+       unsigned int armed;
+       unsigned int dir;
+       struct input_dev *input;
+       struct rotary_encoder_platform_data *pdata;
+};
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+       struct rotary_encoder *encoder = dev_id;
+       struct rotary_encoder_platform_data *pdata = encoder->pdata;
+       int a = !!gpio_get_value(pdata->gpio_a);
+       int b = !!gpio_get_value(pdata->gpio_b);
+       int state;
+
+       a ^= pdata->inverted_a;
+       b ^= pdata->inverted_b;
+       state = (a << 1) | b;
+
+       switch (state) {
+
+       case 0x0:
+               if (!encoder->armed)
+                       break;
+
+               if (encoder->dir) {
+                       /* turning counter-clockwise */
+                       encoder->pos += pdata->steps;
+                       encoder->pos--;
+                       encoder->pos %= pdata->steps;
+               } else {
+                       /* turning clockwise */
+                       encoder->pos++;
+                       encoder->pos %= pdata->steps;
+               }
+
+               input_report_abs(encoder->input, pdata->axis, encoder->pos);
+               input_sync(encoder->input);
+
+               encoder->armed = 0;
+               break;
+
+       case 0x1:
+       case 0x2:
+               if (encoder->armed)
+                       encoder->dir = state - 1;
+               break;
+
+       case 0x3:
+               encoder->armed = 1;
+               break;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit rotary_encoder_probe(struct platform_device *pdev)
+{
+       struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
+       struct rotary_encoder *encoder;
+       struct input_dev *input;
+       int err;
+
+       if (!pdata || !pdata->steps) {
+               dev_err(&pdev->dev, "invalid platform data\n");
+               return -ENOENT;
+       }
+
+       encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+       input = input_allocate_device();
+       if (!encoder || !input) {
+               dev_err(&pdev->dev, "failed to allocate memory for device\n");
+               err = -ENOMEM;
+               goto exit_free_mem;
+       }
+
+       encoder->input = input;
+       encoder->pdata = pdata;
+       encoder->irq_a = gpio_to_irq(pdata->gpio_a);
+       encoder->irq_b = gpio_to_irq(pdata->gpio_b);
+
+       /* create and register the input driver */
+       input->name = pdev->name;
+       input->id.bustype = BUS_HOST;
+       input->dev.parent = &pdev->dev;
+       input->evbit[0] = BIT_MASK(EV_ABS);
+       input_set_abs_params(encoder->input,
+                            pdata->axis, 0, pdata->steps, 0, 1);
+
+       err = input_register_device(input);
+       if (err) {
+               dev_err(&pdev->dev, "failed to register input device\n");
+               goto exit_free_mem;
+       }
+
+       /* request the GPIOs */
+       err = gpio_request(pdata->gpio_a, DRV_NAME);
+       if (err) {
+               dev_err(&pdev->dev, "unable to request GPIO %d\n",
+                       pdata->gpio_a);
+               goto exit_unregister_input;
+       }
+
+       err = gpio_request(pdata->gpio_b, DRV_NAME);
+       if (err) {
+               dev_err(&pdev->dev, "unable to request GPIO %d\n",
+                       pdata->gpio_b);
+               goto exit_free_gpio_a;
+       }
+
+       /* request the IRQs */
+       err = request_irq(encoder->irq_a, &rotary_encoder_irq,
+                         IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+                         DRV_NAME, encoder);
+       if (err) {
+               dev_err(&pdev->dev, "unable to request IRQ %d\n",
+                       encoder->irq_a);
+               goto exit_free_gpio_b;
+       }
+
+       err = request_irq(encoder->irq_b, &rotary_encoder_irq,
+                         IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
+                         DRV_NAME, encoder);
+       if (err) {
+               dev_err(&pdev->dev, "unable to request IRQ %d\n",
+                       encoder->irq_b);
+               goto exit_free_irq_a;
+       }
+
+       platform_set_drvdata(pdev, encoder);
+
+       return 0;
+
+exit_free_irq_a:
+       free_irq(encoder->irq_a, encoder);
+exit_free_gpio_b:
+       gpio_free(pdata->gpio_b);
+exit_free_gpio_a:
+       gpio_free(pdata->gpio_a);
+exit_unregister_input:
+       input_unregister_device(input);
+       input = NULL; /* so we don't try to free it */
+exit_free_mem:
+       input_free_device(input);
+       kfree(encoder);
+       return err;
+}
+
+static int __devexit rotary_encoder_remove(struct platform_device *pdev)
+{
+       struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+       struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
+
+       free_irq(encoder->irq_a, encoder);
+       free_irq(encoder->irq_b, encoder);
+       gpio_free(pdata->gpio_a);
+       gpio_free(pdata->gpio_b);
+       input_unregister_device(encoder->input);
+       platform_set_drvdata(pdev, NULL);
+       kfree(encoder);
+
+       return 0;
+}
+
+static struct platform_driver rotary_encoder_driver = {
+       .probe          = rotary_encoder_probe,
+       .remove         = __devexit_p(rotary_encoder_remove),
+       .driver         = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       }
+};
+
+static int __init rotary_encoder_init(void)
+{
+       return platform_driver_register(&rotary_encoder_driver);
+}
+
+static void __exit rotary_encoder_exit(void)
+{
+       platform_driver_unregister(&rotary_encoder_driver);
+}
+
+module_init(rotary_encoder_init);
+module_exit(rotary_encoder_exit);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
+MODULE_LICENSE("GPL v2");
+
index 4f38e6f..c66cc3d 100644 (file)
@@ -292,4 +292,15 @@ config MOUSE_PXA930_TRKBALL
        help
          Say Y here to support PXA930 Trackball mouse.
 
+config MOUSE_MAPLE
+       tristate "Maple mouse (for the Dreamcast)"
+       depends on MAPLE
+       help
+         This driver supports the Maple mouse on the SEGA Dreamcast.
+
+         Most Dreamcast users, who have a mouse, will say Y here.
+
+         To compile this driver as a module choose M here: the module will be
+         called maplemouse.
+
 endif
index 8c8a1f2..4721894 100644 (file)
@@ -6,18 +6,19 @@
 
 obj-$(CONFIG_MOUSE_AMIGA)              += amimouse.o
 obj-$(CONFIG_MOUSE_APPLETOUCH)         += appletouch.o
-obj-$(CONFIG_MOUSE_BCM5974)            += bcm5974.o
 obj-$(CONFIG_MOUSE_ATARI)              += atarimouse.o
-obj-$(CONFIG_MOUSE_RISCPC)             += rpcmouse.o
+obj-$(CONFIG_MOUSE_BCM5974)            += bcm5974.o
+obj-$(CONFIG_MOUSE_GPIO)               += gpio_mouse.o
+obj-$(CONFIG_MOUSE_HIL)                        += hil_ptr.o
 obj-$(CONFIG_MOUSE_INPORT)             += inport.o
 obj-$(CONFIG_MOUSE_LOGIBM)             += logibm.o
+obj-$(CONFIG_MOUSE_MAPLE)              += maplemouse.o
 obj-$(CONFIG_MOUSE_PC110PAD)           += pc110pad.o
 obj-$(CONFIG_MOUSE_PS2)                        += psmouse.o
 obj-$(CONFIG_MOUSE_PXA930_TRKBALL)     += pxa930_trkball.o
+obj-$(CONFIG_MOUSE_RISCPC)             += rpcmouse.o
 obj-$(CONFIG_MOUSE_SERIAL)             += sermouse.o
-obj-$(CONFIG_MOUSE_HIL)                        += hil_ptr.o
 obj-$(CONFIG_MOUSE_VSXXXAA)            += vsxxxaa.o
-obj-$(CONFIG_MOUSE_GPIO)               += gpio_mouse.o
 
 psmouse-objs := psmouse-base.o synaptics.o
 
index 55cd0fa..a1ad2f1 100644 (file)
@@ -472,7 +472,7 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
                return -EIO;
        }
 
-       hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]);
+       hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]);
 
        /* HGPK signature: 0x67, 0x00, 0x<model> */
        if (param[0] != 0x67 || param[1] != 0x00)
diff --git a/drivers/input/mouse/maplemouse.c b/drivers/input/mouse/maplemouse.c
new file mode 100644 (file)
index 0000000..d196abf
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ *     SEGA Dreamcast mouse driver
+ *     Based on drivers/usb/usbmouse.c
+ *
+ *     Copyright Yaegashi Takeshi, 2001
+ *     Adrian McMenamin, 2008
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
+MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
+MODULE_LICENSE("GPL");
+
+struct dc_mouse {
+       struct input_dev *dev;
+       struct maple_device *mdev;
+};
+
+static void dc_mouse_callback(struct mapleq *mq)
+{
+       int buttons, relx, rely, relz;
+       struct maple_device *mapledev = mq->dev;
+       struct dc_mouse *mse = maple_get_drvdata(mapledev);
+       struct input_dev *dev = mse->dev;
+       unsigned char *res = mq->recvbuf;
+
+       buttons = ~res[8];
+       relx = *(unsigned short *)(res + 12) - 512;
+       rely = *(unsigned short *)(res + 14) - 512;
+       relz = *(unsigned short *)(res + 16) - 512;
+
+       input_report_key(dev, BTN_LEFT,   buttons & 4);
+       input_report_key(dev, BTN_MIDDLE, buttons & 9);
+       input_report_key(dev, BTN_RIGHT,  buttons & 2);
+       input_report_rel(dev, REL_X,      relx);
+       input_report_rel(dev, REL_Y,      rely);
+       input_report_rel(dev, REL_WHEEL,  relz);
+       input_sync(dev);
+}
+
+static int dc_mouse_open(struct input_dev *dev)
+{
+       struct dc_mouse *mse = dev->dev.platform_data;
+
+       maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
+               MAPLE_FUNC_MOUSE);
+
+       return 0;
+}
+
+static void dc_mouse_close(struct input_dev *dev)
+{
+       struct dc_mouse *mse = dev->dev.platform_data;
+
+       maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
+               MAPLE_FUNC_MOUSE);
+}
+
+
+static int __devinit probe_maple_mouse(struct device *dev)
+{
+       struct maple_device *mdev = to_maple_dev(dev);
+       struct maple_driver *mdrv = to_maple_driver(dev->driver);
+       struct input_dev *input_dev;
+       struct dc_mouse *mse;
+       int error;
+
+       mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
+       input_dev = input_allocate_device();
+
+       if (!mse || !input_dev) {
+               error = -ENOMEM;
+               goto fail;
+       }
+
+       mse->dev = input_dev;
+       mse->mdev = mdev;
+
+       input_set_drvdata(input_dev, mse);
+       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+       input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+               BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
+       input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
+               BIT_MASK(REL_WHEEL);
+       input_dev->name = mdev->product_name;
+       input_dev->id.bustype = BUS_HOST;
+       input_dev->open = dc_mouse_open;
+       input_dev->close = dc_mouse_close;
+
+       mdev->driver = mdrv;
+       maple_set_drvdata(mdev, mse);
+
+       error = input_register_device(input_dev);
+       if (error)
+               goto fail;
+
+       return 0;
+
+fail:
+       input_free_device(input_dev);
+       maple_set_drvdata(mdev, NULL);
+       kfree(mse);
+       mdev->driver = NULL;
+       return error;
+}
+
+static int __devexit remove_maple_mouse(struct device *dev)
+{
+       struct maple_device *mdev = to_maple_dev(dev);
+       struct dc_mouse *mse = maple_get_drvdata(mdev);
+
+       mdev->callback = NULL;
+       input_unregister_device(mse->dev);
+       maple_set_drvdata(mdev, NULL);
+       kfree(mse);
+
+       return 0;
+}
+
+static struct maple_driver dc_mouse_driver = {
+       .function =     MAPLE_FUNC_MOUSE,
+       .drv = {
+               .name = "Dreamcast_mouse",
+               .probe = probe_maple_mouse,
+               .remove = __devexit_p(remove_maple_mouse),
+       },
+};
+
+static int __init dc_mouse_init(void)
+{
+       return maple_driver_register(&dc_mouse_driver);
+}
+
+static void __exit dc_mouse_exit(void)
+{
+       maple_driver_unregister(&dc_mouse_driver);
+}
+
+module_init(dc_mouse_init);
+module_exit(dc_mouse_exit);
index fd09c8d..f63995f 100644 (file)
@@ -111,11 +111,8 @@ static int __init pc110pad_init(void)
        struct pci_dev *dev;
        int err;
 
-       dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
-       if (dev) {
-               pci_dev_put(dev);
+       if (!no_pci_devices())
                return -ENODEV;
-       }
 
        if (!request_region(pc110pad_io, 4, "pc110pad")) {
                printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
index 6fa2def..83ed2d5 100644 (file)
@@ -151,6 +151,14 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
                        DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
                },
        },
+       {
+               .ident = "HP DV9700",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
+                       DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
+               },
+       },
        { }
 };
 
index bb6486a..b01fd61 100644 (file)
@@ -29,6 +29,51 @@ config TOUCHSCREEN_ADS7846
          To compile this driver as a module, choose M here: the
          module will be called ads7846.
 
+config TOUCHSCREEN_AD7877
+       tristate "AD7877 based touchscreens"
+       depends on SPI_MASTER
+       help
+         Say Y here if you have a touchscreen interface using the
+         AD7877 controller, and your board-specific initialization
+         code includes that in its table of SPI devices.
+
+         If unsure, say N (but it's safe to say "Y").
+
+         To compile this driver as a module, choose M here: the
+         module will be called ad7877.
+
+config TOUCHSCREEN_AD7879_I2C
+       tristate "AD7879 based touchscreens: AD7879-1 I2C Interface"
+       depends on I2C
+       select TOUCHSCREEN_AD7879
+       help
+         Say Y here if you have a touchscreen interface using the
+         AD7879-1 controller, and your board-specific initialization
+         code includes that in its table of I2C devices.
+
+         If unsure, say N (but it's safe to say "Y").
+
+         To compile this driver as a module, choose M here: the
+         module will be called ad7879.
+
+config TOUCHSCREEN_AD7879_SPI
+       tristate "AD7879 based touchscreens: AD7879 SPI Interface"
+       depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n
+       select TOUCHSCREEN_AD7879
+       help
+         Say Y here if you have a touchscreen interface using the
+         AD7879 controller, and your board-specific initialization
+         code includes that in its table of SPI devices.
+
+         If unsure, say N (but it's safe to say "Y").
+
+         To compile this driver as a module, choose M here: the
+         module will be called ad7879.
+
+config TOUCHSCREEN_AD7879
+       tristate
+       default n
+
 config TOUCHSCREEN_BITSY
        tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
        depends on SA1100_BITSY
@@ -308,6 +353,19 @@ config TOUCHSCREEN_WM97XX_MAINSTONE
          To compile this driver as a module, choose M here: the
          module will be called mainstone-wm97xx.
 
+config TOUCHSCREEN_WM97XX_ZYLONITE
+       tristate "Zylonite accelerated touch"
+       depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE
+       select TOUCHSCREEN_WM9713
+       help
+         Say Y here for support for streaming mode with the touchscreen
+         on Zylonite systems.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called zylonite-wm97xx.
+
 config TOUCHSCREEN_USB_COMPOSITE
        tristate "USB Touchscreen Driver"
        depends on USB_ARCH_HAS_HCD
index d3375af..6700f7b 100644 (file)
@@ -6,6 +6,8 @@
 
 wm97xx-ts-y := wm97xx-core.o
 
+obj-$(CONFIG_TOUCHSCREEN_AD7877)       += ad7877.o
+obj-$(CONFIG_TOUCHSCREEN_AD7879)       += ad7879.o
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)      += ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)                += h3600_ts_input.o
@@ -34,3 +36,4 @@ wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705)        += wm9705.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)     += mainstone-wm97xx.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)      += zylonite-wm97xx.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
new file mode 100644 (file)
index 0000000..e4728a2
--- /dev/null
@@ -0,0 +1,844 @@
+/*
+ * Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc.
+ *
+ * Description:        AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver
+ * Based on:   ads7846.c
+ *
+ * Bugs:        Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ *  - corgi_ts.c
+ *     Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *     Copyright (C) 2002 MontaVista Software
+ *     Copyright (C) 2004 Texas Instruments
+ *     Copyright (C) 2005 Dirk Behme
+ */
+
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ad7877.h>
+#include <asm/irq.h>
+
+#define        TS_PEN_UP_TIMEOUT       msecs_to_jiffies(50)
+
+#define MAX_SPI_FREQ_HZ                        20000000
+#define        MAX_12BIT                       ((1<<12)-1)
+
+#define AD7877_REG_ZEROS                       0
+#define AD7877_REG_CTRL1                       1
+#define AD7877_REG_CTRL2                       2
+#define AD7877_REG_ALERT                       3
+#define AD7877_REG_AUX1HIGH                    4
+#define AD7877_REG_AUX1LOW                     5
+#define AD7877_REG_BAT1HIGH                    6
+#define AD7877_REG_BAT1LOW                     7
+#define AD7877_REG_BAT2HIGH                    8
+#define AD7877_REG_BAT2LOW                     9
+#define AD7877_REG_TEMP1HIGH                   10
+#define AD7877_REG_TEMP1LOW                    11
+#define AD7877_REG_SEQ0                                12
+#define AD7877_REG_SEQ1                                13
+#define AD7877_REG_DAC                         14
+#define AD7877_REG_NONE1                       15
+#define AD7877_REG_EXTWRITE                    15
+#define AD7877_REG_XPLUS                       16
+#define AD7877_REG_YPLUS                       17
+#define AD7877_REG_Z2                          18
+#define AD7877_REG_aux1                                19
+#define AD7877_REG_aux2                                20
+#define AD7877_REG_aux3                                21
+#define AD7877_REG_bat1                                22
+#define AD7877_REG_bat2                                23
+#define AD7877_REG_temp1                       24
+#define AD7877_REG_temp2                       25
+#define AD7877_REG_Z1                          26
+#define AD7877_REG_GPIOCTRL1                   27
+#define AD7877_REG_GPIOCTRL2                   28
+#define AD7877_REG_GPIODATA                    29
+#define AD7877_REG_NONE2                       30
+#define AD7877_REG_NONE3                       31
+
+#define AD7877_SEQ_YPLUS_BIT                   (1<<11)
+#define AD7877_SEQ_XPLUS_BIT                   (1<<10)
+#define AD7877_SEQ_Z2_BIT                      (1<<9)
+#define AD7877_SEQ_AUX1_BIT                    (1<<8)
+#define AD7877_SEQ_AUX2_BIT                    (1<<7)
+#define AD7877_SEQ_AUX3_BIT                    (1<<6)
+#define AD7877_SEQ_BAT1_BIT                    (1<<5)
+#define AD7877_SEQ_BAT2_BIT                    (1<<4)
+#define AD7877_SEQ_TEMP1_BIT                   (1<<3)
+#define AD7877_SEQ_TEMP2_BIT                   (1<<2)
+#define AD7877_SEQ_Z1_BIT                      (1<<1)
+
+enum {
+       AD7877_SEQ_YPOS  = 0,
+       AD7877_SEQ_XPOS  = 1,
+       AD7877_SEQ_Z2    = 2,
+       AD7877_SEQ_AUX1  = 3,
+       AD7877_SEQ_AUX2  = 4,
+       AD7877_SEQ_AUX3  = 5,
+       AD7877_SEQ_BAT1  = 6,
+       AD7877_SEQ_BAT2  = 7,
+       AD7877_SEQ_TEMP1 = 8,
+       AD7877_SEQ_TEMP2 = 9,
+       AD7877_SEQ_Z1    = 10,
+       AD7877_NR_SENSE  = 11,
+};
+
+/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */
+#define AD7877_DAC_CONF                        0x1
+
+/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */
+#define AD7877_EXTW_GPIO_3_CONF                0x1C4
+#define AD7877_EXTW_GPIO_DATA          0x200
+
+/* Control REG 2 */
+#define AD7877_TMR(x)                  ((x & 0x3) << 0)
+#define AD7877_REF(x)                  ((x & 0x1) << 2)
+#define AD7877_POL(x)                  ((x & 0x1) << 3)
+#define AD7877_FCD(x)                  ((x & 0x3) << 4)
+#define AD7877_PM(x)                   ((x & 0x3) << 6)
+#define AD7877_ACQ(x)                  ((x & 0x3) << 8)
+#define AD7877_AVG(x)                  ((x & 0x3) << 10)
+
+/* Control REG 1 */
+#define        AD7877_SER                      (1 << 11)       /* non-differential */
+#define        AD7877_DFR                      (0 << 11)       /* differential */
+
+#define AD7877_MODE_NOC  (0)   /* Do not convert */
+#define AD7877_MODE_SCC  (1)   /* Single channel conversion */
+#define AD7877_MODE_SEQ0 (2)   /* Sequence 0 in Slave Mode */
+#define AD7877_MODE_SEQ1 (3)   /* Sequence 1 in Master Mode */
+
+#define AD7877_CHANADD(x)              ((x&0xF)<<7)
+#define AD7877_READADD(x)              ((x)<<2)
+#define AD7877_WRITEADD(x)             ((x)<<12)
+
+#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \
+               AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \
+               AD7877_READADD(AD7877_REG_ ## x))
+
+#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \
+               AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT)
+
+/*
+ * Non-touchscreen sensors only use single-ended conversions.
+ */
+
+struct ser_req {
+       u16                     reset;
+       u16                     ref_on;
+       u16                     command;
+       u16                     sample;
+       struct spi_message      msg;
+       struct spi_transfer     xfer[6];
+};
+
+struct ad7877 {
+       struct input_dev        *input;
+       char                    phys[32];
+
+       struct spi_device       *spi;
+       u16                     model;
+       u16                     vref_delay_usecs;
+       u16                     x_plate_ohms;
+       u16                     pressure_max;
+
+       u16                     cmd_crtl1;
+       u16                     cmd_crtl2;
+       u16                     cmd_dummy;
+       u16                     dac;
+
+       u8                      stopacq_polarity;
+       u8                      first_conversion_delay;
+       u8                      acquisition_time;
+       u8                      averaging;
+       u8                      pen_down_acc_interval;
+
+       u16                     conversion_data[AD7877_NR_SENSE];
+
+       struct spi_transfer     xfer[AD7877_NR_SENSE + 2];
+       struct spi_message      msg;
+
+       struct mutex            mutex;
+       unsigned                disabled:1;     /* P: mutex */
+       unsigned                gpio3:1;        /* P: mutex */
+       unsigned                gpio4:1;        /* P: mutex */
+
+       spinlock_t              lock;
+       struct timer_list       timer;          /* P: lock */
+       unsigned                pending:1;      /* P: lock */
+};
+
+static int gpio3;
+module_param(gpio3, int, 0);
+MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
+
+/*
+ * ad7877_read/write are only used for initial setup and for sysfs controls.
+ * The main traffic is done using spi_async() in the interrupt handler.
+ */
+
+static int ad7877_read(struct spi_device *spi, u16 reg)
+{
+       struct ser_req *req;
+       int status, ret;
+
+       req = kzalloc(sizeof *req, GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       spi_message_init(&req->msg);
+
+       req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) |
+                       AD7877_READADD(reg));
+       req->xfer[0].tx_buf = &req->command;
+       req->xfer[0].len = 2;
+
+       req->xfer[1].rx_buf = &req->sample;
+       req->xfer[1].len = 2;
+
+       spi_message_add_tail(&req->xfer[0], &req->msg);
+       spi_message_add_tail(&req->xfer[1], &req->msg);
+
+       status = spi_sync(spi, &req->msg);
+       ret = status ? : req->sample;
+
+       kfree(req);
+
+       return ret;
+}
+
+static int ad7877_write(struct spi_device *spi, u16 reg, u16 val)
+{
+       struct ser_req *req;
+       int status;
+
+       req = kzalloc(sizeof *req, GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       spi_message_init(&req->msg);
+
+       req->command = (u16) (AD7877_WRITEADD(reg) | (val & MAX_12BIT));
+       req->xfer[0].tx_buf = &req->command;
+       req->xfer[0].len = 2;
+
+       spi_message_add_tail(&req->xfer[0], &req->msg);
+
+       status = spi_sync(spi, &req->msg);
+
+       kfree(req);
+
+       return status;
+}
+
+static int ad7877_read_adc(struct spi_device *spi, unsigned command)
+{
+       struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+       struct ser_req *req;
+       int status;
+       int sample;
+       int i;
+
+       req = kzalloc(sizeof *req, GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       spi_message_init(&req->msg);
+
+       /* activate reference, so it has time to settle; */
+       req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+                        AD7877_POL(ts->stopacq_polarity) |
+                        AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) |
+                        AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0);
+
+       req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC;
+
+       req->command = (u16) command;
+
+       req->xfer[0].tx_buf = &req->reset;
+       req->xfer[0].len = 2;
+
+       req->xfer[1].tx_buf = &req->ref_on;
+       req->xfer[1].len = 2;
+       req->xfer[1].delay_usecs = ts->vref_delay_usecs;
+
+       req->xfer[2].tx_buf = &req->command;
+       req->xfer[2].len = 2;
+       req->xfer[2].delay_usecs = ts->vref_delay_usecs;
+
+       req->xfer[3].rx_buf = &req->sample;
+       req->xfer[3].len = 2;
+
+       req->xfer[4].tx_buf = &ts->cmd_crtl2;   /*REF OFF*/
+       req->xfer[4].len = 2;
+
+       req->xfer[5].tx_buf = &ts->cmd_crtl1;   /*DEFAULT*/
+       req->xfer[5].len = 2;
+
+       /* group all the transfers together, so we can't interfere with
+        * reading touchscreen state; disable penirq while sampling
+        */
+       for (i = 0; i < 6; i++)
+               spi_message_add_tail(&req->xfer[i], &req->msg);
+
+       status = spi_sync(spi, &req->msg);
+       sample = req->sample;
+
+       kfree(req);
+
+       return status ? : sample;
+}
+
+static void ad7877_rx(struct ad7877 *ts)
+{
+       struct input_dev *input_dev = ts->input;
+       unsigned Rt;
+       u16 x, y, z1, z2;
+
+       x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT;
+       y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT;
+       z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT;
+       z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT;
+
+       /*
+        * The samples processed here are already preprocessed by the AD7877.
+        * The preprocessing function consists of an averaging filter.
+        * The combination of 'first conversion delay' and averaging provides a robust solution,
+        * discarding the spurious noise in the signal and keeping only the data of interest.
+        * The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.h)
+        * Other user-programmable conversion controls include variable acquisition time,
+        * and first conversion delay. Up to 16 averages can be taken per conversion.
+        */
+
+       if (likely(x && z1)) {
+               /* compute touch pressure resistance using equation #1 */
+               Rt = (z2 - z1) * x * ts->x_plate_ohms;
+               Rt /= z1;
+               Rt = (Rt + 2047) >> 12;
+
+               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);
+       }
+}
+
+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_sync(input_dev);
+}
+
+static void ad7877_timer(unsigned long handle)
+{
+       struct ad7877 *ts = (void *)handle;
+
+       ad7877_ts_event_release(ts);
+}
+
+static irqreturn_t ad7877_irq(int irq, void *handle)
+{
+       struct ad7877 *ts = handle;
+       unsigned long flags;
+       int status;
+
+       /*
+        * 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.
+        */
+
+       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);
+       }
+       spin_unlock_irqrestore(&ts->lock, flags);
+
+       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;
+               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
+        * leave it that way after every request
+        */
+
+       mutex_unlock(&ts->mutex);
+}
+
+static void ad7877_enable(struct ad7877 *ts)
+{
+       mutex_lock(&ts->mutex);
+
+       if (ts->disabled) {
+               ts->disabled = 0;
+               enable_irq(ts->spi->irq);
+       }
+
+       mutex_unlock(&ts->mutex);
+}
+
+#define SHOW(name) static ssize_t \
+name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+       struct ad7877   *ts = dev_get_drvdata(dev); \
+       ssize_t v = ad7877_read_adc(ts->spi, \
+                       AD7877_READ_CHAN(name)); \
+       if (v < 0) \
+               return v; \
+       return sprintf(buf, "%u\n", (unsigned) v); \
+} \
+static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
+
+SHOW(aux1)
+SHOW(aux2)
+SHOW(aux3)
+SHOW(bat1)
+SHOW(bat2)
+SHOW(temp1)
+SHOW(temp2)
+
+static ssize_t ad7877_disable_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct ad7877   *ts = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7877_disable_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ad7877 *ts = dev_get_drvdata(dev);
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buf, 10, &val);
+       if (error)
+               return error;
+
+       if (val)
+               ad7877_disable(ts);
+       else
+               ad7877_enable(ts);
+
+       return count;
+}
+
+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);
+
+       return sprintf(buf, "%u\n", ts->dac);
+}
+
+static ssize_t ad7877_dac_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ad7877 *ts = dev_get_drvdata(dev);
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buf, 10, &val);
+       if (error)
+               return error;
+
+       mutex_lock(&ts->mutex);
+       ts->dac = val & 0xFF;
+       ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF);
+       mutex_unlock(&ts->mutex);
+
+       return count;
+}
+
+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);
+
+       return sprintf(buf, "%u\n", ts->gpio3);
+}
+
+static ssize_t ad7877_gpio3_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ad7877 *ts = dev_get_drvdata(dev);
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buf, 10, &val);
+       if (error)
+               return error;
+
+       mutex_lock(&ts->mutex);
+       ts->gpio3 = !!val;
+       ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+                (ts->gpio4 << 4) | (ts->gpio3 << 5));
+       mutex_unlock(&ts->mutex);
+
+       return count;
+}
+
+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);
+
+       return sprintf(buf, "%u\n", ts->gpio4);
+}
+
+static ssize_t ad7877_gpio4_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ad7877 *ts = dev_get_drvdata(dev);
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buf, 10, &val);
+       if (error)
+               return error;
+
+       mutex_lock(&ts->mutex);
+       ts->gpio4 = !!val;
+       ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
+                    (ts->gpio4 << 4) | (ts->gpio3 << 5));
+       mutex_unlock(&ts->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store);
+
+static struct attribute *ad7877_attributes[] = {
+       &dev_attr_temp1.attr,
+       &dev_attr_temp2.attr,
+       &dev_attr_aux1.attr,
+       &dev_attr_aux2.attr,
+       &dev_attr_bat1.attr,
+       &dev_attr_bat2.attr,
+       &dev_attr_disable.attr,
+       &dev_attr_dac.attr,
+       &dev_attr_gpio4.attr,
+       NULL
+};
+
+static const struct attribute_group ad7877_attr_group = {
+       .attrs = ad7877_attributes,
+};
+
+static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
+{
+       struct spi_message *m;
+       int i;
+
+       ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) |
+                       AD7877_POL(ts->stopacq_polarity) |
+                       AD7877_AVG(ts->averaging) | AD7877_PM(1) |
+                       AD7877_TMR(ts->pen_down_acc_interval) |
+                       AD7877_ACQ(ts->acquisition_time) |
+                       AD7877_FCD(ts->first_conversion_delay);
+
+       ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2);
+
+       ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) |
+                       AD7877_READADD(AD7877_REG_XPLUS-1) |
+                       AD7877_MODE_SEQ1 | AD7877_DFR;
+
+       ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1);
+
+       ts->cmd_dummy = 0;
+
+       m = &ts->msg;
+
+       spi_message_init(m);
+
+       m->complete = ad7877_callback;
+       m->context = ts;
+
+       ts->xfer[0].tx_buf = &ts->cmd_crtl1;
+       ts->xfer[0].len = 2;
+
+       spi_message_add_tail(&ts->xfer[0], m);
+
+       ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
+       ts->xfer[1].len = 2;
+
+       spi_message_add_tail(&ts->xfer[1], m);
+
+       for (i = 0; i < 11; i++) {
+               ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
+               ts->xfer[i + 2].len = 2;
+               spi_message_add_tail(&ts->xfer[i + 2], m);
+       }
+}
+
+static int __devinit ad7877_probe(struct spi_device *spi)
+{
+       struct ad7877                   *ts;
+       struct input_dev                *input_dev;
+       struct ad7877_platform_data     *pdata = spi->dev.platform_data;
+       int                             err;
+       u16                             verify;
+
+       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 SPI CLK frequency */
+       if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+               dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz);
+               return -EINVAL;
+       }
+
+       ts = kzalloc(sizeof(struct ad7877), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!ts || !input_dev) {
+               err = -ENOMEM;
+               goto err_free_mem;
+       }
+
+       dev_set_drvdata(&spi->dev, ts);
+       ts->spi = spi;
+       ts->input = input_dev;
+
+       setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts);
+       mutex_init(&ts->mutex);
+       spin_lock_init(&ts->lock);
+
+       ts->model = pdata->model ? : 7877;
+       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;
+
+       ts->stopacq_polarity = pdata->stopacq_polarity;
+       ts->first_conversion_delay = pdata->first_conversion_delay;
+       ts->acquisition_time = pdata->acquisition_time;
+       ts->averaging = pdata->averaging;
+       ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+
+       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
+
+       input_dev->name = "AD7877 Touchscreen";
+       input_dev->phys = ts->phys;
+       input_dev->dev.parent = &spi->dev;
+
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(ABS_X, input_dev->absbit);
+       __set_bit(ABS_Y, input_dev->absbit);
+       __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+       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);
+
+       ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE);
+
+       verify = ad7877_read(spi, AD7877_REG_SEQ1);
+
+       if (verify != AD7877_MM_SEQUENCE){
+               dev_err(&spi->dev, "%s: Failed to probe %s\n",
+                       dev_name(&spi->dev), input_dev->name);
+               err = -ENODEV;
+               goto err_free_mem;
+       }
+
+       if (gpio3)
+               ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF);
+
+       ad7877_setup_ts_def_msg(spi, ts);
+
+       /* Request AD7877 /DAV GPIO interrupt */
+
+       err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING |
+                       IRQF_SAMPLE_RANDOM, spi->dev.driver->name, ts);
+       if (err) {
+               dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
+               goto err_free_mem;
+       }
+
+       err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group);
+       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;
+
+       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:
+       free_irq(spi->irq, ts);
+err_free_mem:
+       input_free_device(input_dev);
+       kfree(ts);
+       dev_set_drvdata(&spi->dev, NULL);
+       return err;
+}
+
+static int __devexit ad7877_remove(struct spi_device *spi)
+{
+       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);
+
+       input_unregister_device(ts->input);
+       kfree(ts);
+
+       dev_dbg(&spi->dev, "unregistered touchscreen\n");
+       dev_set_drvdata(&spi->dev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad7877_suspend(struct spi_device *spi, pm_message_t message)
+{
+       struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+
+       ad7877_disable(ts);
+
+       return 0;
+}
+
+static int ad7877_resume(struct spi_device *spi)
+{
+       struct ad7877 *ts = dev_get_drvdata(&spi->dev);
+
+       ad7877_enable(ts);
+
+       return 0;
+}
+#else
+#define ad7877_suspend NULL
+#define ad7877_resume  NULL
+#endif
+
+static struct spi_driver ad7877_driver = {
+       .driver = {
+               .name   = "ad7877",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ad7877_probe,
+       .remove         = __devexit_p(ad7877_remove),
+       .suspend        = ad7877_suspend,
+       .resume         = ad7877_resume,
+};
+
+static int __init ad7877_init(void)
+{
+       return spi_register_driver(&ad7877_driver);
+}
+module_init(ad7877_init);
+
+static void __exit ad7877_exit(void)
+{
+       spi_unregister_driver(&ad7877_driver);
+}
+module_exit(ad7877_exit);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7877 touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
new file mode 100644 (file)
index 0000000..ea4c61d
--- /dev/null
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) 2008 Michael Hennerich, Analog Devices Inc.
+ *
+ * Description:        AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface)
+ *
+ * Bugs:        Enter bugs at http://blackfin.uclinux.org/
+ *
+ * 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, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * History:
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * Various changes: Imre Deak <imre.deak@nokia.com>
+ *
+ * Using code from:
+ *  - corgi_ts.c
+ *     Copyright (C) 2004-2005 Richard Purdie
+ *  - omap_ts.[hc], ads7846.h, ts_osk.c
+ *     Copyright (C) 2002 MontaVista Software
+ *     Copyright (C) 2004 Texas Instruments
+ *     Copyright (C) 2005 Dirk Behme
+ *  - ad7877.c
+ *     Copyright (C) 2006-2008 Analog Devices Inc.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+#include <linux/i2c.h>
+
+#include <linux/spi/ad7879.h>
+
+#define AD7879_REG_ZEROS               0
+#define AD7879_REG_CTRL1               1
+#define AD7879_REG_CTRL2               2
+#define AD7879_REG_CTRL3               3
+#define AD7879_REG_AUX1HIGH            4
+#define AD7879_REG_AUX1LOW             5
+#define AD7879_REG_TEMP1HIGH           6
+#define AD7879_REG_TEMP1LOW            7
+#define AD7879_REG_XPLUS               8
+#define AD7879_REG_YPLUS               9
+#define AD7879_REG_Z1                  10
+#define AD7879_REG_Z2                  11
+#define AD7879_REG_AUXVBAT             12
+#define AD7879_REG_TEMP                        13
+#define AD7879_REG_REVID               14
+
+/* Control REG 1 */
+#define AD7879_TMR(x)                  ((x & 0xFF) << 0)
+#define AD7879_ACQ(x)                  ((x & 0x3) << 8)
+#define AD7879_MODE_NOC                        (0 << 10)       /* Do not convert */
+#define AD7879_MODE_SCC                        (1 << 10)       /* Single channel conversion */
+#define AD7879_MODE_SEQ0               (2 << 10)       /* Sequence 0 in Slave Mode */
+#define AD7879_MODE_SEQ1               (3 << 10)       /* Sequence 1 in Master Mode */
+#define AD7879_MODE_INT                        (1 << 15)       /* PENIRQ disabled INT enabled */
+
+/* Control REG 2 */
+#define AD7879_FCD(x)                  ((x & 0x3) << 0)
+#define AD7879_RESET                   (1 << 4)
+#define AD7879_MFS(x)                  ((x & 0x3) << 5)
+#define AD7879_AVG(x)                  ((x & 0x3) << 7)
+#define        AD7879_SER                      (1 << 9)        /* non-differential */
+#define        AD7879_DFR                      (0 << 9)        /* differential */
+#define AD7879_GPIOPOL                 (1 << 10)
+#define AD7879_GPIODIR                 (1 << 11)
+#define AD7879_GPIO_DATA               (1 << 12)
+#define AD7879_GPIO_EN                 (1 << 13)
+#define AD7879_PM(x)                   ((x & 0x3) << 14)
+#define AD7879_PM_SHUTDOWN             (0)
+#define AD7879_PM_DYN                  (1)
+#define AD7879_PM_FULLON               (2)
+
+/* Control REG 3 */
+#define AD7879_TEMPMASK_BIT            (1<<15)
+#define AD7879_AUXVBATMASK_BIT         (1<<14)
+#define AD7879_INTMODE_BIT             (1<<13)
+#define AD7879_GPIOALERTMASK_BIT       (1<<12)
+#define AD7879_AUXLOW_BIT              (1<<11)
+#define AD7879_AUXHIGH_BIT             (1<<10)
+#define AD7879_TEMPLOW_BIT             (1<<9)
+#define AD7879_TEMPHIGH_BIT            (1<<8)
+#define AD7879_YPLUS_BIT               (1<<7)
+#define AD7879_XPLUS_BIT               (1<<6)
+#define AD7879_Z1_BIT                  (1<<5)
+#define AD7879_Z2_BIT                  (1<<4)
+#define AD7879_AUX_BIT                 (1<<3)
+#define AD7879_VBAT_BIT                        (1<<2)
+#define AD7879_TEMP_BIT                        (1<<1)
+
+enum {
+       AD7879_SEQ_XPOS  = 0,
+       AD7879_SEQ_YPOS  = 1,
+       AD7879_SEQ_Z1    = 2,
+       AD7879_SEQ_Z2    = 3,
+       AD7879_NR_SENSE  = 4,
+};
+
+#define        MAX_12BIT                       ((1<<12)-1)
+#define        TS_PEN_UP_TIMEOUT               msecs_to_jiffies(50)
+
+#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
+#define AD7879_DEVID           0x7A
+typedef struct spi_device      bus_device;
+#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE)
+#define AD7879_DEVID           0x79
+typedef struct i2c_client      bus_device;
+#endif
+
+struct ad7879 {
+       bus_device              *bus;
+       struct input_dev        *input;
+       struct work_struct      work;
+       struct timer_list       timer;
+
+       struct mutex            mutex;
+       unsigned                disabled:1;     /* P: mutex */
+
+#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
+       struct spi_message      msg;
+       struct spi_transfer     xfer[AD7879_NR_SENSE + 1];
+       u16                     cmd;
+#endif
+       u16                     conversion_data[AD7879_NR_SENSE];
+       char                    phys[32];
+       u8                      first_conversion_delay;
+       u8                      acquisition_time;
+       u8                      averaging;
+       u8                      pen_down_acc_interval;
+       u8                      median;
+       u16                     x_plate_ohms;
+       u16                     pressure_max;
+       u16                     gpio_init;
+       u16                     cmd_crtl1;
+       u16                     cmd_crtl2;
+       u16                     cmd_crtl3;
+       unsigned                gpio:1;
+};
+
+static int ad7879_read(bus_device *, u8);
+static int ad7879_write(bus_device *, u8, u16);
+static void ad7879_collect(struct ad7879 *);
+
+static void ad7879_report(struct ad7879 *ts)
+{
+       struct input_dev *input_dev = ts->input;
+       unsigned Rt;
+       u16 x, y, z1, z2;
+
+       x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT;
+       y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT;
+       z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;
+       z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT;
+
+       /*
+        * The samples processed here are already preprocessed by the AD7879.
+        * The preprocessing function consists of a median and an averaging filter.
+        * The combination of these two techniques provides a robust solution,
+        * discarding the spurious noise in the signal and keeping only the data of interest.
+        * The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h)
+        * Other user-programmable conversion controls include variable acquisition time,
+        * and first conversion delay. Up to 16 averages can be taken per conversion.
+        */
+
+       if (likely(x && z1)) {
+               /* compute touch pressure resistance using equation #1 */
+               Rt = (z2 - z1) * x * ts->x_plate_ohms;
+               Rt /= z1;
+               Rt = (Rt + 2047) >> 12;
+
+               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);
+       }
+}
+
+static void ad7879_work(struct work_struct *work)
+{
+       struct ad7879 *ts = container_of(work, struct ad7879, work);
+
+       /* use keventd context to read the result registers */
+       ad7879_collect(ts);
+       ad7879_report(ts);
+       mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+}
+
+static void ad7879_ts_event_release(struct ad7879 *ts)
+{
+       struct input_dev *input_dev = ts->input;
+
+       input_report_abs(input_dev, ABS_PRESSURE, 0);
+       input_sync(input_dev);
+}
+
+static void ad7879_timer(unsigned long handle)
+{
+       struct ad7879 *ts = (void *)handle;
+
+       ad7879_ts_event_release(ts);
+}
+
+static irqreturn_t ad7879_irq(int irq, void *handle)
+{
+       struct ad7879 *ts = handle;
+
+       /* 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
+        */
+
+       if (!work_pending(&ts->work))
+               schedule_work(&ts->work);
+
+       return IRQ_HANDLED;
+}
+
+static void ad7879_setup(struct ad7879 *ts)
+{
+       ts->cmd_crtl3 = AD7879_YPLUS_BIT |
+                       AD7879_XPLUS_BIT |
+                       AD7879_Z2_BIT |
+                       AD7879_Z1_BIT |
+                       AD7879_TEMPMASK_BIT |
+                       AD7879_AUXVBATMASK_BIT |
+                       AD7879_GPIOALERTMASK_BIT;
+
+       ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR |
+                       AD7879_AVG(ts->averaging) |
+                       AD7879_MFS(ts->median) |
+                       AD7879_FCD(ts->first_conversion_delay) |
+                       ts->gpio_init;
+
+       ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 |
+                       AD7879_ACQ(ts->acquisition_time) |
+                       AD7879_TMR(ts->pen_down_acc_interval);
+
+       ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2);
+       ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3);
+       ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1);
+}
+
+static void ad7879_disable(struct ad7879 *ts)
+{
+       mutex_lock(&ts->mutex);
+
+       if (!ts->disabled) {
+
+               ts->disabled = 1;
+               disable_irq(ts->bus->irq);
+
+               cancel_work_sync(&ts->work);
+
+               if (del_timer_sync(&ts->timer))
+                       ad7879_ts_event_release(ts);
+
+               ad7879_write(ts->bus, AD7879_REG_CTRL2,
+                            AD7879_PM(AD7879_PM_SHUTDOWN));
+       }
+
+       mutex_unlock(&ts->mutex);
+}
+
+static void ad7879_enable(struct ad7879 *ts)
+{
+       mutex_lock(&ts->mutex);
+
+       if (ts->disabled) {
+               ad7879_setup(ts);
+               ts->disabled = 0;
+               enable_irq(ts->bus->irq);
+       }
+
+       mutex_unlock(&ts->mutex);
+}
+
+static ssize_t ad7879_disable_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct ad7879 *ts = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t ad7879_disable_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ad7879 *ts = dev_get_drvdata(dev);
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buf, 10, &val);
+       if (error)
+               return error;
+
+       if (val)
+               ad7879_disable(ts);
+       else
+               ad7879_enable(ts);
+
+       return count;
+}
+
+static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store);
+
+static ssize_t ad7879_gpio_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct ad7879 *ts = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", ts->gpio);
+}
+
+static ssize_t ad7879_gpio_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct ad7879 *ts = dev_get_drvdata(dev);
+       unsigned long val;
+       int error;
+
+       error = strict_strtoul(buf, 10, &val);
+       if (error)
+               return error;
+
+       mutex_lock(&ts->mutex);
+       ts->gpio = !!val;
+       error = ad7879_write(ts->bus, AD7879_REG_CTRL2,
+                          ts->gpio ?
+                               ts->cmd_crtl2 & ~AD7879_GPIO_DATA :
+                               ts->cmd_crtl2 | AD7879_GPIO_DATA);
+       mutex_unlock(&ts->mutex);
+
+       return error ? : count;
+}
+
+static DEVICE_ATTR(gpio, 0664, ad7879_gpio_show, ad7879_gpio_store);
+
+static struct attribute *ad7879_attributes[] = {
+       &dev_attr_disable.attr,
+       &dev_attr_gpio.attr,
+       NULL
+};
+
+static const struct attribute_group ad7879_attr_group = {
+       .attrs = ad7879_attributes,
+};
+
+static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)
+{
+       struct input_dev *input_dev;
+       struct ad7879_platform_data *pdata = bus->dev.platform_data;
+       int err;
+       u16 revid;
+
+       if (!bus->irq) {
+               dev_err(&bus->dev, "no IRQ?\n");
+               return -ENODEV;
+       }
+
+       if (!pdata) {
+               dev_err(&bus->dev, "no platform data?\n");
+               return -ENODEV;
+       }
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               return -ENOMEM;
+
+       ts->input = input_dev;
+
+       setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
+       INIT_WORK(&ts->work, ad7879_work);
+       mutex_init(&ts->mutex);
+
+       ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+       ts->pressure_max = pdata->pressure_max ? : ~0;
+
+       ts->first_conversion_delay = pdata->first_conversion_delay;
+       ts->acquisition_time = pdata->acquisition_time;
+       ts->averaging = pdata->averaging;
+       ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+       ts->median = pdata->median;
+
+       if (pdata->gpio_output)
+               ts->gpio_init = AD7879_GPIO_EN |
+                               (pdata->gpio_default ? 0 : AD7879_GPIO_DATA);
+       else
+               ts->gpio_init = AD7879_GPIO_EN | AD7879_GPIODIR;
+
+       snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev));
+
+       input_dev->name = "AD7879 Touchscreen";
+       input_dev->phys = ts->phys;
+       input_dev->dev.parent = &bus->dev;
+
+       __set_bit(EV_ABS, input_dev->evbit);
+       __set_bit(ABS_X, input_dev->absbit);
+       __set_bit(ABS_Y, input_dev->absbit);
+       __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+       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);
+
+       err = ad7879_write(bus, AD7879_REG_CTRL2, AD7879_RESET);
+
+       if (err < 0) {
+               dev_err(&bus->dev, "Failed to write %s\n", input_dev->name);
+               goto err_free_mem;
+       }
+
+       revid = ad7879_read(bus, AD7879_REG_REVID);
+
+       if ((revid & 0xFF) != AD7879_DEVID) {
+               dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name);
+               err = -ENODEV;
+               goto err_free_mem;
+       }
+
+       ad7879_setup(ts);
+
+       err = request_irq(bus->irq, ad7879_irq,
+                         IRQF_TRIGGER_FALLING | IRQF_SAMPLE_RANDOM,
+                         bus->dev.driver->name, ts);
+
+       if (err) {
+               dev_err(&bus->dev, "irq %d busy?\n", bus->irq);
+               goto err_free_mem;
+       }
+
+       err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group);
+       if (err)
+               goto err_free_irq;
+
+       err = input_register_device(input_dev);
+       if (err)
+               goto err_remove_attr;
+
+       dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n",
+                revid >> 8, bus->irq);
+
+       return 0;
+
+err_remove_attr:
+       sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group);
+err_free_irq:
+       free_irq(bus->irq, ts);
+err_free_mem:
+       input_free_device(input_dev);
+
+       return err;
+}
+
+static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts)
+{
+       ad7879_disable(ts);
+       sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group);
+       free_irq(ts->bus->irq, ts);
+       input_unregister_device(ts->input);
+       dev_dbg(&bus->dev, "unregistered touchscreen\n");
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int ad7879_suspend(bus_device *bus, pm_message_t message)
+{
+       struct ad7879 *ts = dev_get_drvdata(&bus->dev);
+
+       ad7879_disable(ts);
+
+       return 0;
+}
+
+static int ad7879_resume(bus_device *bus)
+{
+       struct ad7879 *ts = dev_get_drvdata(&bus->dev);
+
+       ad7879_enable(ts);
+
+       return 0;
+}
+#else
+#define ad7879_suspend NULL
+#define ad7879_resume  NULL
+#endif
+
+#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
+#define MAX_SPI_FREQ_HZ                5000000
+#define AD7879_CMD_MAGIC       0xE000
+#define AD7879_CMD_READ                (1 << 10)
+#define AD7879_WRITECMD(reg)   (AD7879_CMD_MAGIC | (reg & 0xF))
+#define AD7879_READCMD(reg)    (AD7879_CMD_MAGIC | AD7879_CMD_READ | (reg & 0xF))
+
+struct ser_req {
+       u16                     command;
+       u16                     data;
+       struct spi_message      msg;
+       struct spi_transfer     xfer[2];
+};
+
+/*
+ * ad7879_read/write are only used for initial setup and for sysfs controls.
+ * The main traffic is done in ad7879_collect().
+ */
+
+static int ad7879_read(struct spi_device *spi, u8 reg)
+{
+       struct ser_req *req;
+       int status, ret;
+
+       req = kzalloc(sizeof *req, GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       spi_message_init(&req->msg);
+
+       req->command = (u16) AD7879_READCMD(reg);
+       req->xfer[0].tx_buf = &req->command;
+       req->xfer[0].len = 2;
+
+       req->xfer[1].rx_buf = &req->data;
+       req->xfer[1].len = 2;
+
+       spi_message_add_tail(&req->xfer[0], &req->msg);
+       spi_message_add_tail(&req->xfer[1], &req->msg);
+
+       status = spi_sync(spi, &req->msg);
+       ret = status ? : req->data;
+
+       kfree(req);
+
+       return ret;
+}
+
+static int ad7879_write(struct spi_device *spi, u8 reg, u16 val)
+{
+       struct ser_req *req;
+       int status;
+
+       req = kzalloc(sizeof *req, GFP_KERNEL);
+       if (!req)
+               return -ENOMEM;
+
+       spi_message_init(&req->msg);
+
+       req->command = (u16) AD7879_WRITECMD(reg);
+       req->xfer[0].tx_buf = &req->command;
+       req->xfer[0].len = 2;
+
+       req->data = val;
+       req->xfer[1].tx_buf = &req->data;
+       req->xfer[1].len = 2;
+
+       spi_message_add_tail(&req->xfer[0], &req->msg);
+       spi_message_add_tail(&req->xfer[1], &req->msg);
+
+       status = spi_sync(spi, &req->msg);
+
+       kfree(req);
+
+       return status;
+}
+
+static void ad7879_collect(struct ad7879 *ts)
+{
+       int status = spi_sync(ts->bus, &ts->msg);
+
+       if (status)
+               dev_err(&ts->bus->dev, "spi_sync --> %d\n", status);
+}
+
+static void ad7879_setup_ts_def_msg(struct ad7879 *ts)
+{
+       struct spi_message *m;
+       int i;
+
+       ts->cmd = (u16) AD7879_READCMD(AD7879_REG_XPLUS);
+
+       m = &ts->msg;
+       spi_message_init(m);
+       ts->xfer[0].tx_buf = &ts->cmd;
+       ts->xfer[0].len = 2;
+
+       spi_message_add_tail(&ts->xfer[0], m);
+
+       for (i = 0; i < AD7879_NR_SENSE; i++) {
+               ts->xfer[i + 1].rx_buf = &ts->conversion_data[i];
+               ts->xfer[i + 1].len = 2;
+               spi_message_add_tail(&ts->xfer[i + 1], m);
+       }
+}
+
+static int __devinit ad7879_probe(struct spi_device *spi)
+{
+       struct ad7879 *ts;
+       int error;
+
+       /* don't exceed max specified SPI CLK frequency */
+       if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+               dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz);
+               return -EINVAL;
+       }
+
+       ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, ts);
+       ts->bus = spi;
+
+       ad7879_setup_ts_def_msg(ts);
+
+       error = ad7879_construct(spi, ts);
+       if (error) {
+               dev_set_drvdata(&spi->dev, NULL);
+               kfree(ts);
+       }
+
+       return 0;
+}
+
+static int __devexit ad7879_remove(struct spi_device *spi)
+{
+       struct ad7879 *ts = dev_get_drvdata(&spi->dev);
+
+       ad7879_destroy(spi, ts);
+       dev_set_drvdata(&spi->dev, NULL);
+       kfree(ts);
+
+       return 0;
+}
+
+static struct spi_driver ad7879_driver = {
+       .driver = {
+               .name   = "ad7879",
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ad7879_probe,
+       .remove         = __devexit_p(ad7879_remove),
+       .suspend        = ad7879_suspend,
+       .resume         = ad7879_resume,
+};
+
+static int __init ad7879_init(void)
+{
+       return spi_register_driver(&ad7879_driver);
+}
+module_init(ad7879_init);
+
+static void __exit ad7879_exit(void)
+{
+       spi_unregister_driver(&ad7879_driver);
+}
+module_exit(ad7879_exit);
+
+#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE)
+
+/* All registers are word-sized.
+ * AD7879 uses a high-byte first convention.
+ */
+static int ad7879_read(struct i2c_client *client, u8 reg)
+{
+       return swab16(i2c_smbus_read_word_data(client, reg));
+}
+
+static int ad7879_write(struct i2c_client *client, u8 reg, u16 val)
+{
+       return i2c_smbus_write_word_data(client, reg, swab16(val));
+}
+
+static void ad7879_collect(struct ad7879 *ts)
+{
+       int i;
+
+       for (i = 0; i < AD7879_NR_SENSE; i++)
+               ts->conversion_data[i] = ad7879_read(ts->bus,
+                                                    AD7879_REG_XPLUS + i);
+}
+
+static int __devinit ad7879_probe(struct i2c_client *client,
+                                       const struct i2c_device_id *id)
+{
+       struct ad7879 *ts;
+       int error;
+
+       if (!i2c_check_functionality(client->adapter,
+                                       I2C_FUNC_SMBUS_WORD_DATA)) {
+               dev_err(&client->dev, "SMBUS Word Data not Supported\n");
+               return -EIO;
+       }
+
+       ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL);
+       if (!ts)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, ts);
+       ts->bus = client;
+
+       error = ad7879_construct(client, ts);
+       if (error) {
+               i2c_set_clientdata(client, NULL);
+               kfree(ts);
+       }
+
+       return 0;
+}
+
+static int __devexit ad7879_remove(struct i2c_client *client)
+{
+       struct ad7879 *ts = dev_get_drvdata(&client->dev);
+
+       ad7879_destroy(client, ts);
+       i2c_set_clientdata(client, NULL);
+       kfree(ts);
+
+       return 0;
+}
+
+static const struct i2c_device_id ad7879_id[] = {
+       { "ad7879", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ad7879_id);
+
+static struct i2c_driver ad7879_driver = {
+       .driver = {
+               .name   = "ad7879",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ad7879_probe,
+       .remove         = __devexit_p(ad7879_remove),
+       .suspend        = ad7879_suspend,
+       .resume         = ad7879_resume,
+       .id_table       = ad7879_id,
+};
+
+static int __init ad7879_init(void)
+{
+       return i2c_add_driver(&ad7879_driver);
+}
+module_init(ad7879_init);
+
+static void __exit ad7879_exit(void)
+{
+       i2c_del_driver(&ad7879_driver);
+}
+module_exit(ad7879_exit);
+#endif
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
+MODULE_LICENSE("GPL");
index 1d11e2b..dfa6a84 100644 (file)
@@ -162,6 +162,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
                input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
                input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
                input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+               input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
                input_sync(wm->input_dev);
                reads++;
        } while (reads < cinfo[sp_idx].reads);
@@ -245,7 +246,7 @@ static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
        if (enable)
                enable_irq(wm->pen_irq);
        else
-               disable_irq(wm->pen_irq);
+               disable_irq_nosync(wm->pen_irq);
 }
 
 static struct wm97xx_mach_ops mainstone_mach_ops = {
index 5498662..e868264 100644 (file)
@@ -151,12 +151,14 @@ static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16
        input_report_abs(idev, ABS_X, x);
        input_report_abs(idev, ABS_Y, y);
        input_report_abs(idev, ABS_PRESSURE, pressure);
+       input_report_key(idev, BTN_TOUCH, 1);
        input_sync(idev);
 }
 
 static void ucb1400_ts_event_release(struct input_dev *idev)
 {
        input_report_abs(idev, ABS_PRESSURE, 0);
+       input_report_key(idev, BTN_TOUCH, 0);
        input_sync(idev);
 }
 
@@ -377,7 +379,8 @@ static int ucb1400_ts_probe(struct platform_device *dev)
        ucb->ts_idev->id.product        = ucb->id;
        ucb->ts_idev->open              = ucb1400_ts_open;
        ucb->ts_idev->close             = ucb1400_ts_close;
-       ucb->ts_idev->evbit[0]          = BIT_MASK(EV_ABS);
+       ucb->ts_idev->evbit[0]          = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
+       ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
 
        ucb1400_adc_enable(ucb->ac97);
        x_res = ucb1400_ts_read_xres(ucb);
index d15aa11..cec480b 100644 (file)
@@ -409,6 +409,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)
                        wm->pen_is_down = 0;
                        dev_dbg(wm->dev, "pen up\n");
                        input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
+                       input_report_key(wm->input_dev, BTN_TOUCH, 0);
                        input_sync(wm->input_dev);
                } else if (!(rc & RC_AGAIN)) {
                        /* We need high frequency updates only while
@@ -433,6 +434,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)
                input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
                input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
                input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
+               input_report_key(wm->input_dev, BTN_TOUCH, 1);
                input_sync(wm->input_dev);
                wm->pen_is_down = 1;
                wm->ts_reader_interval = wm->ts_reader_min_interval;
@@ -628,18 +630,21 @@ static int wm97xx_probe(struct device *dev)
        wm->input_dev->phys = "wm97xx";
        wm->input_dev->open = wm97xx_ts_input_open;
        wm->input_dev->close = wm97xx_ts_input_close;
-       set_bit(EV_ABS, wm->input_dev->evbit);
-       set_bit(ABS_X, wm->input_dev->absbit);
-       set_bit(ABS_Y, wm->input_dev->absbit);
-       set_bit(ABS_PRESSURE, wm->input_dev->absbit);
+
+       __set_bit(EV_ABS, wm->input_dev->evbit);
+       __set_bit(EV_KEY, wm->input_dev->evbit);
+       __set_bit(BTN_TOUCH, wm->input_dev->keybit);
+
        input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
                             abs_x[2], 0);
        input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
                             abs_y[2], 0);
        input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
                             abs_p[2], 0);
+
        input_set_drvdata(wm->input_dev, wm);
        wm->input_dev->dev.parent = dev;
+
        ret = input_register_device(wm->input_dev);
        if (ret < 0)
                goto dev_alloc_err;
diff --git a/drivers/input/touchscreen/zylonite-wm97xx.c b/drivers/input/touchscreen/zylonite-wm97xx.c
new file mode 100644 (file)
index 0000000..41e4359
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * zylonite-wm97xx.c  --  Zylonite Continuous Touch screen driver
+ *
+ * Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *
+ *  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.
+ *
+ * Notes:
+ *     This is a wm97xx extended touch driver supporting interrupt driven
+ *     and continuous operation on Marvell Zylonite development systems
+ *     (which have a WM9713 on board).
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/wm97xx.h>
+
+#include <mach/hardware.h>
+#include <mach/mfp.h>
+#include <mach/regs-ac97.h>
+
+struct continuous {
+       u16 id;    /* codec id */
+       u8 code;   /* continuous code */
+       u8 reads;  /* number of coord reads per read cycle */
+       u32 speed; /* number of coords per second */
+};
+
+#define WM_READS(sp) ((sp / HZ) + 1)
+
+static const struct continuous cinfo[] = {
+       { WM9713_ID2, 0, WM_READS(94),  94  },
+       { WM9713_ID2, 1, WM_READS(120), 120 },
+       { WM9713_ID2, 2, WM_READS(154), 154 },
+       { WM9713_ID2, 3, WM_READS(188), 188 },
+};
+
+/* continuous speed index */
+static int sp_idx;
+
+/*
+ * Pen sampling frequency (Hz) in continuous mode.
+ */
+static int cont_rate = 200;
+module_param(cont_rate, int, 0);
+MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
+
+/*
+ * Pressure readback.
+ *
+ * Set to 1 to read back pen down pressure
+ */
+static int pressure;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
+
+/*
+ * AC97 touch data slot.
+ *
+ * Touch screen readback data ac97 slot
+ */
+static int ac97_touch_slot = 5;
+module_param(ac97_touch_slot, int, 0);
+MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
+
+
+/* flush AC97 slot 5 FIFO machines */
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+       int i;
+
+       msleep(1);
+
+       for (i = 0; i < 16; i++)
+               MODR;
+}
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+       u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+       int reads = 0;
+       static u16 last, tries;
+
+       /* When the AC97 queue has been drained we need to allow time
+        * to buffer up samples otherwise we end up spinning polling
+        * for samples.  The controller can't have a suitably low
+        * threashold set to use the notifications it gives.
+        */
+       msleep(1);
+
+       if (tries > 5) {
+               tries = 0;
+               return RC_PENUP;
+       }
+
+       x = MODR;
+       if (x == last) {
+               tries++;
+               return RC_AGAIN;
+       }
+       last = x;
+       do {
+               if (reads)
+                       x = MODR;
+               y = MODR;
+               if (pressure)
+                       p = MODR;
+
+               /* are samples valid */
+               if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X ||
+                   (y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y ||
+                   (p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES)
+                       goto up;
+
+               /* coordinate is good */
+               tries = 0;
+               input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
+               input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
+               input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
+               input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
+               input_sync(wm->input_dev);
+               reads++;
+       } while (reads < cinfo[sp_idx].reads);
+up:
+       return RC_PENDOWN | RC_AGAIN;
+}
+
+static int wm97xx_acc_startup(struct wm97xx *wm)
+{
+       int idx;
+
+       /* check we have a codec */
+       if (wm->ac97 == NULL)
+               return -ENODEV;
+
+       /* Go you big red fire engine */
+       for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
+               if (wm->id != cinfo[idx].id)
+                       continue;
+               sp_idx = idx;
+               if (cont_rate <= cinfo[idx].speed)
+                       break;
+       }
+       wm->acc_rate = cinfo[sp_idx].code;
+       wm->acc_slot = ac97_touch_slot;
+       dev_info(wm->dev,
+                "zylonite accelerated touchscreen driver, %d samples/sec\n",
+                cinfo[sp_idx].speed);
+
+       return 0;
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+       if (enable)
+               enable_irq(wm->pen_irq);
+       else
+               disable_irq_nosync(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops zylonite_mach_ops = {
+       .acc_enabled    = 1,
+       .acc_pen_up     = wm97xx_acc_pen_up,
+       .acc_pen_down   = wm97xx_acc_pen_down,
+       .acc_startup    = wm97xx_acc_startup,
+       .irq_enable     = wm97xx_irq_enable,
+       .irq_gpio       = WM97XX_GPIO_2,
+};
+
+static int zylonite_wm97xx_probe(struct platform_device *pdev)
+{
+       struct wm97xx *wm = platform_get_drvdata(pdev);
+       int gpio_touch_irq;
+
+       if (cpu_is_pxa320())
+               gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15);
+       else
+               gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26);
+
+       wm->pen_irq = IRQ_GPIO(gpio_touch_irq);
+       set_irq_type(IRQ_GPIO(gpio_touch_irq), IRQ_TYPE_EDGE_BOTH);
+
+       wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
+                          WM97XX_GPIO_POL_HIGH,
+                          WM97XX_GPIO_STICKY,
+                          WM97XX_GPIO_WAKE);
+       wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT,
+                          WM97XX_GPIO_POL_HIGH,
+                          WM97XX_GPIO_NOTSTICKY,
+                          WM97XX_GPIO_NOWAKE);
+
+       return wm97xx_register_mach_ops(wm, &zylonite_mach_ops);
+}
+
+static int zylonite_wm97xx_remove(struct platform_device *pdev)
+{
+       struct wm97xx *wm = platform_get_drvdata(pdev);
+
+       wm97xx_unregister_mach_ops(wm);
+
+       return 0;
+}
+
+static struct platform_driver zylonite_wm97xx_driver = {
+       .probe  = zylonite_wm97xx_probe,
+       .remove = zylonite_wm97xx_remove,
+       .driver = {
+               .name   = "wm97xx-touch",
+       },
+};
+
+static int __init zylonite_wm97xx_init(void)
+{
+       return platform_driver_register(&zylonite_wm97xx_driver);
+}
+
+static void __exit zylonite_wm97xx_exit(void)
+{
+       platform_driver_unregister(&zylonite_wm97xx_driver);
+}
+
+module_init(zylonite_wm97xx_init);
+module_exit(zylonite_wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/rotary_encoder.h b/include/linux/rotary_encoder.h
new file mode 100644 (file)
index 0000000..12d63a3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __ROTARY_ENCODER_H__
+#define __ROTARY_ENCODER_H__
+
+struct rotary_encoder_platform_data {
+       unsigned int steps;
+       unsigned int axis;
+       unsigned int gpio_a;
+       unsigned int gpio_b;
+       unsigned int inverted_a;
+       unsigned int inverted_b;
+};
+
+#endif /* __ROTARY_ENCODER_H__ */
diff --git a/include/linux/spi/ad7879.h b/include/linux/spi/ad7879.h
new file mode 100644 (file)
index 0000000..4231104
--- /dev/null
@@ -0,0 +1,35 @@
+/* linux/spi/ad7879.h */
+
+/* Touchscreen characteristics vary between boards and models.  The
+ * platform_data for the device's "struct device" holds this information.
+ *
+ * It's OK if the min/max values are zero.
+ */
+struct ad7879_platform_data {
+       u16     model;                  /* 7879 */
+       u16     x_plate_ohms;
+       u16     x_min, x_max;
+       u16     y_min, y_max;
+       u16     pressure_min, pressure_max;
+
+       /* [0..255] 0=OFF Starts at 1=550us and goes
+        * all the way to 9.440ms in steps of 35us.
+        */
+       u8      pen_down_acc_interval;
+       /* [0..15] Starts at 0=128us and goes all the
+        * way to 4.096ms in steps of 128us.
+        */
+       u8      first_conversion_delay;
+       /* [0..3] 0 = 2us, 1 = 4us, 2 = 8us, 3 = 16us */
+       u8      acquisition_time;
+       /* [0..3] Average X middle samples 0 = 2, 1 = 4, 2 = 8, 3 = 16 */
+       u8      averaging;
+       /* [0..3] Perform X measurements 0 = OFF,
+        * 1 = 4, 2 = 8, 3 = 16 (median > averaging)
+        */
+       u8      median;
+       /* 1 = AUX/VBAT/GPIO set to GPIO Output */
+       u8      gpio_output;
+       /* Initial GPIO pin state (valid if gpio_output = 1) */
+       u8      gpio_default;
+};