Merge branch 'master' into for-linus
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 25 Apr 2008 03:37:52 +0000 (23:37 -0400)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 25 Apr 2008 03:37:52 +0000 (23:37 -0400)
49 files changed:
MAINTAINERS
drivers/char/keyboard.c
drivers/hid/usbhid/hid-quirks.c
drivers/input/input-polldev.c
drivers/input/joystick/Kconfig
drivers/input/joystick/Makefile
drivers/input/joystick/xpad.c
drivers/input/joystick/zhenhua.c [new file with mode: 0644]
drivers/input/keyboard/aaed2000_kbd.c
drivers/input/keyboard/bf54x-keys.c
drivers/input/keyboard/corgikbd.c
drivers/input/keyboard/gpio_keys.c
drivers/input/keyboard/jornada680_kbd.c
drivers/input/keyboard/jornada720_kbd.c
drivers/input/keyboard/locomokbd.c
drivers/input/keyboard/omap-keypad.c
drivers/input/keyboard/pxa27x_keypad.c
drivers/input/keyboard/spitzkbd.c
drivers/input/keyboard/tosakbd.c
drivers/input/misc/cobalt_btns.c
drivers/input/mouse/gpio_mouse.c
drivers/input/serio/Kconfig
drivers/input/serio/Makefile
drivers/input/serio/at32psif.c [new file with mode: 0644]
drivers/input/serio/i8042-x86ia64io.h
drivers/input/serio/rpckbd.c
drivers/input/tablet/Kconfig
drivers/input/tablet/aiptek.c
drivers/input/tablet/wacom.h
drivers/input/tablet/wacom_sys.c
drivers/input/tablet/wacom_wac.c
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/corgi_ts.c
drivers/input/touchscreen/jornada720_ts.c
drivers/input/touchscreen/mainstone-wm97xx.c [new file with mode: 0644]
drivers/input/touchscreen/ucb1400_ts.c
drivers/input/touchscreen/usbtouchscreen.c
drivers/input/touchscreen/wm9705.c [new file with mode: 0644]
drivers/input/touchscreen/wm9712.c [new file with mode: 0644]
drivers/input/touchscreen/wm9713.c [new file with mode: 0644]
drivers/input/touchscreen/wm97xx-core.c [new file with mode: 0644]
drivers/macintosh/mac_hid.c
include/linux/input.h
include/linux/keyboard.h
include/linux/serio.h
include/linux/spi/ads7846.h
include/linux/wm97xx.h [new file with mode: 0644]

index 90dcbbc..87bdced 100644 (file)
@@ -4343,6 +4343,16 @@ L:       linux-wireless@vger.kernel.org
 W:     http://oops.ghostprotocols.net:81/blog
 S:     Maintained
 
+WM97XX TOUCHSCREEN DRIVERS
+P:     Mark Brown
+M:     broonie@opensource.wolfsonmicro.com
+P:     Liam Girdwood
+M:     liam.girdwood@wolfsonmicro.com
+L:     linux-input@vger.kernel.org
+T:     git git://opensource.wolfsonmicro.com/linux-2.6-touch
+W:     http://opensource.wolfsonmicro.com/node/7
+S:     Supported
+
 X.25 NETWORK LAYER
 P:     Henner Eisen
 M:     eis@baty.hanse.de
index 4dbd342..45806d2 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/input.h>
 #include <linux/reboot.h>
 #include <linux/notifier.h>
+#include <linux/jiffies.h>
 
 extern void ctrl_alt_del(void);
 
@@ -928,7 +929,8 @@ static void k_brl(struct vc_data *vc, unsigned char value, char up_flag)
        if (up_flag) {
                if (brl_timeout) {
                        if (!committing ||
-                           jiffies - releasestart > (brl_timeout * HZ) / 1000) {
+                           time_after(jiffies,
+                                      releasestart + msecs_to_jiffies(brl_timeout))) {
                                committing = pressed;
                                releasestart = jiffies;
                        }
@@ -1237,6 +1239,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
        }
 
        param.shift = shift_final = (shift_state | kbd->slockstate) ^ kbd->lockstate;
+       param.ledstate = kbd->ledflagstate;
        key_map = key_maps[shift_final];
 
        if (atomic_notifier_call_chain(&keyboard_notifier_list, KBD_KEYCODE, &param) == NOTIFY_STOP || !key_map) {
@@ -1285,6 +1288,7 @@ static void kbd_keycode(unsigned int keycode, int down, int hw_raw)
 
        (*k_handler[type])(vc, keysym & 0xff, !down);
 
+       param.ledstate = kbd->ledflagstate;
        atomic_notifier_call_chain(&keyboard_notifier_list, KBD_POST_KEYSYM, &param);
 
        if (type != KT_SLOCK)
index e29a057..17b2f49 100644 (file)
 #define USB_VENDOR_ID_YEALINK          0x6993
 #define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K      0xb001
 
+#define USB_VENDOR_ID_KYE              0x0458
+#define USB_DEVICE_ID_KYE_GPEN_560     0x5003
+
 /*
  * Alphabetically sorted blacklist by quirk type.
  */
@@ -703,6 +706,7 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_63, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_64, HID_QUIRK_IGNORE },
        { USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR, USB_DEVICE_ID_N_S_HARMONY, HID_QUIRK_IGNORE },
+       { USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560, HID_QUIRK_IGNORE },
 
        { 0, 0 }
 };
index 490918a..0d3ce7a 100644 (file)
@@ -73,7 +73,7 @@ static void input_polled_device_work(struct work_struct *work)
 
 static int input_open_polled_device(struct input_dev *input)
 {
-       struct input_polled_dev *dev = input->private;
+       struct input_polled_dev *dev = input_get_drvdata(input);
        int error;
 
        error = input_polldev_start_workqueue();
@@ -91,7 +91,7 @@ static int input_open_polled_device(struct input_dev *input)
 
 static void input_close_polled_device(struct input_dev *input)
 {
-       struct input_polled_dev *dev = input->private;
+       struct input_polled_dev *dev = input_get_drvdata(input);
 
        cancel_delayed_work_sync(&dev->work);
        input_polldev_stop_workqueue();
@@ -151,10 +151,10 @@ int input_register_polled_device(struct input_polled_dev *dev)
 {
        struct input_dev *input = dev->input;
 
+       input_set_drvdata(input, dev);
        INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
        if (!dev->poll_interval)
                dev->poll_interval = 500;
-       input->private = dev;
        input->open = input_open_polled_device;
        input->close = input_close_polled_device;
 
index 7c662ee..be5c14a 100644 (file)
@@ -193,6 +193,18 @@ config JOYSTICK_TWIDJOY
          To compile this driver as a module, choose M here: the
          module will be called twidjoy.
 
+config JOYSTICK_ZHENHUA
+       tristate "5-byte Zhenhua RC transmitter"
+       select SERIO
+       help
+         Say Y here if you have a Zhen Hua PPM-4CH transmitter which is
+         supplied with a ready to fly micro electric indoor helicopters
+         such as EasyCopter, Lama, MiniCopter, DragonFly or Jabo and want
+         to use it via serial cable as a joystick.
+
+         To compile this driver as a module, choose M here: the
+         module will be called zhenhua.
+
 config JOYSTICK_DB9
        tristate "Multisystem, Sega Genesis, Saturn joysticks and gamepads"
        depends on PARPORT
index e855abb..fdbf8c4 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_JOYSTICK_GF2K)           += gf2k.o
 obj-$(CONFIG_JOYSTICK_GRIP)            += grip.o
 obj-$(CONFIG_JOYSTICK_GRIP_MP)         += grip_mp.o
 obj-$(CONFIG_JOYSTICK_GUILLEMOT)       += guillemot.o
+obj-$(CONFIG_JOYSTICK_IFORCE)          += iforce/
 obj-$(CONFIG_JOYSTICK_INTERACT)                += interact.o
 obj-$(CONFIG_JOYSTICK_JOYDUMP)         += joydump.o
 obj-$(CONFIG_JOYSTICK_MAGELLAN)                += magellan.o
@@ -27,5 +28,5 @@ obj-$(CONFIG_JOYSTICK_TURBOGRAFX)     += turbografx.o
 obj-$(CONFIG_JOYSTICK_TWIDJOY)         += twidjoy.o
 obj-$(CONFIG_JOYSTICK_WARRIOR)         += warrior.o
 obj-$(CONFIG_JOYSTICK_XPAD)            += xpad.o
+obj-$(CONFIG_JOYSTICK_ZHENHUA)         += zhenhua.o
 
-obj-$(CONFIG_JOYSTICK_IFORCE)          += iforce/
index 0380597..52ddb04 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * X-Box gamepad - v0.0.6
+ * X-Box gamepad driver
  *
  * Copyright (c) 2002 Marko Friedemann <mfr@bmx-chemnitz.de>
  *               2004 Oliver Schwartz <Oliver.Schwartz@gmx.de>,
@@ -68,6 +68,8 @@
  *  - dance pads will map D-PAD to buttons, not axes
  *  - pass the module paramater 'dpad_to_buttons' to force
  *    the D-PAD to map to buttons if your pad is not detected
+ *
+ * Later changes can be tracked in SCM.
  */
 
 #include <linux/kernel.h>
@@ -77,7 +79,6 @@
 #include <linux/module.h>
 #include <linux/usb/input.h>
 
-#define DRIVER_VERSION "v0.0.6"
 #define DRIVER_AUTHOR "Marko Friedemann <mfr@bmx-chemnitz.de>"
 #define DRIVER_DESC "X-Box pad driver"
 
    but we map them to axes when possible to simplify things */
 #define MAP_DPAD_TO_BUTTONS    0
 #define MAP_DPAD_TO_AXES       1
-#define MAP_DPAD_UNKNOWN       -1
+#define MAP_DPAD_UNKNOWN       2
 
 #define XTYPE_XBOX        0
 #define XTYPE_XBOX360     1
+#define XTYPE_XBOX360W    2
+#define XTYPE_UNKNOWN     3
 
 static int dpad_to_buttons;
 module_param(dpad_to_buttons, bool, S_IRUGO);
@@ -107,8 +110,10 @@ static const struct xpad_device {
        { 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x045e, 0x0287, "Microsoft Xbox Controller S", MAP_DPAD_TO_AXES, XTYPE_XBOX },
+       { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
        { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
+       { 0x046d, 0xc242, "Logitech Chillstream Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x046d, 0xca88, "Logitech Compact Controller for Xbox", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x05fd, 0x1007, "Mad Catz Controller (unverified)", MAP_DPAD_TO_AXES, XTYPE_XBOX },
@@ -135,18 +140,26 @@ static const struct xpad_device {
        { 0x0f30, 0x8888, "BigBen XBMiniPad Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
        { 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
+       { 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
        { 0x045e, 0x028e, "Microsoft X-Box 360 pad", MAP_DPAD_TO_AXES, XTYPE_XBOX360 },
        { 0xffff, 0xffff, "Chinese-made Xbox Controller", MAP_DPAD_TO_AXES, XTYPE_XBOX },
-       { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_XBOX }
+       { 0x0000, 0x0000, "Generic X-Box pad", MAP_DPAD_UNKNOWN, XTYPE_UNKNOWN }
 };
 
-static const signed short xpad_btn[] = {
-       BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z,       /* "analog" buttons */
+/* buttons shared with xbox and xbox360 */
+static const signed short xpad_common_btn[] = {
+       BTN_A, BTN_B, BTN_X, BTN_Y,                     /* "analog" buttons */
        BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR,    /* start/back/sticks */
        -1                                              /* terminating entry */
 };
 
+/* original xbox controllers only */
+static const signed short xpad_btn[] = {
+       BTN_C, BTN_Z,           /* "analog" buttons */
+       -1                      /* terminating entry */
+};
+
 /* only used if MAP_DPAD_TO_BUTTONS */
 static const signed short xpad_btn_pad[] = {
        BTN_LEFT, BTN_RIGHT,            /* d-pad left, right */
@@ -173,12 +186,27 @@ static const signed short xpad_abs_pad[] = {
        -1                      /* terminating entry */
 };
 
-/* Xbox 360 has a vendor-specific (sub)class, so we cannot match it with only
- * USB_INTERFACE_INFO, more to that this device has 4 InterfaceProtocols,
- * but we need only one of them. */
+/* Xbox 360 has a vendor-specific class, so we cannot match it with only
+ * USB_INTERFACE_INFO (also specifically refused by USB subsystem), so we
+ * match against vendor id as well. Wired Xbox 360 devices have protocol 1,
+ * wireless controllers have protocol 129. */
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
+       .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
+       .idVendor = (vend), \
+       .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
+       .bInterfaceSubClass = 93, \
+       .bInterfaceProtocol = (pr)
+#define XPAD_XBOX360_VENDOR(vend) \
+       { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
+       { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
+
 static struct usb_device_id xpad_table [] = {
        { USB_INTERFACE_INFO('X', 'B', 0) },    /* X-Box USB-IF not approved class */
-       { USB_DEVICE_INTERFACE_PROTOCOL(0x045e, 0x028e, 1) },   /* X-Box 360 controller */
+       XPAD_XBOX360_VENDOR(0x045e),            /* Microsoft X-Box 360 controllers */
+       XPAD_XBOX360_VENDOR(0x046d),            /* Logitech X-Box 360 style controllers */
+       XPAD_XBOX360_VENDOR(0x0738),            /* Mad Catz X-Box 360 controllers */
+       XPAD_XBOX360_VENDOR(0x0e6f),            /* 0x0e6f X-Box 360 controllers */
+       XPAD_XBOX360_VENDOR(0x1430),            /* RedOctane X-Box 360 controllers */
        { }
 };
 
@@ -188,10 +216,15 @@ struct usb_xpad {
        struct input_dev *dev;          /* input device interface */
        struct usb_device *udev;        /* usb device */
 
+       int pad_present;
+
        struct urb *irq_in;             /* urb for interrupt in report */
        unsigned char *idata;           /* input data */
        dma_addr_t idata_dma;
 
+       struct urb *bulk_out;
+       unsigned char *bdata;
+
 #if defined(CONFIG_JOYSTICK_XPAD_FF) || defined(CONFIG_JOYSTICK_XPAD_LEDS)
        struct urb *irq_out;            /* urb for interrupt out report */
        unsigned char *odata;           /* output data */
@@ -227,13 +260,13 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
        input_report_abs(dev, ABS_X,
                         (__s16) le16_to_cpup((__le16 *)(data + 12)));
        input_report_abs(dev, ABS_Y,
-                        (__s16) le16_to_cpup((__le16 *)(data + 14)));
+                        ~(__s16) le16_to_cpup((__le16 *)(data + 14)));
 
        /* right stick */
        input_report_abs(dev, ABS_RX,
                         (__s16) le16_to_cpup((__le16 *)(data + 16)));
        input_report_abs(dev, ABS_RY,
-                        (__s16) le16_to_cpup((__le16 *)(data + 18)));
+                        ~(__s16) le16_to_cpup((__le16 *)(data + 18)));
 
        /* triggers left/right */
        input_report_abs(dev, ABS_Z, data[10]);
@@ -321,13 +354,13 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
        input_report_abs(dev, ABS_X,
                         (__s16) le16_to_cpup((__le16 *)(data + 6)));
        input_report_abs(dev, ABS_Y,
-                        (__s16) le16_to_cpup((__le16 *)(data + 8)));
+                        ~(__s16) le16_to_cpup((__le16 *)(data + 8)));
 
        /* right stick */
        input_report_abs(dev, ABS_RX,
                         (__s16) le16_to_cpup((__le16 *)(data + 10)));
        input_report_abs(dev, ABS_RY,
-                        (__s16) le16_to_cpup((__le16 *)(data + 12)));
+                        ~(__s16) le16_to_cpup((__le16 *)(data + 12)));
 
        /* triggers left/right */
        input_report_abs(dev, ABS_Z, data[4]);
@@ -336,6 +369,39 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
        input_sync(dev);
 }
 
+/*
+ * xpad360w_process_packet
+ *
+ * Completes a request by converting the data into events for the
+ * input subsystem. It is version for xbox 360 wireless controller.
+ *
+ * Byte.Bit
+ * 00.1 - Status change: The controller or headset has connected/disconnected
+ *                       Bits 01.7 and 01.6 are valid
+ * 01.7 - Controller present
+ * 01.6 - Headset present
+ * 01.1 - Pad state (Bytes 4+) valid
+ *
+ */
+
+static void xpad360w_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *data)
+{
+       /* Presence change */
+       if (data[0] & 0x08) {
+               if (data[1] & 0x80) {
+                       xpad->pad_present = 1;
+                       usb_submit_urb(xpad->bulk_out, GFP_ATOMIC);
+               } else
+                       xpad->pad_present = 0;
+       }
+
+       /* Valid pad data */
+       if (!(data[1] & 0x1))
+               return;
+
+       xpad360_process_packet(xpad, cmd, &data[4]);
+}
+
 static void xpad_irq_in(struct urb *urb)
 {
        struct usb_xpad *xpad = urb->context;
@@ -358,10 +424,16 @@ static void xpad_irq_in(struct urb *urb)
                goto exit;
        }
 
-       if (xpad->xtype == XTYPE_XBOX360)
+       switch (xpad->xtype) {
+       case XTYPE_XBOX360:
                xpad360_process_packet(xpad, 0, xpad->idata);
-       else
+               break;
+       case XTYPE_XBOX360W:
+               xpad360w_process_packet(xpad, 0, xpad->idata);
+               break;
+       default:
                xpad_process_packet(xpad, 0, xpad->idata);
+       }
 
 exit:
        retval = usb_submit_urb (urb, GFP_ATOMIC);
@@ -399,6 +471,23 @@ exit:
                    __FUNCTION__, retval);
 }
 
+static void xpad_bulk_out(struct urb *urb)
+{
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated, clean up */
+               dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+               break;
+       default:
+               dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+       }
+}
+
 static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
 {
        struct usb_endpoint_descriptor *ep_irq_out;
@@ -408,7 +497,7 @@ static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
                return 0;
 
        xpad->odata = usb_buffer_alloc(xpad->udev, XPAD_PKT_LEN,
-                                      GFP_ATOMIC, &xpad->odata_dma );
+                                      GFP_KERNEL, &xpad->odata_dma);
        if (!xpad->odata)
                goto fail1;
 
@@ -469,6 +558,7 @@ static int xpad_play_effect(struct input_dev *dev, void *data,
                xpad->odata[5] = 0x00;
                xpad->odata[6] = 0x00;
                xpad->odata[7] = 0x00;
+               xpad->irq_out->transfer_buffer_length = 8;
                usb_submit_urb(xpad->irq_out, GFP_KERNEL);
        }
 
@@ -477,6 +567,9 @@ static int xpad_play_effect(struct input_dev *dev, void *data,
 
 static int xpad_init_ff(struct usb_xpad *xpad)
 {
+       if (xpad->xtype != XTYPE_XBOX360)
+               return 0;
+
        input_set_capability(xpad->dev, EV_FF, FF_RUMBLE);
 
        return input_ff_create_memless(xpad->dev, NULL, xpad_play_effect);
@@ -502,6 +595,7 @@ static void xpad_send_led_command(struct usb_xpad *xpad, int command)
                xpad->odata[0] = 0x01;
                xpad->odata[1] = 0x03;
                xpad->odata[2] = command;
+               xpad->irq_out->transfer_buffer_length = 3;
                usb_submit_urb(xpad->irq_out, GFP_KERNEL);
                mutex_unlock(&xpad->odata_mutex);
        }
@@ -574,6 +668,10 @@ static int xpad_open(struct input_dev *dev)
 {
        struct usb_xpad *xpad = input_get_drvdata(dev);
 
+       /* URB was submitted in probe */
+       if(xpad->xtype == XTYPE_XBOX360W)
+               return 0;
+
        xpad->irq_in->dev = xpad->udev;
        if (usb_submit_urb(xpad->irq_in, GFP_KERNEL))
                return -EIO;
@@ -585,7 +683,8 @@ static void xpad_close(struct input_dev *dev)
 {
        struct usb_xpad *xpad = input_get_drvdata(dev);
 
-       usb_kill_urb(xpad->irq_in);
+       if(xpad->xtype != XTYPE_XBOX360W)
+               usb_kill_urb(xpad->irq_in);
        xpad_stop_output(xpad);
 }
 
@@ -632,7 +731,7 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
                goto fail1;
 
        xpad->idata = usb_buffer_alloc(udev, XPAD_PKT_LEN,
-                                      GFP_ATOMIC, &xpad->idata_dma);
+                                      GFP_KERNEL, &xpad->idata_dma);
        if (!xpad->idata)
                goto fail1;
 
@@ -644,7 +743,16 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
        xpad->dpad_mapping = xpad_device[i].dpad_mapping;
        xpad->xtype = xpad_device[i].xtype;
        if (xpad->dpad_mapping == MAP_DPAD_UNKNOWN)
-               xpad->dpad_mapping = dpad_to_buttons;
+               xpad->dpad_mapping = !dpad_to_buttons;
+       if (xpad->xtype == XTYPE_UNKNOWN) {
+               if (intf->cur_altsetting->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC) {
+                       if (intf->cur_altsetting->desc.bInterfaceProtocol == 129)
+                               xpad->xtype = XTYPE_XBOX360W;
+                       else
+                               xpad->xtype = XTYPE_XBOX360;
+               } else
+                       xpad->xtype = XTYPE_XBOX;
+       }
        xpad->dev = input_dev;
        usb_make_path(udev, xpad->phys, sizeof(xpad->phys));
        strlcat(xpad->phys, "/input0", sizeof(xpad->phys));
@@ -662,11 +770,14 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
 
        /* set up buttons */
-       for (i = 0; xpad_btn[i] >= 0; i++)
-               set_bit(xpad_btn[i], input_dev->keybit);
-       if (xpad->xtype == XTYPE_XBOX360)
+       for (i = 0; xpad_common_btn[i] >= 0; i++)
+               set_bit(xpad_common_btn[i], input_dev->keybit);
+       if ((xpad->xtype == XTYPE_XBOX360) || (xpad->xtype == XTYPE_XBOX360W))
                for (i = 0; xpad360_btn[i] >= 0; i++)
                        set_bit(xpad360_btn[i], input_dev->keybit);
+       else
+               for (i = 0; xpad_btn[i] >= 0; i++)
+                       set_bit(xpad_btn[i], input_dev->keybit);
        if (xpad->dpad_mapping == MAP_DPAD_TO_BUTTONS)
                for (i = 0; xpad_btn_pad[i] >= 0; i++)
                        set_bit(xpad_btn_pad[i], input_dev->keybit);
@@ -703,8 +814,57 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
                goto fail4;
 
        usb_set_intfdata(intf, xpad);
+
+       /*
+        * Submit the int URB immediatly rather than waiting for open
+        * because we get status messages from the device whether
+        * or not any controllers are attached.  In fact, it's
+        * exactly the message that a controller has arrived that
+        * we're waiting for.
+        */
+       if (xpad->xtype == XTYPE_XBOX360W) {
+               xpad->irq_in->dev = xpad->udev;
+               error = usb_submit_urb(xpad->irq_in, GFP_KERNEL);
+               if (error)
+                       goto fail4;
+
+               /*
+                * Setup the message to set the LEDs on the
+                * controller when it shows up
+                */
+               xpad->bulk_out = usb_alloc_urb(0, GFP_KERNEL);
+               if(!xpad->bulk_out)
+                       goto fail5;
+
+               xpad->bdata = kzalloc(XPAD_PKT_LEN, GFP_KERNEL);
+               if(!xpad->bdata)
+                       goto fail6;
+
+               xpad->bdata[2] = 0x08;
+               switch (intf->cur_altsetting->desc.bInterfaceNumber) {
+               case 0:
+                       xpad->bdata[3] = 0x42;
+                       break;
+               case 2:
+                       xpad->bdata[3] = 0x43;
+                       break;
+               case 4:
+                       xpad->bdata[3] = 0x44;
+                       break;
+               case 6:
+                       xpad->bdata[3] = 0x45;
+               }
+
+               ep_irq_in = &intf->cur_altsetting->endpoint[1].desc;
+               usb_fill_bulk_urb(xpad->bulk_out, udev,
+                               usb_sndbulkpipe(udev, ep_irq_in->bEndpointAddress),
+                               xpad->bdata, XPAD_PKT_LEN, xpad_bulk_out, xpad);
+       }
+
        return 0;
 
+ fail6:        usb_free_urb(xpad->bulk_out);
+ fail5:        usb_kill_urb(xpad->irq_in);
  fail4:        usb_free_urb(xpad->irq_in);
  fail3:        xpad_deinit_output(xpad);
  fail2:        usb_buffer_free(udev, XPAD_PKT_LEN, xpad->idata, xpad->idata_dma);
@@ -723,6 +883,11 @@ static void xpad_disconnect(struct usb_interface *intf)
                xpad_led_disconnect(xpad);
                input_unregister_device(xpad->dev);
                xpad_deinit_output(xpad);
+               if (xpad->xtype == XTYPE_XBOX360W) {
+                       usb_kill_urb(xpad->bulk_out);
+                       usb_free_urb(xpad->bulk_out);
+                       usb_kill_urb(xpad->irq_in);
+               }
                usb_free_urb(xpad->irq_in);
                usb_buffer_free(xpad->udev, XPAD_PKT_LEN,
                                xpad->idata, xpad->idata_dma);
@@ -741,7 +906,7 @@ static int __init usb_xpad_init(void)
 {
        int result = usb_register(&xpad_driver);
        if (result == 0)
-               info(DRIVER_DESC ":" DRIVER_VERSION);
+               info(DRIVER_DESC);
        return result;
 }
 
diff --git a/drivers/input/joystick/zhenhua.c b/drivers/input/joystick/zhenhua.c
new file mode 100644 (file)
index 0000000..b585312
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ *  derived from "twidjoy.c"
+ *
+ *  Copyright (c) 2008 Martin Kebert
+ *  Copyright (c) 2001 Arndt Schoenewald
+ *  Copyright (c) 2000-2001 Vojtech Pavlik
+ *  Copyright (c) 2000 Mark Fletcher
+ *
+ */
+
+/*
+ * Driver to use 4CH RC transmitter using Zhen Hua 5-byte protocol (Walkera Lama,
+ * EasyCopter etc.) as a joystick under Linux.
+ *
+ * RC transmitters using Zhen Hua 5-byte protocol are cheap four channels
+ * transmitters for control a RC planes or RC helicopters with possibility to
+ * connect on a serial port.
+ * Data coming from transmitter is in this order:
+ * 1. byte = synchronisation byte
+ * 2. byte = X axis
+ * 3. byte = Y axis
+ * 4. byte = RZ axis
+ * 5. byte = Z axis
+ * (and this is repeated)
+ *
+ * For questions or feedback regarding this driver module please contact:
+ * Martin Kebert <gkmarty@gmail.com> - but I am not a C-programmer nor kernel
+ * coder :-(
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC    "RC transmitter with 5-byte Zhen Hua protocol joystick driver"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Constants.
+ */
+
+#define ZHENHUA_MAX_LENGTH 5
+
+/*
+ * Zhen Hua data.
+ */
+
+struct zhenhua {
+       struct input_dev *dev;
+       int idx;
+       unsigned char data[ZHENHUA_MAX_LENGTH];
+       char phys[32];
+};
+
+
+/* bits in all incoming bytes needs to be "reversed" */
+static int zhenhua_bitreverse(int x)
+{
+       x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1);
+       x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2);
+       x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4);
+       return x;
+}
+
+/*
+ * zhenhua_process_packet() decodes packets the driver receives from the
+ * RC transmitter. It updates the data accordingly.
+ */
+
+static void zhenhua_process_packet(struct zhenhua *zhenhua)
+{
+       struct input_dev *dev = zhenhua->dev;
+       unsigned char *data = zhenhua->data;
+
+       input_report_abs(dev, ABS_Y, data[1]);
+       input_report_abs(dev, ABS_X, data[2]);
+       input_report_abs(dev, ABS_RZ, data[3]);
+       input_report_abs(dev, ABS_Z, data[4]);
+
+       input_sync(dev);
+}
+
+/*
+ * zhenhua_interrupt() is called by the low level driver when characters
+ * are ready for us. We then buffer them for further processing, or call the
+ * packet processing routine.
+ */
+
+static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, unsigned int flags)
+{
+       struct zhenhua *zhenhua = serio_get_drvdata(serio);
+
+       /* All Zhen Hua packets are 5 bytes. The fact that the first byte
+        * is allways 0xf7 and all others are in range 0x32 - 0xc8 (50-200)
+        * can be used to check and regain sync. */
+
+       if (data == 0xef)
+               zhenhua->idx = 0;       /* this byte starts a new packet */
+       else if (zhenhua->idx == 0)
+               return IRQ_HANDLED;     /* wrong MSB -- ignore this byte */
+
+       if (zhenhua->idx < ZHENHUA_MAX_LENGTH)
+               zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data);
+
+       if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {
+               zhenhua_process_packet(zhenhua);
+               zhenhua->idx = 0;
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * zhenhua_disconnect() is the opposite of zhenhua_connect()
+ */
+
+static void zhenhua_disconnect(struct serio *serio)
+{
+       struct zhenhua *zhenhua = serio_get_drvdata(serio);
+
+       serio_close(serio);
+       serio_set_drvdata(serio, NULL);
+       input_unregister_device(zhenhua->dev);
+       kfree(zhenhua);
+}
+
+/*
+ * zhenhua_connect() is the routine that is called when someone adds a
+ * new serio device. It looks for the Twiddler, and if found, registers
+ * it as an input device.
+ */
+
+static int zhenhua_connect(struct serio *serio, struct serio_driver *drv)
+{
+       struct zhenhua *zhenhua;
+       struct input_dev *input_dev;
+       int err = -ENOMEM;
+
+       zhenhua = kzalloc(sizeof(struct zhenhua), GFP_KERNEL);
+       input_dev = input_allocate_device();
+       if (!zhenhua || !input_dev)
+               goto fail1;
+
+       zhenhua->dev = input_dev;
+       snprintf(zhenhua->phys, sizeof(zhenhua->phys), "%s/input0", serio->phys);
+
+       input_dev->name = "Zhen Hua 5-byte device";
+       input_dev->phys = zhenhua->phys;
+       input_dev->id.bustype = BUS_RS232;
+       input_dev->id.vendor = SERIO_ZHENHUA;
+       input_dev->id.product = 0x0001;
+       input_dev->id.version = 0x0100;
+       input_dev->dev.parent = &serio->dev;
+
+       input_dev->evbit[0] = BIT(EV_ABS);
+       input_set_abs_params(input_dev, ABS_X, 50, 200, 0, 0);
+       input_set_abs_params(input_dev, ABS_Y, 50, 200, 0, 0);
+       input_set_abs_params(input_dev, ABS_Z, 50, 200, 0, 0);
+       input_set_abs_params(input_dev, ABS_RZ, 50, 200, 0, 0);
+
+       serio_set_drvdata(serio, zhenhua);
+
+       err = serio_open(serio, drv);
+       if (err)
+               goto fail2;
+
+       err = input_register_device(zhenhua->dev);
+       if (err)
+               goto fail3;
+
+       return 0;
+
+ fail3:        serio_close(serio);
+ fail2:        serio_set_drvdata(serio, NULL);
+ fail1:        input_free_device(input_dev);
+       kfree(zhenhua);
+       return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id zhenhua_serio_ids[] = {
+       {
+               .type   = SERIO_RS232,
+               .proto  = SERIO_ZHENHUA,
+               .id     = SERIO_ANY,
+               .extra  = SERIO_ANY,
+       },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, zhenhua_serio_ids);
+
+static struct serio_driver zhenhua_drv = {
+       .driver         = {
+               .name   = "zhenhua",
+       },
+       .description    = DRIVER_DESC,
+       .id_table       = zhenhua_serio_ids,
+       .interrupt      = zhenhua_interrupt,
+       .connect        = zhenhua_connect,
+       .disconnect     = zhenhua_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init zhenhua_init(void)
+{
+       return serio_register_driver(&zhenhua_drv);
+}
+
+static void __exit zhenhua_exit(void)
+{
+       serio_unregister_driver(&zhenhua_drv);
+}
+
+module_init(zhenhua_init);
+module_exit(zhenhua_exit);
index 72abc19..a293e8b 100644 (file)
@@ -156,11 +156,15 @@ static int __devexit aaedkbd_remove(struct platform_device *pdev)
        return 0;
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:aaed2000-keyboard");
+
 static struct platform_driver aaedkbd_driver = {
        .probe          = aaedkbd_probe,
        .remove         = __devexit_p(aaedkbd_remove),
        .driver         = {
                .name   = "aaed2000-keyboard",
+               .owner  = THIS_MODULE,
        },
 };
 
index 05e3494..54ed8e2 100644 (file)
@@ -312,6 +312,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
 
        bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
 
+       device_init_wakeup(&pdev->dev, 1);
+
        printk(KERN_ERR DRV_NAME
                ": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq);
 
@@ -354,12 +356,40 @@ static int __devexit bfin_kpad_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(&pdev->dev))
+               enable_irq_wake(bf54x_kpad->irq);
+
+       return 0;
+}
+
+static int bfin_kpad_resume(struct platform_device *pdev)
+{
+       struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+       if (device_may_wakeup(&pdev->dev))
+               disable_irq_wake(bf54x_kpad->irq);
+
+       return 0;
+}
+#else
+# define bfin_kpad_suspend NULL
+# define bfin_kpad_resume  NULL
+#endif
+
 struct platform_driver bfin_kpad_device_driver = {
-       .probe          = bfin_kpad_probe,
-       .remove         = __devexit_p(bfin_kpad_remove),
        .driver         = {
                .name   = DRV_NAME,
-       }
+               .owner  = THIS_MODULE,
+       },
+       .probe          = bfin_kpad_probe,
+       .remove         = __devexit_p(bfin_kpad_remove),
+       .suspend        = bfin_kpad_suspend,
+       .resume         = bfin_kpad_resume,
 };
 
 static int __init bfin_kpad_init(void)
@@ -378,3 +408,4 @@ module_exit(bfin_kpad_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
+MODULE_ALIAS("platform:bf54x-keys");
index 790fed3..5187c0c 100644 (file)
@@ -392,6 +392,7 @@ static struct platform_driver corgikbd_driver = {
        .resume         = corgikbd_resume,
        .driver         = {
                .name   = "corgi-keyboard",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -411,3 +412,4 @@ module_exit(corgikbd_exit);
 MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
 MODULE_DESCRIPTION("Corgi Keyboard Driver");
 MODULE_LICENSE("GPLv2");
+MODULE_ALIAS("platform:corgi-keyboard");
index 6a9ca4b..bbd00c3 100644 (file)
@@ -43,10 +43,11 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
 
                        input_event(input, type, button->code, !!state);
                        input_sync(input);
+                       return IRQ_HANDLED;
                }
        }
 
-       return IRQ_HANDLED;
+       return IRQ_NONE;
 }
 
 static int __devinit gpio_keys_probe(struct platform_device *pdev)
@@ -213,6 +214,7 @@ struct platform_driver gpio_keys_device_driver = {
        .resume         = gpio_keys_resume,
        .driver         = {
                .name   = "gpio-keys",
+               .owner  = THIS_MODULE,
        }
 };
 
@@ -232,3 +234,4 @@ module_exit(gpio_keys_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
 MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
+MODULE_ALIAS("platform:gpio-keys");
index a23633a..9387da3 100644 (file)
@@ -254,6 +254,7 @@ static int __devexit jornada680kbd_remove(struct platform_device *pdev)
 static struct platform_driver jornada680kbd_driver = {
        .driver = {
                .name   = "jornada680_kbd",
+               .owner  = THIS_MODULE,
        },
        .probe  = jornada680kbd_probe,
        .remove = __devexit_p(jornada680kbd_remove),
@@ -275,3 +276,4 @@ module_exit(jornada680kbd_exit);
 MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
 MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver");
 MODULE_LICENSE("GPLv2");
+MODULE_ALIAS("platform:jornada680_kbd");
index 986f93c..a1164a0 100644 (file)
@@ -162,9 +162,13 @@ static int __devexit jornada720_kbd_remove(struct platform_device *pdev)
        return 0;
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada720_kbd");
+
 static struct platform_driver jornada720_kbd_driver = {
        .driver  = {
                .name    = "jornada720_kbd",
+               .owner  = THIS_MODULE,
         },
        .probe   = jornada720_kbd_probe,
        .remove  = __devexit_p(jornada720_kbd_remove),
index 5a0ca18..9caed30 100644 (file)
@@ -1,14 +1,12 @@
 /*
- *  Copyright (c) 2005 John Lenz
+ * LoCoMo keyboard driver for Linux-based ARM PDAs:
+ *     - SHARP Zaurus Collie (SL-5500)
+ *     - SHARP Zaurus Poodle (SL-5600)
  *
+ * Copyright (c) 2005 John Lenz
  * Based on from xtkbd.c
- */
-
-/*
- * LoCoMo keyboard driver for Linux/ARM
- */
-
-/*
+ *
+ *
  * 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
@@ -47,7 +45,8 @@ MODULE_LICENSE("GPL");
 #define KEY_CONTACT            KEY_F18
 #define KEY_CENTER             KEY_F15
 
-static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
+static const unsigned char
+locomokbd_keycode[LOCOMOKBD_NUMKEYS] __devinitconst = {
        0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0,                          /* 0 - 9 */
        0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT,                   /* 10 - 19 */
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                                           /* 20 - 29 */
@@ -67,22 +66,21 @@ static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
 #define KB_COLS                        8
 #define KB_ROWMASK(r)          (1 << (r))
 #define SCANCODE(c,r)          ( ((c)<<4) + (r) + 1 )
-#define        NR_SCANCODES            128
 
 #define KB_DELAY               8
 #define SCAN_INTERVAL          (HZ/10)
-#define LOCOMOKBD_PRESSED      1
 
 struct locomokbd {
        unsigned char keycode[LOCOMOKBD_NUMKEYS];
        struct input_dev *input;
        char phys[32];
 
-       struct locomo_dev *ldev;
        unsigned long base;
        spinlock_t lock;
 
        struct timer_list timer;
+       unsigned long suspend_jiffies;
+       unsigned int count_cancel;
 };
 
 /* helper functions for reading the keyboard matrix */
@@ -128,7 +126,7 @@ static inline void locomokbd_reset_col(unsigned long membase, int col)
 /* Scan the hardware keyboard and push any changes up through the input layer */
 static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
 {
-       unsigned int row, col, rowd, scancode;
+       unsigned int row, col, rowd;
        unsigned long flags;
        unsigned int num_pressed;
        unsigned long membase = locomokbd->base;
@@ -145,13 +143,33 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
 
                rowd = ~locomo_readl(membase + LOCOMO_KIB);
                for (row = 0; row < KB_ROWS; row++) {
+                       unsigned int scancode, pressed, key;
+
                        scancode = SCANCODE(col, row);
-                       if (rowd & KB_ROWMASK(row)) {
-                               num_pressed += 1;
-                               input_report_key(locomokbd->input, locomokbd->keycode[scancode], 1);
-                       } else {
-                               input_report_key(locomokbd->input, locomokbd->keycode[scancode], 0);
-                       }
+                       pressed = rowd & KB_ROWMASK(row);
+                       key = locomokbd->keycode[scancode];
+
+                       input_report_key(locomokbd->input, key, pressed);
+                       if (likely(!pressed))
+                               continue;
+
+                       num_pressed++;
+
+                       /* The "Cancel/ESC" key is labeled "On/Off" on
+                        * Collie and Poodle and should suspend the device
+                        * if it was pressed for more than a second. */
+                       if (unlikely(key == KEY_ESC)) {
+                               if (!time_after(jiffies,
+                                       locomokbd->suspend_jiffies + HZ))
+                                       continue;
+                               if (locomokbd->count_cancel++
+                                       != (HZ/SCAN_INTERVAL + 1))
+                                       continue;
+                               input_event(locomokbd->input, EV_PWR,
+                                       KEY_SUSPEND, 1);
+                               locomokbd->suspend_jiffies = jiffies;
+                       } else
+                               locomokbd->count_cancel = 0;
                }
                locomokbd_reset_col(membase, col);
        }
@@ -162,6 +180,8 @@ static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
        /* if any keys are pressed, enable the timer */
        if (num_pressed)
                mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
+       else
+               locomokbd->count_cancel = 0;
 
        spin_unlock_irqrestore(&locomokbd->lock, flags);
 }
@@ -186,10 +206,11 @@ static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
 static void locomokbd_timer_callback(unsigned long data)
 {
        struct locomokbd *locomokbd = (struct locomokbd *) data;
+
        locomokbd_scankeyboard(locomokbd);
 }
 
-static int locomokbd_probe(struct locomo_dev *dev)
+static int __devinit locomokbd_probe(struct locomo_dev *dev)
 {
        struct locomokbd *locomokbd;
        struct input_dev *input_dev;
@@ -211,7 +232,6 @@ static int locomokbd_probe(struct locomo_dev *dev)
                goto err_free_mem;
        }
 
-       locomokbd->ldev = dev;
        locomo_set_drvdata(dev, locomokbd);
 
        locomokbd->base = (unsigned long) dev->mapbase;
@@ -222,6 +242,8 @@ static int locomokbd_probe(struct locomo_dev *dev)
        locomokbd->timer.function = locomokbd_timer_callback;
        locomokbd->timer.data = (unsigned long) locomokbd;
 
+       locomokbd->suspend_jiffies = jiffies;
+
        locomokbd->input = input_dev;
        strcpy(locomokbd->phys, "locomokbd/input0");
 
@@ -233,9 +255,10 @@ static int locomokbd_probe(struct locomo_dev *dev)
        input_dev->id.version = 0x0100;
        input_dev->dev.parent = &dev->dev;
 
-       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+       input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+                               BIT_MASK(EV_PWR);
        input_dev->keycode = locomokbd->keycode;
-       input_dev->keycodesize = sizeof(unsigned char);
+       input_dev->keycodesize = sizeof(locomokbd_keycode[0]);
        input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode);
 
        memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
@@ -268,7 +291,7 @@ static int locomokbd_probe(struct locomo_dev *dev)
        return err;
 }
 
-static int locomokbd_remove(struct locomo_dev *dev)
+static int __devexit locomokbd_remove(struct locomo_dev *dev)
 {
        struct locomokbd *locomokbd = locomo_get_drvdata(dev);
 
@@ -292,7 +315,7 @@ static struct locomo_driver keyboard_driver = {
        },
        .devid  = LOCOMO_DEVID_KEYBOARD,
        .probe  = locomokbd_probe,
-       .remove = locomokbd_remove,
+       .remove = __devexit_p(locomokbd_remove),
 };
 
 static int __init locomokbd_init(void)
index babc913..10afd20 100644 (file)
@@ -352,6 +352,9 @@ static int __init omap_kp_probe(struct platform_device *pdev)
                        }
                        omap_set_gpio_direction(row_gpios[row_idx], 1);
                }
+       } else {
+               col_idx = 0;
+               row_idx = 0;
        }
 
        setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
@@ -415,10 +418,10 @@ err4:
 err3:
        device_remove_file(&pdev->dev, &dev_attr_enable);
 err2:
-       for (i = row_idx-1; i >=0; i--)
+       for (i = row_idx - 1; i >=0; i--)
                omap_free_gpio(row_gpios[i]);
 err1:
-       for (i = col_idx-1; i >=0; i--)
+       for (i = col_idx - 1; i >=0; i--)
                omap_free_gpio(col_gpios[i]);
 
        kfree(omap_kp);
@@ -464,6 +467,7 @@ static struct platform_driver omap_kp_driver = {
        .resume         = omap_kp_resume,
        .driver         = {
                .name   = "omap-keypad",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -484,3 +488,4 @@ module_exit(omap_kp_exit);
 MODULE_AUTHOR("Timo Teräs");
 MODULE_DESCRIPTION("OMAP Keypad Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-keypad");
index 4e651c1..3dea0c5 100644 (file)
@@ -545,6 +545,9 @@ static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
        return 0;
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:pxa27x-keypad");
+
 static struct platform_driver pxa27x_keypad_driver = {
        .probe          = pxa27x_keypad_probe,
        .remove         = __devexit_p(pxa27x_keypad_remove),
@@ -552,6 +555,7 @@ static struct platform_driver pxa27x_keypad_driver = {
        .resume         = pxa27x_keypad_resume,
        .driver         = {
                .name   = "pxa27x-keypad",
+               .owner  = THIS_MODULE,
        },
 };
 
index 1d59a2d..92102f9 100644 (file)
@@ -494,3 +494,4 @@ module_exit(spitzkbd_exit);
 MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
 MODULE_DESCRIPTION("Spitz Keyboard Driver");
 MODULE_LICENSE("GPLv2");
+MODULE_ALIAS("platform:spitz-keyboard");
index 3884d1e..94e444b 100644 (file)
@@ -52,7 +52,7 @@ KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_
 struct tosakbd {
        unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)];
        struct input_dev *input;
-
+       int suspended;
        spinlock_t lock; /* protect kbd scanning */
        struct timer_list timer;
 };
@@ -133,6 +133,9 @@ static void tosakbd_scankeyboard(struct platform_device *dev)
 
        spin_lock_irqsave(&tosakbd->lock, flags);
 
+       if (tosakbd->suspended)
+               goto out;
+
        for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) {
                /*
                 * Discharge the output driver capacitatance
@@ -174,6 +177,7 @@ static void tosakbd_scankeyboard(struct platform_device *dev)
        if (num_pressed)
                mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL);
 
+ out:
        spin_unlock_irqrestore(&tosakbd->lock, flags);
 }
 
@@ -200,6 +204,7 @@ static irqreturn_t tosakbd_interrupt(int irq, void *__dev)
 static void tosakbd_timer_callback(unsigned long __dev)
 {
        struct platform_device *dev = (struct platform_device *)__dev;
+
        tosakbd_scankeyboard(dev);
 }
 
@@ -207,6 +212,13 @@ static void tosakbd_timer_callback(unsigned long __dev)
 static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
 {
        struct tosakbd *tosakbd = platform_get_drvdata(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&tosakbd->lock, flags);
+       PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT);
+       PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT);
+       tosakbd->suspended = 1;
+       spin_unlock_irqrestore(&tosakbd->lock, flags);
 
        del_timer_sync(&tosakbd->timer);
 
@@ -215,6 +227,9 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
 
 static int tosakbd_resume(struct platform_device *dev)
 {
+       struct tosakbd *tosakbd = platform_get_drvdata(dev);
+
+       tosakbd->suspended = 0;
        tosakbd_scankeyboard(dev);
 
        return 0;
@@ -365,8 +380,8 @@ fail:
        return error;
 }
 
-static int __devexit tosakbd_remove(struct platform_device *dev) {
-
+static int __devexit tosakbd_remove(struct platform_device *dev)
+{
        int i;
        struct tosakbd *tosakbd = platform_get_drvdata(dev);
 
@@ -394,6 +409,7 @@ static struct platform_driver tosakbd_driver = {
        .resume         = tosakbd_resume,
        .driver         = {
                .name   = "tosa-keyboard",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -413,3 +429,4 @@ module_exit(tosakbd_exit);
 MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
 MODULE_DESCRIPTION("Tosa Keyboard Driver");
 MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tosa-keyboard");
index 5511ef0..6a1f48b 100644 (file)
@@ -148,6 +148,9 @@ static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
        return 0;
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:Cobalt buttons");
+
 static struct platform_driver cobalt_buttons_driver = {
        .probe  = cobalt_buttons_probe,
        .remove = __devexit_p(cobalt_buttons_remove),
index 0936d6b..3392901 100644 (file)
@@ -171,10 +171,14 @@ static int __devexit gpio_mouse_remove(struct platform_device *pdev)
        return 0;
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:gpio_mouse");
+
 struct platform_driver gpio_mouse_device_driver = {
        .remove         = __devexit_p(gpio_mouse_remove),
        .driver         = {
                .name   = "gpio_mouse",
+               .owner  = THIS_MODULE,
        }
 };
 
index b88569e..ec4b661 100644 (file)
@@ -88,6 +88,16 @@ config SERIO_RPCKBD
          To compile this driver as a module, choose M here: the
          module will be called rpckbd.
 
+config SERIO_AT32PSIF
+       tristate "AVR32 PSIF PS/2 keyboard and mouse controller"
+       depends on AVR32
+       help
+         Say Y here if you want to use the PSIF peripheral on AVR32 devices
+         and connect a PS/2 keyboard and/or mouse to it.
+
+         To compile this driver as a module, choose M here: the module will
+         be called at32psif.
+
 config SERIO_AMBAKMI
        tristate "AMBA KMI keyboard controller"
        depends on ARM_AMBA
index 4155197..38b8868 100644 (file)
@@ -12,6 +12,7 @@ obj-$(CONFIG_SERIO_CT82C710)  += ct82c710.o
 obj-$(CONFIG_SERIO_RPCKBD)     += rpckbd.o
 obj-$(CONFIG_SERIO_SA1111)     += sa1111ps2.o
 obj-$(CONFIG_SERIO_AMBAKMI)    += ambakmi.o
+obj-$(CONFIG_SERIO_AT32PSIF)   += at32psif.o
 obj-$(CONFIG_SERIO_Q40KBD)     += q40kbd.o
 obj-$(CONFIG_SERIO_GSCPS2)     += gscps2.o
 obj-$(CONFIG_HP_SDC)           += hp_sdc.o
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
new file mode 100644 (file)
index 0000000..41fda8c
--- /dev/null
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * Driver for the AT32AP700X PS/2 controller (PSIF).
+ *
+ * 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/device.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+/* PSIF register offsets */
+#define PSIF_CR                                0x00
+#define PSIF_RHR                       0x04
+#define PSIF_THR                       0x08
+#define PSIF_SR                                0x10
+#define PSIF_IER                       0x14
+#define PSIF_IDR                       0x18
+#define PSIF_IMR                       0x1c
+#define PSIF_PSR                       0x24
+
+/* Bitfields in control register. */
+#define PSIF_CR_RXDIS_OFFSET           1
+#define PSIF_CR_RXDIS_SIZE             1
+#define PSIF_CR_RXEN_OFFSET            0
+#define PSIF_CR_RXEN_SIZE              1
+#define PSIF_CR_SWRST_OFFSET           15
+#define PSIF_CR_SWRST_SIZE             1
+#define PSIF_CR_TXDIS_OFFSET           9
+#define PSIF_CR_TXDIS_SIZE             1
+#define PSIF_CR_TXEN_OFFSET            8
+#define PSIF_CR_TXEN_SIZE              1
+
+/* Bitfields in interrupt disable, enable, mask and status register. */
+#define PSIF_NACK_OFFSET               8
+#define PSIF_NACK_SIZE                 1
+#define PSIF_OVRUN_OFFSET              5
+#define PSIF_OVRUN_SIZE                        1
+#define PSIF_PARITY_OFFSET             9
+#define PSIF_PARITY_SIZE               1
+#define PSIF_RXRDY_OFFSET              4
+#define PSIF_RXRDY_SIZE                        1
+#define PSIF_TXEMPTY_OFFSET            1
+#define PSIF_TXEMPTY_SIZE              1
+#define PSIF_TXRDY_OFFSET              0
+#define PSIF_TXRDY_SIZE                        1
+
+/* Bitfields in prescale register. */
+#define PSIF_PSR_PRSCV_OFFSET          0
+#define PSIF_PSR_PRSCV_SIZE            12
+
+/* Bitfields in receive hold register. */
+#define PSIF_RHR_RXDATA_OFFSET         0
+#define PSIF_RHR_RXDATA_SIZE           8
+
+/* Bitfields in transmit hold register. */
+#define PSIF_THR_TXDATA_OFFSET         0
+#define PSIF_THR_TXDATA_SIZE           8
+
+/* Bit manipulation macros */
+#define PSIF_BIT(name)                                 \
+       (1 << PSIF_##name##_OFFSET)
+
+#define PSIF_BF(name, value)                           \
+       (((value) & ((1 << PSIF_##name##_SIZE) - 1))    \
+        << PSIF_##name##_OFFSET)
+
+#define PSIF_BFEXT(name, value)                                \
+       (((value) >> PSIF_##name##_OFFSET)              \
+        & ((1 << PSIF_##name##_SIZE) - 1))
+
+#define PSIF_BFINS(name, value, old)                   \
+       (((old) & ~(((1 << PSIF_##name##_SIZE) - 1)     \
+                   << PSIF_##name##_OFFSET))           \
+        | PSIF_BF(name, value))
+
+/* Register access macros */
+#define psif_readl(port, reg)                          \
+       __raw_readl((port)->regs + PSIF_##reg)
+
+#define psif_writel(port, reg, value)                  \
+       __raw_writel((value), (port)->regs + PSIF_##reg)
+
+struct psif {
+       struct platform_device  *pdev;
+       struct clk              *pclk;
+       struct serio            *io;
+       void __iomem            *regs;
+       unsigned int            irq;
+       unsigned int            open;
+       /* Prevent concurrent writes to PSIF THR. */
+       spinlock_t              lock;
+};
+
+static irqreturn_t psif_interrupt(int irq, void *_ptr)
+{
+       struct psif *psif = _ptr;
+       int retval = IRQ_NONE;
+       unsigned int io_flags = 0;
+       unsigned long status;
+
+       status = psif_readl(psif, SR);
+
+       if (status & PSIF_BIT(RXRDY)) {
+               unsigned char val = (unsigned char) psif_readl(psif, RHR);
+
+               if (status & PSIF_BIT(PARITY))
+                       io_flags |= SERIO_PARITY;
+               if (status & PSIF_BIT(OVRUN))
+                       dev_err(&psif->pdev->dev, "overrun read error\n");
+
+               serio_interrupt(psif->io, val, io_flags);
+
+               retval = IRQ_HANDLED;
+       }
+
+       return retval;
+}
+
+static int psif_write(struct serio *io, unsigned char val)
+{
+       struct psif *psif = io->port_data;
+       unsigned long flags;
+       int timeout = 10;
+       int retval = 0;
+
+       spin_lock_irqsave(&psif->lock, flags);
+
+       while (!(psif_readl(psif, SR) & PSIF_BIT(TXEMPTY)) && timeout--)
+               msleep(10);
+
+       if (timeout >= 0) {
+               psif_writel(psif, THR, val);
+       } else {
+               dev_dbg(&psif->pdev->dev, "timeout writing to THR\n");
+               retval = -EBUSY;
+       }
+
+       spin_unlock_irqrestore(&psif->lock, flags);
+
+       return retval;
+}
+
+static int psif_open(struct serio *io)
+{
+       struct psif *psif = io->port_data;
+       int retval;
+
+       retval = clk_enable(psif->pclk);
+       if (retval)
+               goto out;
+
+       psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN));
+       psif_writel(psif, IER, PSIF_BIT(RXRDY));
+
+       psif->open = 1;
+out:
+       return retval;
+}
+
+static void psif_close(struct serio *io)
+{
+       struct psif *psif = io->port_data;
+
+       psif->open = 0;
+
+       psif_writel(psif, IDR, ~0UL);
+       psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
+
+       clk_disable(psif->pclk);
+}
+
+static void psif_set_prescaler(struct psif *psif)
+{
+       unsigned long prscv;
+       unsigned long rate = clk_get_rate(psif->pclk);
+
+       /* PRSCV = Pulse length (100 us) * PSIF module frequency. */
+       prscv = 100 * (rate / 1000000UL);
+
+       if (prscv > ((1<<PSIF_PSR_PRSCV_SIZE) - 1)) {
+               prscv = (1<<PSIF_PSR_PRSCV_SIZE) - 1;
+               dev_dbg(&psif->pdev->dev, "pclk too fast, "
+                               "prescaler set to max\n");
+       }
+
+       clk_enable(psif->pclk);
+       psif_writel(psif, PSR, prscv);
+       clk_disable(psif->pclk);
+}
+
+static int __init psif_probe(struct platform_device *pdev)
+{
+       struct resource *regs;
+       struct psif *psif;
+       struct serio *io;
+       struct clk *pclk;
+       int irq;
+       int ret;
+
+       psif = kzalloc(sizeof(struct psif), GFP_KERNEL);
+       if (!psif) {
+               dev_dbg(&pdev->dev, "out of memory\n");
+               ret = -ENOMEM;
+               goto out;
+       }
+       psif->pdev = pdev;
+
+       io = kzalloc(sizeof(struct serio), GFP_KERNEL);
+       if (!io) {
+               dev_dbg(&pdev->dev, "out of memory\n");
+               ret = -ENOMEM;
+               goto out_free_psif;
+       }
+       psif->io = io;
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!regs) {
+               dev_dbg(&pdev->dev, "no mmio resources defined\n");
+               ret = -ENOMEM;
+               goto out_free_io;
+       }
+
+       psif->regs = ioremap(regs->start, regs->end - regs->start + 1);
+       if (!psif->regs) {
+               ret = -ENOMEM;
+               dev_dbg(&pdev->dev, "could not map I/O memory\n");
+               goto out_free_io;
+       }
+
+       pclk = clk_get(&pdev->dev, "pclk");
+       if (IS_ERR(pclk)) {
+               dev_dbg(&pdev->dev, "could not get peripheral clock\n");
+               ret = PTR_ERR(pclk);
+               goto out_iounmap;
+       }
+       psif->pclk = pclk;
+
+       /* Reset the PSIF to enter at a known state. */
+       ret = clk_enable(pclk);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not enable pclk\n");
+               goto out_put_clk;
+       }
+       psif_writel(psif, CR, PSIF_BIT(CR_SWRST));
+       clk_disable(pclk);
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_dbg(&pdev->dev, "could not get irq\n");
+               ret = -ENXIO;
+               goto out_put_clk;
+       }
+       ret = request_irq(irq, psif_interrupt, IRQF_SHARED, "at32psif", psif);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq %d\n", irq);
+               goto out_put_clk;
+       }
+       psif->irq = irq;
+
+       io->id.type     = SERIO_8042;
+       io->write       = psif_write;
+       io->open        = psif_open;
+       io->close       = psif_close;
+       snprintf(io->name, sizeof(io->name), "AVR32 PS/2 port%d", pdev->id);
+       snprintf(io->phys, sizeof(io->phys), "at32psif/serio%d", pdev->id);
+       io->port_data   = psif;
+       io->dev.parent  = &pdev->dev;
+
+       psif_set_prescaler(psif);
+
+       spin_lock_init(&psif->lock);
+       serio_register_port(psif->io);
+       platform_set_drvdata(pdev, psif);
+
+       dev_info(&pdev->dev, "Atmel AVR32 PSIF PS/2 driver on 0x%08x irq %d\n",
+                       (int)psif->regs, psif->irq);
+
+       return 0;
+
+out_put_clk:
+       clk_put(psif->pclk);
+out_iounmap:
+       iounmap(psif->regs);
+out_free_io:
+       kfree(io);
+out_free_psif:
+       kfree(psif);
+out:
+       return ret;
+}
+
+static int __exit psif_remove(struct platform_device *pdev)
+{
+       struct psif *psif = platform_get_drvdata(pdev);
+
+       psif_writel(psif, IDR, ~0UL);
+       psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
+
+       serio_unregister_port(psif->io);
+       iounmap(psif->regs);
+       free_irq(psif->irq, psif);
+       clk_put(psif->pclk);
+       kfree(psif);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int psif_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct psif *psif = platform_get_drvdata(pdev);
+
+       if (psif->open) {
+               psif_writel(psif, CR, PSIF_BIT(CR_RXDIS) | PSIF_BIT(CR_TXDIS));
+               clk_disable(psif->pclk);
+       }
+
+       return 0;
+}
+
+static int psif_resume(struct platform_device *pdev)
+{
+       struct psif *psif = platform_get_drvdata(pdev);
+
+       if (psif->open) {
+               clk_enable(psif->pclk);
+               psif_set_prescaler(psif);
+               psif_writel(psif, CR, PSIF_BIT(CR_RXEN) | PSIF_BIT(CR_TXEN));
+       }
+
+       return 0;
+}
+#else
+#define psif_suspend   NULL
+#define psif_resume    NULL
+#endif
+
+static struct platform_driver psif_driver = {
+       .remove         = __exit_p(psif_remove),
+       .driver         = {
+               .name   = "atmel_psif",
+       },
+       .suspend        = psif_suspend,
+       .resume         = psif_resume,
+};
+
+static int __init psif_init(void)
+{
+       return platform_driver_probe(&psif_driver, psif_probe);
+}
+
+static void __exit psif_exit(void)
+{
+       platform_driver_unregister(&psif_driver);
+}
+
+module_init(psif_init);
+module_exit(psif_exit);
+
+MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_DESCRIPTION("Atmel AVR32 PSIF PS/2 driver");
+MODULE_LICENSE("GPL");
index 60931ac..5ece9f5 100644 (file)
@@ -370,10 +370,10 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *
        if (pnp_irq_valid(dev,0))
                i8042_pnp_kbd_irq = pnp_irq(dev, 0);
 
-       strncpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name));
+       strlcpy(i8042_pnp_kbd_name, did->id, sizeof(i8042_pnp_kbd_name));
        if (strlen(pnp_dev_name(dev))) {
-               strncat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name));
-               strncat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));
+               strlcat(i8042_pnp_kbd_name, ":", sizeof(i8042_pnp_kbd_name));
+               strlcat(i8042_pnp_kbd_name, pnp_dev_name(dev), sizeof(i8042_pnp_kbd_name));
        }
 
        i8042_pnp_kbd_devices++;
@@ -391,10 +391,10 @@ static int i8042_pnp_aux_probe(struct pnp_dev *dev, const struct pnp_device_id *
        if (pnp_irq_valid(dev, 0))
                i8042_pnp_aux_irq = pnp_irq(dev, 0);
 
-       strncpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name));
+       strlcpy(i8042_pnp_aux_name, did->id, sizeof(i8042_pnp_aux_name));
        if (strlen(pnp_dev_name(dev))) {
-               strncat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name));
-               strncat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));
+               strlcat(i8042_pnp_aux_name, ":", sizeof(i8042_pnp_aux_name));
+               strlcat(i8042_pnp_aux_name, pnp_dev_name(dev), sizeof(i8042_pnp_aux_name));
        }
 
        i8042_pnp_aux_devices++;
index 49f8431..34c59d9 100644 (file)
@@ -45,6 +45,7 @@
 MODULE_AUTHOR("Vojtech Pavlik, Russell King");
 MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:kart");
 
 static int rpckbd_write(struct serio *port, unsigned char val)
 {
@@ -140,6 +141,7 @@ static struct platform_driver rpckbd_driver = {
        .remove         = __devexit_p(rpckbd_remove),
        .driver         = {
                .name   = "kart",
+               .owner  = THIS_MODULE,
        },
 };
 
index d371c0b..effb49e 100644 (file)
@@ -25,14 +25,14 @@ config TABLET_USB_ACECAD
          module will be called acecad.
 
 config TABLET_USB_AIPTEK
-       tristate "Aiptek 6000U/8000U tablet support (USB)"
+       tristate "Aiptek 6000U/8000U and Genius G_PEN tablet support (USB)"
        depends on USB_ARCH_HAS_HCD
        select USB
        help
-         Say Y here if you want to use the USB version of the Aiptek 6000U
-         or Aiptek 8000U tablet.  Make sure to say Y to "Mouse support"
-         (CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
-         (CONFIG_INPUT_EVDEV) as well.
+         Say Y here if you want to use the USB version of the Aiptek 6000U,
+         Aiptek 8000U or Genius G-PEN 560 tablet.  Make sure to say Y to
+         "Mouse support" (CONFIG_INPUT_MOUSEDEV) and/or "Event interface
+         support" (CONFIG_INPUT_EVDEV) as well.
 
          To compile this driver as a module, choose M here: the
          module will be called aiptek.
index 94683f5..1d759f6 100644 (file)
  */
 
 #define USB_VENDOR_ID_AIPTEK                           0x08ca
+#define USB_VENDOR_ID_KYE                              0x0458
 #define USB_REQ_GET_REPORT                             0x01
 #define USB_REQ_SET_REPORT                             0x09
 
@@ -832,6 +833,7 @@ static const struct usb_device_id aiptek_ids[] = {
        {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x22)},
        {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x23)},
        {USB_DEVICE(USB_VENDOR_ID_AIPTEK, 0x24)},
+       {USB_DEVICE(USB_VENDOR_ID_KYE, 0x5003)},
        {}
 };
 
index acf9830..706619d 100644 (file)
@@ -101,8 +101,11 @@ struct wacom {
        dma_addr_t data_dma;
        struct input_dev *dev;
        struct usb_device *usbdev;
+       struct usb_interface *intf;
        struct urb *irq;
        struct wacom_wac * wacom_wac;
+       struct mutex lock;
+       int open:1;
        char phys[32];
 };
 
index 41caaef..71cc0c1 100644 (file)
@@ -70,6 +70,7 @@ static void wacom_sys_irq(struct urb *urb)
                input_sync(get_input_dev(&wcombo));
 
  exit:
+       usb_mark_last_busy(wacom->usbdev);
        retval = usb_submit_urb (urb, GFP_ATOMIC);
        if (retval)
                err ("%s - usb_submit_urb failed with result %d",
@@ -124,10 +125,25 @@ static int wacom_open(struct input_dev *dev)
 {
        struct wacom *wacom = input_get_drvdata(dev);
 
+       mutex_lock(&wacom->lock);
+
        wacom->irq->dev = wacom->usbdev;
-       if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+
+       if (usb_autopm_get_interface(wacom->intf) < 0) {
+               mutex_unlock(&wacom->lock);
                return -EIO;
+       }
+
+       if (usb_submit_urb(wacom->irq, GFP_KERNEL)) {
+               usb_autopm_put_interface(wacom->intf);
+               mutex_unlock(&wacom->lock);
+               return -EIO;
+       }
+
+       wacom->open = 1;
+       wacom->intf->needs_remote_wakeup = 1;
 
+       mutex_unlock(&wacom->lock);
        return 0;
 }
 
@@ -135,7 +151,11 @@ static void wacom_close(struct input_dev *dev)
 {
        struct wacom *wacom = input_get_drvdata(dev);
 
+       mutex_lock(&wacom->lock);
        usb_kill_urb(wacom->irq);
+       wacom->open = 0;
+       wacom->intf->needs_remote_wakeup = 0;
+       mutex_unlock(&wacom->lock);
 }
 
 void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
@@ -243,6 +263,8 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 
        wacom->usbdev = dev;
        wacom->dev = input_dev;
+       wacom->intf = intf;
+       mutex_init(&wacom->lock);
        usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
        strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
 
@@ -304,23 +326,57 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
 
 static void wacom_disconnect(struct usb_interface *intf)
 {
-       struct wacom *wacom = usb_get_intfdata (intf);
+       struct wacom *wacom = usb_get_intfdata(intf);
 
        usb_set_intfdata(intf, NULL);
-       if (wacom) {
-               usb_kill_urb(wacom->irq);
-               input_unregister_device(wacom->dev);
-               usb_free_urb(wacom->irq);
-               usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
-               kfree(wacom->wacom_wac);
-               kfree(wacom);
-       }
+
+       usb_kill_urb(wacom->irq);
+       input_unregister_device(wacom->dev);
+       usb_free_urb(wacom->irq);
+       usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
+       kfree(wacom->wacom_wac);
+       kfree(wacom);
+}
+
+static int wacom_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct wacom *wacom = usb_get_intfdata(intf);
+
+       mutex_lock(&wacom->lock);
+       usb_kill_urb(wacom->irq);
+       mutex_unlock(&wacom->lock);
+
+       return 0;
+}
+
+static int wacom_resume(struct usb_interface *intf)
+{
+       struct wacom *wacom = usb_get_intfdata(intf);
+       int rv;
+
+       mutex_lock(&wacom->lock);
+       if (wacom->open)
+               rv = usb_submit_urb(wacom->irq, GFP_NOIO);
+       else
+               rv = 0;
+       mutex_unlock(&wacom->lock);
+
+       return rv;
+}
+
+static int wacom_reset_resume(struct usb_interface *intf)
+{
+       return wacom_resume(intf);
 }
 
 static struct usb_driver wacom_driver = {
        .name =         "wacom",
        .probe =        wacom_probe,
        .disconnect =   wacom_disconnect,
+       .suspend =      wacom_suspend,
+       .resume =       wacom_resume,
+       .reset_resume = wacom_reset_resume,
+       .supports_autosuspend = 1,
 };
 
 static int __init wacom_init(void)
index ffe3384..192513e 100644 (file)
@@ -649,6 +649,7 @@ static struct wacom_features wacom_features[] = {
        { "Wacom Intuos3 6x11",  10, 54204, 31750, 1023, 63, INTUOS3 },
        { "Wacom Intuos3 4x6",   10, 31496, 19685, 1023, 63, INTUOS3S },
        { "Wacom Cintiq 21UX",   10, 87200, 65600, 1023, 63, CINTIQ },
+       { "Wacom Cintiq 20WSX",  10, 86680, 54180, 1023, 63, WACOM_BEE },
        { "Wacom Cintiq 12WX",   10, 53020, 33440, 1023, 63, WACOM_BEE },
        { "Wacom Intuos2 6x8",   10, 20320, 16240, 1023, 31, INTUOS },
        { }
@@ -702,6 +703,7 @@ static struct usb_device_id wacom_ids[] = {
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
+       { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC5) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC6) },
        { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
        { }
index 90e8e92..565ec71 100644 (file)
@@ -185,6 +185,59 @@ config TOUCHSCREEN_UCB1400
          To compile this driver as a module, choose M here: the
          module will be called ucb1400_ts.
 
+config TOUCHSCREEN_WM97XX
+       tristate "Support for WM97xx AC97 touchscreen controllers"
+       depends on AC97_BUS
+       help
+         Say Y here if you have a Wolfson Microelectronics WM97xx
+         touchscreen connected to your system. Note that this option
+         only enables core driver, you will also need to select
+         support for appropriate chip below.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called wm97xx-ts.
+
+config TOUCHSCREEN_WM9705
+       bool "WM9705 Touchscreen interface support"
+       depends on TOUCHSCREEN_WM97XX
+       help
+         Say Y here if you have a Wolfson Microelectronics WM9705
+         touchscreen controller connected to your system.
+
+         If unsure, say N.
+
+config TOUCHSCREEN_WM9712
+       bool "WM9712 Touchscreen interface support"
+       depends on TOUCHSCREEN_WM97XX
+       help
+         Say Y here if you have a Wolfson Microelectronics WM9712
+         touchscreen controller connected to your system.
+
+         If unsure, say N.
+
+config TOUCHSCREEN_WM9713
+       bool "WM9713 Touchscreen interface support"
+       depends on TOUCHSCREEN_WM97XX
+       help
+         Say Y here if you have a Wolfson Microelectronics WM9713 touchscreen
+         controller connected to your system.
+
+         If unsure, say N.
+
+config TOUCHSCREEN_WM97XX_MAINSTONE
+       tristate "WM97xx Mainstone accelerated touch"
+       depends on TOUCHSCREEN_WM97XX && ARCH_PXA
+       help
+         Say Y here for support for streaming mode with WM97xx touchscreens
+         on Mainstone systems.
+
+         If unsure, say N.
+
+         To compile this driver as a module, choose M here: the
+         module will be called mainstone-wm97xx.
+
 config TOUCHSCREEN_USB_COMPOSITE
        tristate "USB Touchscreen Driver"
        depends on USB_ARCH_HAS_HCD
index 35d4097..3c096d7 100644 (file)
@@ -4,6 +4,8 @@
 
 # Each configuration option enables a list of files.
 
+wm97xx-ts-y := wm97xx-core.o
+
 obj-$(CONFIG_TOUCHSCREEN_ADS7846)      += ads7846.o
 obj-$(CONFIG_TOUCHSCREEN_BITSY)                += h3600_ts_input.o
 obj-$(CONFIG_TOUCHSCREEN_CORGI)                += corgi_ts.o
@@ -19,3 +21,8 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)    += penmount.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
+obj-$(CONFIG_TOUCHSCREEN_WM97XX)       += wm97xx-ts.o
+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
index 57a1c28..a571aa9 100644 (file)
@@ -87,6 +87,7 @@ struct ads7846 {
 #endif
 
        u16                     model;
+       u16                     vref_mv;
        u16                     vref_delay_usecs;
        u16                     x_plate_ohms;
        u16                     pressure_max;
@@ -184,9 +185,6 @@ struct ads7846 {
  * The range is GND..vREF. The ads7843 and ads7835 must use external vREF;
  * ads7846 lets that pin be unconnected, to use internal vREF.
  */
-static unsigned vREF_mV;
-module_param(vREF_mV, uint, 0);
-MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts");
 
 struct ser_req {
        u8                      ref_on;
@@ -213,7 +211,6 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
        struct ads7846          *ts = dev_get_drvdata(dev);
        struct ser_req          *req = kzalloc(sizeof *req, GFP_KERNEL);
        int                     status;
-       int                     uninitialized_var(sample);
        int                     use_internal;
 
        if (!req)
@@ -270,13 +267,13 @@ static int ads7846_read12_ser(struct device *dev, unsigned command)
 
        if (status == 0) {
                /* on-wire is a must-ignore bit, a BE12 value, then padding */
-               sample = be16_to_cpu(req->sample);
-               sample = sample >> 3;
-               sample &= 0x0fff;
+               status = be16_to_cpu(req->sample);
+               status = status >> 3;
+               status &= 0x0fff;
        }
 
        kfree(req);
-       return status ? status : sample;
+       return status;
 }
 
 #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
@@ -317,7 +314,7 @@ static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v)
        unsigned retval = v;
 
        /* external resistors may scale vAUX into 0..vREF */
-       retval *= vREF_mV;
+       retval *= ts->vref_mv;
        retval = retval >> 12;
        return retval;
 }
@@ -375,14 +372,14 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
        /* hwmon sensors need a reference voltage */
        switch (ts->model) {
        case 7846:
-               if (!vREF_mV) {
+               if (!ts->vref_mv) {
                        dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n");
-                       vREF_mV = 2500;
+                       ts->vref_mv = 2500;
                }
                break;
        case 7845:
        case 7843:
-               if (!vREF_mV) {
+               if (!ts->vref_mv) {
                        dev_warn(&spi->dev,
                                "external vREF for ADS%d not specified\n",
                                ts->model);
@@ -875,6 +872,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
 
        ts->spi = spi;
        ts->input = input_dev;
+       ts->vref_mv = pdata->vref_mv;
 
        hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        ts->timer.function = ads7846_timer;
index 99d92f5..765e964 100644 (file)
@@ -361,6 +361,7 @@ static struct platform_driver corgits_driver = {
        .resume         = corgits_resume,
        .driver         = {
                .name   = "corgi-ts",
+               .owner  = THIS_MODULE,
        },
 };
 
@@ -380,3 +381,4 @@ module_exit(corgits_exit);
 MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
 MODULE_DESCRIPTION("Corgi TouchScreen Driver");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:corgi-ts");
index 42a1c9a..7422421 100644 (file)
@@ -160,11 +160,15 @@ static int __devexit jornada720_ts_remove(struct platform_device *pdev)
        return 0;
 }
 
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada_ts");
+
 static struct platform_driver jornada720_ts_driver = {
        .probe          = jornada720_ts_probe,
        .remove         = __devexit_p(jornada720_ts_remove),
        .driver         = {
                .name   = "jornada_ts",
+               .owner  = THIS_MODULE,
        },
 };
 
diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c
new file mode 100644 (file)
index 0000000..a79f029
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * mainstone-wm97xx.c  --  Mainstone Continuous Touch screen driver for
+ *                         Wolfson WM97xx AC97 Codecs.
+ *
+ * Copyright 2004, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@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 to capture touch
+ *     data in a continuous manner on the Intel XScale archictecture
+ *
+ *  Features:
+ *       - codecs supported:- WM9705, WM9712, WM9713
+ *       - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wm97xx.h>
+#include <linux/io.h>
+#include <asm/arch/pxa-regs.h>
+
+#define VERSION                "0.13"
+
+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[] = {
+       {WM9705_ID2, 0, WM_READS(94), 94},
+       {WM9705_ID2, 1, WM_READS(188), 188},
+       {WM9705_ID2, 2, WM_READS(375), 375},
+       {WM9705_ID2, 3, WM_READS(750), 750},
+       {WM9712_ID2, 0, WM_READS(94), 94},
+       {WM9712_ID2, 1, WM_READS(188), 188},
+       {WM9712_ID2, 2, WM_READS(375), 375},
+       {WM9712_ID2, 3, WM_READS(750), 750},
+       {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;
+static u16 last, tries;
+
+/*
+ * 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)");
+
+/*
+ * Pen down detection.
+ *
+ * This driver can either poll or use an interrupt to indicate a pen down
+ * event. If the irq request fails then it will fall back to polling mode.
+ */
+static int pen_int;
+module_param(pen_int, int, 0);
+MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
+
+/*
+ * 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 on pxa machines */
+#ifdef CONFIG_PXA27x
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+       schedule_timeout_uninterruptible(1);
+
+       while (MISR & (1 << 2))
+               MODR;
+}
+#else
+static void wm97xx_acc_pen_up(struct wm97xx *wm)
+{
+       int count = 16;
+       schedule_timeout_uninterruptible(1);
+
+       while (count < 16) {
+               MODR;
+               count--;
+       }
+}
+#endif
+
+static int wm97xx_acc_pen_down(struct wm97xx *wm)
+{
+       u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
+       int reads = 0;
+
+       /* 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.
+        */
+       schedule_timeout_uninterruptible(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_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 = 0;
+
+       /* 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,
+                "mainstone accelerated touchscreen driver, %d samples/sec\n",
+                cinfo[sp_idx].speed);
+
+       /* codec specific irq config */
+       if (pen_int) {
+               switch (wm->id) {
+               case WM9705_ID2:
+                       wm->pen_irq = IRQ_GPIO(4);
+                       set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE);
+                       break;
+               case WM9712_ID2:
+               case WM9713_ID2:
+                       /* enable pen down interrupt */
+                       /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */
+                       wm->pen_irq = MAINSTONE_AC97_IRQ;
+                       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);
+                       break;
+               default:
+                       dev_err(wm->dev,
+                               "pen down irq not supported on this device\n");
+                       pen_int = 0;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+static void wm97xx_acc_shutdown(struct wm97xx *wm)
+{
+       /* codec specific deconfig */
+       if (pen_int) {
+               switch (wm->id & 0xffff) {
+               case WM9705_ID2:
+                       wm->pen_irq = 0;
+                       break;
+               case WM9712_ID2:
+               case WM9713_ID2:
+                       /* disable interrupt */
+                       wm->pen_irq = 0;
+                       break;
+               }
+       }
+}
+
+static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
+{
+       if (enable)
+               enable_irq(wm->pen_irq);
+       else
+               disable_irq(wm->pen_irq);
+}
+
+static struct wm97xx_mach_ops mainstone_mach_ops = {
+       .acc_enabled = 1,
+       .acc_pen_up = wm97xx_acc_pen_up,
+       .acc_pen_down = wm97xx_acc_pen_down,
+       .acc_startup = wm97xx_acc_startup,
+       .acc_shutdown = wm97xx_acc_shutdown,
+       .irq_enable = wm97xx_irq_enable,
+       .irq_gpio = WM97XX_GPIO_2,
+};
+
+static int mainstone_wm97xx_probe(struct platform_device *pdev)
+{
+       struct wm97xx *wm = platform_get_drvdata(pdev);
+
+       return wm97xx_register_mach_ops(wm, &mainstone_mach_ops);
+}
+
+static int mainstone_wm97xx_remove(struct platform_device *pdev)
+{
+       struct wm97xx *wm = platform_get_drvdata(pdev);
+
+       wm97xx_unregister_mach_ops(wm);
+       return 0;
+}
+
+static struct platform_driver mainstone_wm97xx_driver = {
+       .probe = mainstone_wm97xx_probe,
+       .remove = mainstone_wm97xx_remove,
+       .driver = {
+               .name = "wm97xx-touch",
+       },
+};
+
+static int __init mainstone_wm97xx_init(void)
+{
+       return platform_driver_register(&mainstone_wm97xx_driver);
+}
+
+static void __exit mainstone_wm97xx_exit(void)
+{
+       platform_driver_unregister(&mainstone_wm97xx_driver);
+}
+
+module_init(mainstone_wm97xx_init);
+module_exit(mainstone_wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
+MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone");
+MODULE_LICENSE("GPL");
index 607f993..bce018e 100644 (file)
@@ -427,10 +427,6 @@ static int ucb1400_detect_irq(struct ucb1400 *ucb)
        unsigned long mask, timeout;
 
        mask = probe_irq_on();
-       if (!mask) {
-               probe_irq_off(mask);
-               return -EBUSY;
-       }
 
        /* Enable the ADC interrupt. */
        ucb1400_reg_write(ucb, UCB_IE_RIS, UCB_IE_ADC);
index 63f9664..3a0a8ca 100644 (file)
@@ -396,9 +396,12 @@ static int gunze_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
 static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
 {
        struct usb_device *dev = usbtouch->udev;
-       int ret;
-       unsigned char buf[2];
+       int ret = -ENOMEM;
+       unsigned char *buf;
 
+       buf = kmalloc(2, GFP_KERNEL);
+       if (!buf)
+               goto err_nobuf;
        /* reset */
        buf[0] = buf[1] = 0xFF;
        ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
@@ -406,9 +409,11 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                              0, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
        if (ret < 0)
-               return ret;
-       if (buf[0] != 0x06 || buf[1] != 0x00)
-               return -ENODEV;
+               goto err_out;
+       if (buf[0] != 0x06 || buf[1] != 0x00) {
+               ret = -ENODEV;
+               goto err_out;
+       }
 
        /* set coordinate output rate */
        buf[0] = buf[1] = 0xFF;
@@ -417,20 +422,22 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                              TSC10_RATE_150, 0, buf, 2, USB_CTRL_SET_TIMEOUT);
        if (ret < 0)
-               return ret;
+               goto err_out;
        if ((buf[0] != 0x06 || buf[1] != 0x00) &&
-           (buf[0] != 0x15 || buf[1] != 0x01))
-               return -ENODEV;
+           (buf[0] != 0x15 || buf[1] != 0x01)) {
+               ret = -ENODEV;
+               goto err_out;
+       }
 
        /* start sending data */
        ret = usb_control_msg(dev, usb_rcvctrlpipe (dev, 0),
                              TSC10_CMD_DATA1,
                              USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
                              0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
-       if (ret < 0)
-               return ret;
-
-       return 0;
+err_out:
+       kfree(buf);
+err_nobuf:
+       return ret;
 }
 
 
diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c
new file mode 100644 (file)
index 0000000..978e1a1
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * wm9705.c  --  Codec driver for Wolfson WM9705 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME                        "wm97xx"
+#define WM9705_VERSION         "1.00"
+#define DEFAULT_PRESSURE       0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Pen detect comparator threshold.
+ *
+ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold
+ * i.e. 1 =  Vmid/15 threshold
+ *      15 =  Vmid/1 threshold
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down events.
+ */
+static int pdd = 8;
+module_param(pdd, int, 0);
+MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+       21,    /* 1 AC97 Link frames */
+       42,    /* 2                  */
+       84,    /* 4                  */
+       167,   /* 8                  */
+       333,   /* 16                 */
+       667,   /* 32                 */
+       1000,  /* 48                 */
+       1333,  /* 64                 */
+       2000,  /* 96                 */
+       2667,  /* 128                */
+       3333,  /* 160                */
+       4000,  /* 192                */
+       4667,  /* 224                */
+       5333,  /* 256                */
+       6000,  /* 288                */
+       0      /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+       udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9705
+ */
+static void wm9705_phy_init(struct wm97xx *wm)
+{
+       u16 dig1 = 0, dig2 = WM97XX_RPR;
+
+       /*
+       * mute VIDEO and AUX as they share X and Y touchscreen
+       * inputs on the WM9705
+       */
+       wm97xx_reg_write(wm, AC97_AUX, 0x8000);
+       wm97xx_reg_write(wm, AC97_VIDEO, 0x8000);
+
+       /* touchpanel pressure current*/
+       if (pil == 2) {
+               dig2 |= WM9705_PIL;
+               dev_dbg(wm->dev,
+                       "setting pressure measurement current to 400uA.");
+       } else if (pil)
+               dev_dbg(wm->dev,
+                       "setting pressure measurement current to 200uA.");
+       if (!pil)
+               pressure = 0;
+
+       /* polling mode sample settling delay */
+       if (delay != 4) {
+               if (delay < 0 || delay > 15) {
+                       dev_dbg(wm->dev, "supplied delay out of range.");
+                       delay = 4;
+               }
+       }
+       dig1 &= 0xff0f;
+       dig1 |= WM97XX_DELAY(delay);
+       dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+               delay_table[delay]);
+
+       /* WM9705 pdd */
+       dig2 |= (pdd & 0x000f);
+       dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f));
+
+       /* mask */
+       dig2 |= ((mask & 0x3) << 4);
+
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9705_dig_enable(struct wm97xx *wm, int enable)
+{
+       if (enable) {
+               wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+                                wm->dig[2] | WM97XX_PRP_DET_DIG);
+               wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+       } else
+               wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+                                wm->dig[2] & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_aux_prepare(struct wm97xx *wm)
+{
+       memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9705_dig_restore(struct wm97xx *wm)
+{
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+       return wm->dig[2] & WM9705_PDEN;
+}
+
+/*
+ * Read a sample from the WM9705 adc in polling mode.
+ */
+static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+       int timeout = 5 * delay;
+
+       if (!wm->pen_probably_down) {
+               u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+               if (!(data & WM97XX_PEN_DOWN))
+                       return RC_PENUP;
+               wm->pen_probably_down = 1;
+       }
+
+       /* set up digitiser */
+       if (adcsel & 0x8000)
+               adcsel = ((adcsel & 0x7fff) + 3) << 12;
+
+       if (wm->mach_ops && wm->mach_ops->pre_sample)
+               wm->mach_ops->pre_sample(adcsel);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+                        adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+
+       /* wait 3 AC97 time slots + delay for conversion */
+       poll_delay(delay);
+
+       /* wait for POLL to go low */
+       while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+              && timeout) {
+               udelay(AC97_LINK_FRAME);
+               timeout--;
+       }
+
+       if (timeout == 0) {
+               /* If PDEN is set, we can get a timeout when pen goes up */
+               if (is_pden(wm))
+                       wm->pen_probably_down = 0;
+               else
+                       dev_dbg(wm->dev, "adc sample timeout");
+               return RC_PENUP;
+       }
+
+       *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       if (wm->mach_ops && wm->mach_ops->post_sample)
+               wm->mach_ops->post_sample(adcsel);
+
+       /* check we have correct sample */
+       if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
+               dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
+               *sample & WM97XX_ADCSEL_MASK);
+               return RC_PENUP;
+       }
+
+       if (!(*sample & WM97XX_PEN_DOWN)) {
+               wm->pen_probably_down = 0;
+               return RC_PENUP;
+       }
+
+       return RC_VALID;
+}
+
+/*
+ * Sample the WM9705 touchscreen in polling mode
+ */
+static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+       int rc;
+
+       rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
+       if (rc != RC_VALID)
+               return rc;
+       rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
+       if (rc != RC_VALID)
+               return rc;
+       if (pil) {
+               rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p);
+               if (rc != RC_VALID)
+                       return rc;
+       } else
+               data->p = DEFAULT_PRESSURE;
+
+       return RC_VALID;
+}
+
+/*
+ * Enable WM9705 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9705_acc_enable(struct wm97xx *wm, int enable)
+{
+       u16 dig1, dig2;
+       int ret = 0;
+
+       dig1 = wm->dig[1];
+       dig2 = wm->dig[2];
+
+       if (enable) {
+               /* continous mode */
+               if (wm->mach_ops->acc_startup &&
+                   (ret = wm->mach_ops->acc_startup(wm)) < 0)
+                       return ret;
+               dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+                         WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+               dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+                       WM97XX_DELAY(delay) |
+                       WM97XX_SLT(wm->acc_slot) |
+                       WM97XX_RATE(wm->acc_rate);
+               if (pil)
+                       dig1 |= WM97XX_ADCSEL_PRES;
+               dig2 |= WM9705_PDEN;
+       } else {
+               dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+               dig2 &= ~WM9705_PDEN;
+               if (wm->mach_ops->acc_shutdown)
+                       wm->mach_ops->acc_shutdown(wm);
+       }
+
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+       return ret;
+}
+
+struct wm97xx_codec_drv wm9705_codec = {
+       .id = WM9705_ID2,
+       .name = "wm9705",
+       .poll_sample = wm9705_poll_sample,
+       .poll_touch = wm9705_poll_touch,
+       .acc_enable = wm9705_acc_enable,
+       .phy_init = wm9705_phy_init,
+       .dig_enable = wm9705_dig_enable,
+       .dig_restore = wm9705_dig_restore,
+       .aux_prepare = wm9705_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9705_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM9705 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c
new file mode 100644 (file)
index 0000000..0b6e4cf
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+ * wm9712.c  --  Codec driver for Wolfson WM9712 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME                        "wm97xx"
+#define WM9712_VERSION         "1.00"
+#define DEFAULT_PRESSURE       0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 3;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set five_wire = 1 to use a 5 wire touchscreen.
+ *
+ * NOTE: Five wire mode does not allow for readback of pressure.
+ */
+static int five_wire;
+module_param(five_wire, int, 0);
+MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+       21,    /* 1 AC97 Link frames */
+       42,    /* 2 */
+       84,    /* 4 */
+       167,   /* 8 */
+       333,   /* 16 */
+       667,   /* 32 */
+       1000,  /* 48 */
+       1333,  /* 64 */
+       2000,  /* 96 */
+       2667,  /* 128 */
+       3333,  /* 160 */
+       4000,  /* 192 */
+       4667,  /* 224 */
+       5333,  /* 256 */
+       6000,  /* 288 */
+       0      /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+       udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9712
+ */
+static void wm9712_phy_init(struct wm97xx *wm)
+{
+       u16 dig1 = 0;
+       u16 dig2 = WM97XX_RPR | WM9712_RPU(1);
+
+       /* WM9712 rpu */
+       if (rpu) {
+               dig2 &= 0xffc0;
+               dig2 |= WM9712_RPU(rpu);
+               dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms",
+                       64000 / rpu);
+       }
+
+       /* touchpanel pressure current*/
+       if (pil == 2) {
+               dig2 |= WM9712_PIL;
+               dev_dbg(wm->dev,
+                       "setting pressure measurement current to 400uA.");
+       } else if (pil)
+               dev_dbg(wm->dev,
+                       "setting pressure measurement current to 200uA.");
+       if (!pil)
+               pressure = 0;
+
+       /* WM9712 five wire */
+       if (five_wire) {
+               dig2 |= WM9712_45W;
+               dev_dbg(wm->dev, "setting 5-wire touchscreen mode.");
+       }
+
+       /* polling mode sample settling delay */
+       if (delay < 0 || delay > 15) {
+               dev_dbg(wm->dev, "supplied delay out of range.");
+               delay = 4;
+       }
+       dig1 &= 0xff0f;
+       dig1 |= WM97XX_DELAY(delay);
+       dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.",
+               delay_table[delay]);
+
+       /* mask */
+       dig2 |= ((mask & 0x3) << 6);
+       if (mask) {
+               u16 reg;
+               /* Set GPIO4 as Mask Pin*/
+               reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+               wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4);
+               reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+               wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4);
+       }
+
+       /* wait - coord mode */
+       if (coord)
+               dig2 |= WM9712_WAIT;
+
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+}
+
+static void wm9712_dig_enable(struct wm97xx *wm, int enable)
+{
+       u16 dig2 = wm->dig[2];
+
+       if (enable) {
+               wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+                                dig2 | WM97XX_PRP_DET_DIG);
+               wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+       } else
+               wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2,
+                                dig2 & ~WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_aux_prepare(struct wm97xx *wm)
+{
+       memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG);
+}
+
+static void wm9712_dig_restore(struct wm97xx *wm)
+{
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+       return wm->dig[2] & WM9712_PDEN;
+}
+
+/*
+ * Read a sample from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+       int timeout = 5 * delay;
+
+       if (!wm->pen_probably_down) {
+               u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+               if (!(data & WM97XX_PEN_DOWN))
+                       return RC_PENUP;
+               wm->pen_probably_down = 1;
+       }
+
+       /* set up digitiser */
+       if (adcsel & 0x8000)
+               adcsel = ((adcsel & 0x7fff) + 3) << 12;
+
+       if (wm->mach_ops && wm->mach_ops->pre_sample)
+               wm->mach_ops->pre_sample(adcsel);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+                        adcsel | WM97XX_POLL | WM97XX_DELAY(delay));
+
+       /* wait 3 AC97 time slots + delay for conversion */
+       poll_delay(delay);
+
+       /* wait for POLL to go low */
+       while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+              && timeout) {
+               udelay(AC97_LINK_FRAME);
+               timeout--;
+       }
+
+       if (timeout <= 0) {
+               /* If PDEN is set, we can get a timeout when pen goes up */
+               if (is_pden(wm))
+                       wm->pen_probably_down = 0;
+               else
+                       dev_dbg(wm->dev, "adc sample timeout");
+               return RC_PENUP;
+       }
+
+       *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       if (wm->mach_ops && wm->mach_ops->post_sample)
+               wm->mach_ops->post_sample(adcsel);
+
+       /* check we have correct sample */
+       if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) {
+               dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
+               *sample & WM97XX_ADCSEL_MASK);
+               return RC_PENUP;
+       }
+
+       if (!(*sample & WM97XX_PEN_DOWN)) {
+               wm->pen_probably_down = 0;
+               return RC_PENUP;
+       }
+
+       return RC_VALID;
+}
+
+/*
+ * Read a coord from the WM9712 adc in polling mode.
+ */
+static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+       int timeout = 5 * delay;
+
+       if (!wm->pen_probably_down) {
+               u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+               if (!(data_rd & WM97XX_PEN_DOWN))
+                       return RC_PENUP;
+               wm->pen_probably_down = 1;
+       }
+
+       /* set up digitiser */
+       if (wm->mach_ops && wm->mach_ops->pre_sample)
+               wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1,
+               WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay));
+
+       /* wait 3 AC97 time slots + delay for conversion and read x */
+       poll_delay(delay);
+       data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       /* wait for POLL to go low */
+       while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)
+              && timeout) {
+               udelay(AC97_LINK_FRAME);
+               timeout--;
+       }
+
+       if (timeout <= 0) {
+               /* If PDEN is set, we can get a timeout when pen goes up */
+               if (is_pden(wm))
+                       wm->pen_probably_down = 0;
+               else
+                       dev_dbg(wm->dev, "adc sample timeout");
+               return RC_PENUP;
+       }
+
+       /* read back y data */
+       data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       if (pil)
+               data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       else
+               data->p = DEFAULT_PRESSURE;
+
+       if (wm->mach_ops && wm->mach_ops->post_sample)
+               wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+       /* check we have correct sample */
+       if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+               goto err;
+       if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+               goto err;
+
+       if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+               wm->pen_probably_down = 0;
+               return RC_PENUP;
+       }
+       return RC_VALID;
+err:
+       return 0;
+}
+
+/*
+ * Sample the WM9712 touchscreen in polling mode
+ */
+static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+       int rc;
+
+       if (coord) {
+               rc = wm9712_poll_coord(wm, data);
+               if (rc != RC_VALID)
+                       return rc;
+       } else {
+               rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x);
+               if (rc != RC_VALID)
+                       return rc;
+
+               rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y);
+               if (rc != RC_VALID)
+                       return rc;
+
+               if (pil && !five_wire) {
+                       rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES,
+                                               &data->p);
+                       if (rc != RC_VALID)
+                               return rc;
+               } else
+                       data->p = DEFAULT_PRESSURE;
+       }
+       return RC_VALID;
+}
+
+/*
+ * Enable WM9712 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9712_acc_enable(struct wm97xx *wm, int enable)
+{
+       u16 dig1, dig2;
+       int ret = 0;
+
+       dig1 = wm->dig[1];
+       dig2 = wm->dig[2];
+
+       if (enable) {
+               /* continous mode */
+               if (wm->mach_ops->acc_startup) {
+                       ret = wm->mach_ops->acc_startup(wm);
+                       if (ret < 0)
+                               return ret;
+               }
+               dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK |
+                       WM97XX_DELAY_MASK | WM97XX_SLT_MASK);
+               dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN |
+                       WM97XX_DELAY(delay) |
+                       WM97XX_SLT(wm->acc_slot) |
+                       WM97XX_RATE(wm->acc_rate);
+               if (pil)
+                       dig1 |= WM97XX_ADCSEL_PRES;
+               dig2 |= WM9712_PDEN;
+       } else {
+               dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN);
+               dig2 &= ~WM9712_PDEN;
+               if (wm->mach_ops->acc_shutdown)
+                       wm->mach_ops->acc_shutdown(wm);
+       }
+
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1);
+       wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2);
+
+       return 0;
+}
+
+struct wm97xx_codec_drv wm9712_codec = {
+       .id = WM9712_ID2,
+       .name = "wm9712",
+       .poll_sample = wm9712_poll_sample,
+       .poll_touch = wm9712_poll_touch,
+       .acc_enable = wm9712_acc_enable,
+       .phy_init = wm9712_phy_init,
+       .dig_enable = wm9712_dig_enable,
+       .dig_restore = wm9712_dig_restore,
+       .aux_prepare = wm9712_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9712_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM9712 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c
new file mode 100644 (file)
index 0000000..01278bd
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * wm9713.c  --  Codec touch driver for Wolfson WM9713 AC97 Codec.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/wm97xx.h>
+
+#define TS_NAME                        "wm97xx"
+#define WM9713_VERSION         "1.00"
+#define DEFAULT_PRESSURE       0xb0c0
+
+/*
+ * Module parameters
+ */
+
+/*
+ * Set internal pull up for pen detect.
+ *
+ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive)
+ * i.e. pull up resistance = 64k Ohms / rpu.
+ *
+ * Adjust this value if you are having problems with pen detect not
+ * detecting any down event.
+ */
+static int rpu = 8;
+module_param(rpu, int, 0);
+MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect.");
+
+/*
+ * Set current used for pressure measurement.
+ *
+ * Set pil = 2 to use 400uA
+ *     pil = 1 to use 200uA and
+ *     pil = 0 to disable pressure measurement.
+ *
+ * This is used to increase the range of values returned by the adc
+ * when measureing touchpanel pressure.
+ */
+static int pil;
+module_param(pil, int, 0);
+MODULE_PARM_DESC(pil, "Set current used for pressure measurement.");
+
+/*
+ * Set threshold for pressure measurement.
+ *
+ * Pen down pressure below threshold is ignored.
+ */
+static int pressure = DEFAULT_PRESSURE & 0xfff;
+module_param(pressure, int, 0);
+MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement.");
+
+/*
+ * Set adc sample delay.
+ *
+ * For accurate touchpanel measurements, some settling time may be
+ * required between the switch matrix applying a voltage across the
+ * touchpanel plate and the ADC sampling the signal.
+ *
+ * This delay can be set by setting delay = n, where n is the array
+ * position of the delay in the array delay_table below.
+ * Long delays > 1ms are supported for completeness, but are not
+ * recommended.
+ */
+static int delay = 4;
+module_param(delay, int, 0);
+MODULE_PARM_DESC(delay, "Set adc sample delay.");
+
+/*
+ * Set adc mask function.
+ *
+ * Sources of glitch noise, such as signals driving an LCD display, may feed
+ * through to the touch screen plates and affect measurement accuracy. In
+ * order to minimise this, a signal may be applied to the MASK pin to delay or
+ * synchronise the sampling.
+ *
+ * 0 = No delay or sync
+ * 1 = High on pin stops conversions
+ * 2 = Edge triggered, edge on pin delays conversion by delay param (above)
+ * 3 = Edge triggered, edge on pin starts conversion after delay param
+ */
+static int mask;
+module_param(mask, int, 0);
+MODULE_PARM_DESC(mask, "Set adc mask function.");
+
+/*
+ * Coordinate Polling Enable.
+ *
+ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together
+ * for every poll.
+ */
+static int coord;
+module_param(coord, int, 0);
+MODULE_PARM_DESC(coord, "Polling coordinate mode");
+
+/*
+ * ADC sample delay times in uS
+ */
+static const int delay_table[] = {
+       21,    /* 1 AC97 Link frames */
+       42,    /* 2 */
+       84,    /* 4 */
+       167,   /* 8 */
+       333,   /* 16 */
+       667,   /* 32 */
+       1000,  /* 48 */
+       1333,  /* 64 */
+       2000,  /* 96 */
+       2667,  /* 128 */
+       3333,  /* 160 */
+       4000,  /* 192 */
+       4667,  /* 224 */
+       5333,  /* 256 */
+       6000,  /* 288 */
+       0      /* No delay, switch matrix always on */
+};
+
+/*
+ * Delay after issuing a POLL command.
+ *
+ * The delay is 3 AC97 link frames + the touchpanel settling delay
+ */
+static inline void poll_delay(int d)
+{
+       udelay(3 * AC97_LINK_FRAME + delay_table[d]);
+}
+
+/*
+ * set up the physical settings of the WM9713
+ */
+static void wm9713_phy_init(struct wm97xx *wm)
+{
+       u16 dig1 = 0, dig2, dig3;
+
+       /* default values */
+       dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5);
+       dig3 = WM9712_RPU(1);
+
+       /* rpu */
+       if (rpu) {
+               dig3 &= 0xffc0;
+               dig3 |= WM9712_RPU(rpu);
+               dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n",
+                        64000 / rpu);
+       }
+
+       /* touchpanel pressure */
+       if (pil == 2) {
+               dig3 |= WM9712_PIL;
+               dev_info(wm->dev,
+                        "setting pressure measurement current to 400uA.");
+       } else if (pil)
+               dev_info(wm->dev,
+                        "setting pressure measurement current to 200uA.");
+       if (!pil)
+               pressure = 0;
+
+       /* sample settling delay */
+       if (delay < 0 || delay > 15) {
+               dev_info(wm->dev, "supplied delay out of range.");
+               delay = 4;
+               dev_info(wm->dev, "setting adc sample delay to %d u Secs.",
+                        delay_table[delay]);
+       }
+       dig2 &= 0xff0f;
+       dig2 |= WM97XX_DELAY(delay);
+
+       /* mask */
+       dig3 |= ((mask & 0x3) << 4);
+       if (coord)
+               dig3 |= WM9713_WAIT;
+
+       wm->misc = wm97xx_reg_read(wm, 0x5a);
+
+       wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+       wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0);
+}
+
+static void wm9713_dig_enable(struct wm97xx *wm, int enable)
+{
+       u16 val;
+
+       if (enable) {
+               val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+               wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff);
+               wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] |
+                                WM97XX_PRP_DET_DIG);
+               wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */
+       } else {
+               wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] &
+                                       ~WM97XX_PRP_DET_DIG);
+               val = wm97xx_reg_read(wm, AC97_EXTENDED_MID);
+               wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000);
+       }
+}
+
+static void wm9713_dig_restore(struct wm97xx *wm)
+{
+       wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]);
+}
+
+static void wm9713_aux_prepare(struct wm97xx *wm)
+{
+       memcpy(wm->dig_save, wm->dig, sizeof(wm->dig));
+       wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG);
+}
+
+static inline int is_pden(struct wm97xx *wm)
+{
+       return wm->dig[2] & WM9713_PDEN;
+}
+
+/*
+ * Read a sample from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample)
+{
+       u16 dig1;
+       int timeout = 5 * delay;
+
+       if (!wm->pen_probably_down) {
+               u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+               if (!(data & WM97XX_PEN_DOWN))
+                       return RC_PENUP;
+               wm->pen_probably_down = 1;
+       }
+
+       /* set up digitiser */
+       if (adcsel & 0x8000)
+               adcsel = 1 << ((adcsel & 0x7fff) + 3);
+
+       dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+       dig1 &= ~WM9713_ADCSEL_MASK;
+
+       if (wm->mach_ops && wm->mach_ops->pre_sample)
+               wm->mach_ops->pre_sample(adcsel);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL);
+
+       /* wait 3 AC97 time slots + delay for conversion */
+       poll_delay(delay);
+
+       /* wait for POLL to go low */
+       while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) &&
+               timeout) {
+               udelay(AC97_LINK_FRAME);
+               timeout--;
+       }
+
+       if (timeout <= 0) {
+               /* If PDEN is set, we can get a timeout when pen goes up */
+               if (is_pden(wm))
+                       wm->pen_probably_down = 0;
+               else
+                       dev_dbg(wm->dev, "adc sample timeout");
+               return RC_PENUP;
+       }
+
+       *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       if (wm->mach_ops && wm->mach_ops->post_sample)
+               wm->mach_ops->post_sample(adcsel);
+
+       /* check we have correct sample */
+       if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) {
+               dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel,
+                       *sample & WM97XX_ADCSRC_MASK);
+               return RC_PENUP;
+       }
+
+       if (!(*sample & WM97XX_PEN_DOWN)) {
+               wm->pen_probably_down = 0;
+               return RC_PENUP;
+       }
+
+       return RC_VALID;
+}
+
+/*
+ * Read a coordinate from the WM9713 adc in polling mode.
+ */
+static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data)
+{
+       u16 dig1;
+       int timeout = 5 * delay;
+
+       if (!wm->pen_probably_down) {
+               u16 val = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+               if (!(val & WM97XX_PEN_DOWN))
+                       return RC_PENUP;
+               wm->pen_probably_down = 1;
+       }
+
+       /* set up digitiser */
+       dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1);
+       dig1 &= ~WM9713_ADCSEL_MASK;
+       if (pil)
+               dig1 |= WM9713_ADCSEL_PRES;
+
+       if (wm->mach_ops && wm->mach_ops->pre_sample)
+               wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG1,
+                        dig1 | WM9713_POLL | WM9713_COO);
+
+       /* wait 3 AC97 time slots + delay for conversion */
+       poll_delay(delay);
+       data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       /* wait for POLL to go low */
+       while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL)
+              && timeout) {
+               udelay(AC97_LINK_FRAME);
+               timeout--;
+       }
+
+       if (timeout <= 0) {
+               /* If PDEN is set, we can get a timeout when pen goes up */
+               if (is_pden(wm))
+                       wm->pen_probably_down = 0;
+               else
+                       dev_dbg(wm->dev, "adc sample timeout");
+               return RC_PENUP;
+       }
+
+       /* read back data */
+       data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       if (pil)
+               data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD);
+       else
+               data->p = DEFAULT_PRESSURE;
+
+       if (wm->mach_ops && wm->mach_ops->post_sample)
+               wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y);
+
+       /* check we have correct sample */
+       if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y))
+               goto err;
+       if (pil && !(data->p & WM97XX_ADCSEL_PRES))
+               goto err;
+
+       if (!(data->x & WM97XX_PEN_DOWN) || !(data->y & WM97XX_PEN_DOWN)) {
+               wm->pen_probably_down = 0;
+               return RC_PENUP;
+       }
+       return RC_VALID;
+err:
+       return 0;
+}
+
+/*
+ * Sample the WM9713 touchscreen in polling mode
+ */
+static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data)
+{
+       int rc;
+
+       if (coord) {
+               rc = wm9713_poll_coord(wm, data);
+               if (rc != RC_VALID)
+                       return rc;
+       } else {
+               rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x);
+               if (rc != RC_VALID)
+                       return rc;
+               rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y);
+               if (rc != RC_VALID)
+                       return rc;
+               if (pil) {
+                       rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES,
+                                               &data->p);
+                       if (rc != RC_VALID)
+                               return rc;
+               } else
+                       data->p = DEFAULT_PRESSURE;
+       }
+       return RC_VALID;
+}
+
+/*
+ * Enable WM9713 continuous mode, i.e. touch data is streamed across
+ * an AC97 slot
+ */
+static int wm9713_acc_enable(struct wm97xx *wm, int enable)
+{
+       u16 dig1, dig2, dig3;
+       int ret = 0;
+
+       dig1 = wm->dig[0];
+       dig2 = wm->dig[1];
+       dig3 = wm->dig[2];
+
+       if (enable) {
+               /* continous mode */
+               if (wm->mach_ops->acc_startup &&
+                       (ret = wm->mach_ops->acc_startup(wm)) < 0)
+                       return ret;
+
+               dig1 &= ~WM9713_ADCSEL_MASK;
+               dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X |
+                       WM9713_ADCSEL_Y;
+               if (pil)
+                       dig1 |= WM9713_ADCSEL_PRES;
+               dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK  |
+                       WM97XX_CM_RATE_MASK);
+               dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) |
+               WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate);
+               dig3 |= WM9713_PDEN;
+       } else {
+               dig1 &= ~(WM9713_CTC | WM9713_COO);
+               dig2 &= ~WM97XX_SLEN;
+               dig3 &= ~WM9713_PDEN;
+               if (wm->mach_ops->acc_shutdown)
+                       wm->mach_ops->acc_shutdown(wm);
+       }
+
+       wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3);
+
+       return ret;
+}
+
+struct wm97xx_codec_drv wm9713_codec = {
+       .id = WM9713_ID2,
+       .name = "wm9713",
+       .poll_sample = wm9713_poll_sample,
+       .poll_touch = wm9713_poll_touch,
+       .acc_enable = wm9713_acc_enable,
+       .phy_init = wm9713_phy_init,
+       .dig_enable = wm9713_dig_enable,
+       .dig_restore = wm9713_dig_restore,
+       .aux_prepare = wm9713_aux_prepare,
+};
+EXPORT_SYMBOL_GPL(wm9713_codec);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM9713 Touch Screen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c
new file mode 100644 (file)
index 0000000..e9c7ea4
--- /dev/null
@@ -0,0 +1,829 @@
+/*
+ * wm97xx-core.c  --  Touch screen driver core for Wolfson WM9705, WM9712
+ *                    and WM9713 AC97 Codecs.
+ *
+ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood
+ *         liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Parts Copyright : Ian Molton <spyro@f2s.com>
+ *                   Andrew Zabolotny <zap@homelink.ru>
+ *                   Russell King <rmk@arm.linux.org.uk>
+ *
+ *  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:
+ *
+ *  Features:
+ *       - supports WM9705, WM9712, WM9713
+ *       - polling mode
+ *       - continuous mode (arch-dependent)
+ *       - adjustable rpu/dpp settings
+ *       - adjustable pressure current
+ *       - adjustable sample settle delay
+ *       - 4 and 5 wire touchscreens (5 wire is WM9712 only)
+ *       - pen down detection
+ *       - battery monitor
+ *       - sample AUX adcs
+ *       - power management
+ *       - codec GPIO
+ *       - codec event notification
+ * Todo
+ *       - Support for async sampling control for noisy LCDs.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <linux/pm.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/workqueue.h>
+#include <linux/wm97xx.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#define TS_NAME                        "wm97xx"
+#define WM_CORE_VERSION                "1.00"
+#define DEFAULT_PRESSURE       0xb0c0
+
+
+/*
+ * Touchscreen absolute values
+ *
+ * These parameters are used to help the input layer discard out of
+ * range readings and reduce jitter etc.
+ *
+ *   o min, max:- indicate the min and max values your touch screen returns
+ *   o fuzz:- use a higher number to reduce jitter
+ *
+ * The default values correspond to Mainstone II in QVGA mode
+ *
+ * Please read
+ * Documentation/input/input-programming.txt for more details.
+ */
+
+static int abs_x[3] = {350, 3900, 5};
+module_param_array(abs_x, int, NULL, 0);
+MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz");
+
+static int abs_y[3] = {320, 3750, 40};
+module_param_array(abs_y, int, NULL, 0);
+MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz");
+
+static int abs_p[3] = {0, 150, 4};
+module_param_array(abs_p, int, NULL, 0);
+MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz");
+
+/*
+ * wm97xx IO access, all IO locking done by AC97 layer
+ */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg)
+{
+       if (wm->ac97)
+               return wm->ac97->bus->ops->read(wm->ac97, reg);
+       else
+               return -1;
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_read);
+
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val)
+{
+       /* cache digitiser registers */
+       if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3)
+               wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val;
+
+       /* cache gpio regs */
+       if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE)
+               wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val;
+
+       /* wm9713 irq reg */
+       if (reg == 0x5a)
+               wm->misc = val;
+
+       if (wm->ac97)
+               wm->ac97->bus->ops->write(wm->ac97, reg, val);
+}
+EXPORT_SYMBOL_GPL(wm97xx_reg_write);
+
+/**
+ * wm97xx_read_aux_adc - Read the aux adc.
+ * @wm: wm97xx device.
+ * @adcsel: codec ADC to be read
+ *
+ * Reads the selected AUX ADC.
+ */
+
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
+{
+       int power_adc = 0, auxval;
+       u16 power = 0;
+
+       /* get codec */
+       mutex_lock(&wm->codec_mutex);
+
+       /* When the touchscreen is not in use, we may have to power up
+        * the AUX ADC before we can use sample the AUX inputs->
+        */
+       if (wm->id == WM9713_ID2 &&
+           (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) {
+               power_adc = 1;
+               wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff);
+       }
+
+       /* Prepare the codec for AUX reading */
+       wm->codec->aux_prepare(wm);
+
+       /* Turn polling mode on to read AUX ADC */
+       wm->pen_probably_down = 1;
+       wm->codec->poll_sample(wm, adcsel, &auxval);
+
+       if (power_adc)
+               wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
+
+       wm->codec->dig_restore(wm);
+
+       wm->pen_probably_down = 0;
+
+       mutex_unlock(&wm->codec_mutex);
+       return auxval & 0xfff;
+}
+EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
+
+/**
+ * wm97xx_get_gpio - Get the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ * Get the status of a codec GPIO pin
+ */
+
+enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio)
+{
+       u16 status;
+       enum wm97xx_gpio_status ret;
+
+       mutex_lock(&wm->codec_mutex);
+       status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+       if (status & gpio)
+               ret = WM97XX_GPIO_HIGH;
+       else
+               ret = WM97XX_GPIO_LOW;
+
+       mutex_unlock(&wm->codec_mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(wm97xx_get_gpio);
+
+/**
+ * wm97xx_set_gpio - Set the status of a codec GPIO.
+ * @wm: wm97xx device.
+ * @gpio: gpio
+ *
+ *
+ * Set the status of a codec GPIO pin
+ */
+
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+                               enum wm97xx_gpio_status status)
+{
+       u16 reg;
+
+       mutex_lock(&wm->codec_mutex);
+       reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+
+       if (status & WM97XX_GPIO_HIGH)
+               reg |= gpio;
+       else
+               reg &= ~gpio;
+
+       if (wm->id == WM9712_ID2)
+               wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1);
+       else
+               wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg);
+       mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_gpio);
+
+/*
+ * Codec GPIO pin configuration, this sets pin direction, polarity,
+ * stickyness and wake up.
+ */
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir,
+                  enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky,
+                  enum wm97xx_gpio_wake wake)
+{
+       u16 reg;
+
+       mutex_lock(&wm->codec_mutex);
+       reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+       if (pol == WM97XX_GPIO_POL_HIGH)
+               reg |= gpio;
+       else
+               reg &= ~gpio;
+
+       wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg);
+       reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+
+       if (sticky == WM97XX_GPIO_STICKY)
+               reg |= gpio;
+       else
+               reg &= ~gpio;
+
+       wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg);
+       reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+
+       if (wake == WM97XX_GPIO_WAKE)
+               reg |= gpio;
+       else
+               reg &= ~gpio;
+
+       wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg);
+       reg = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+
+       if (dir == WM97XX_GPIO_IN)
+               reg |= gpio;
+       else
+               reg &= ~gpio;
+
+       wm97xx_reg_write(wm, AC97_GPIO_CFG, reg);
+       mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_config_gpio);
+
+/*
+ * Configure the WM97XX_PRP value to use while system is suspended.
+ * If a value other than 0 is set then WM97xx pen detection will be
+ * left enabled in the configured mode while the system is in suspend,
+ * the device has users and suspend has not been disabled via the
+ * wakeup sysfs entries.
+ *
+ * @wm:   WM97xx device to configure
+ * @mode: WM97XX_PRP value to configure while suspended
+ */
+void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode)
+{
+       wm->suspend_mode = mode;
+       device_init_wakeup(&wm->input_dev->dev, mode != 0);
+}
+EXPORT_SYMBOL_GPL(wm97xx_set_suspend_mode);
+
+/*
+ * Handle a pen down interrupt.
+ */
+static void wm97xx_pen_irq_worker(struct work_struct *work)
+{
+       struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work);
+       int pen_was_down = wm->pen_is_down;
+
+       /* do we need to enable the touch panel reader */
+       if (wm->id == WM9705_ID2) {
+               if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) &
+                                       WM97XX_PEN_DOWN)
+                       wm->pen_is_down = 1;
+               else
+                       wm->pen_is_down = 0;
+       } else {
+               u16 status, pol;
+               mutex_lock(&wm->codec_mutex);
+               status = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+               pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+
+               if (WM97XX_GPIO_13 & pol & status) {
+                       wm->pen_is_down = 1;
+                       wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol &
+                                               ~WM97XX_GPIO_13);
+               } else {
+                       wm->pen_is_down = 0;
+                       wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol |
+                                        WM97XX_GPIO_13);
+               }
+
+               if (wm->id == WM9712_ID2)
+                       wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status &
+                                               ~WM97XX_GPIO_13) << 1);
+               else
+                       wm97xx_reg_write(wm, AC97_GPIO_STATUS, status &
+                                               ~WM97XX_GPIO_13);
+               mutex_unlock(&wm->codec_mutex);
+       }
+
+       /* If the system is not using continuous mode or it provides a
+        * pen down operation then we need to schedule polls while the
+        * pen is down.  Otherwise the machine driver is responsible
+        * for scheduling reads.
+        */
+       if (!wm->mach_ops->acc_enabled || wm->mach_ops->acc_pen_down) {
+               if (wm->pen_is_down && !pen_was_down) {
+                       /* Data is not availiable immediately on pen down */
+                       queue_delayed_work(wm->ts_workq, &wm->ts_reader, 1);
+               }
+
+               /* Let ts_reader report the pen up for debounce. */
+               if (!wm->pen_is_down && pen_was_down)
+                       wm->pen_is_down = 1;
+       }
+
+       if (!wm->pen_is_down && wm->mach_ops->acc_enabled)
+               wm->mach_ops->acc_pen_up(wm);
+
+       wm->mach_ops->irq_enable(wm, 1);
+}
+
+/*
+ * Codec PENDOWN irq handler
+ *
+ * We have to disable the codec interrupt in the handler because it
+ * can take upto 1ms to clear the interrupt source. We schedule a task
+ * in a work queue to do the actual interaction with the chip.  The
+ * interrupt is then enabled again in the slow handler when the source
+ * has been cleared.
+ */
+static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id)
+{
+       struct wm97xx *wm = dev_id;
+
+       if (!work_pending(&wm->pen_event_work)) {
+               wm->mach_ops->irq_enable(wm, 0);
+               queue_work(wm->ts_workq, &wm->pen_event_work);
+       }
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * initialise pen IRQ handler and workqueue
+ */
+static int wm97xx_init_pen_irq(struct wm97xx *wm)
+{
+       u16 reg;
+
+       /* If an interrupt is supplied an IRQ enable operation must also be
+        * provided. */
+       BUG_ON(!wm->mach_ops->irq_enable);
+
+       if (request_irq(wm->pen_irq, wm97xx_pen_interrupt,
+                       IRQF_SHARED | IRQF_SAMPLE_RANDOM,
+                       "wm97xx-pen", wm)) {
+               dev_err(wm->dev,
+                       "Failed to register pen down interrupt, polling");
+               wm->pen_irq = 0;
+               return -EINVAL;
+       }
+
+       /* Configure GPIO as interrupt source on WM971x */
+       if (wm->id != WM9705_ID2) {
+               BUG_ON(!wm->mach_ops->irq_gpio);
+               reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+               wm97xx_reg_write(wm, AC97_MISC_AFE,
+                               reg & ~(wm->mach_ops->irq_gpio));
+               reg = wm97xx_reg_read(wm, 0x5a);
+               wm97xx_reg_write(wm, 0x5a, reg & ~0x0001);
+       }
+
+       return 0;
+}
+
+static int wm97xx_read_samples(struct wm97xx *wm)
+{
+       struct wm97xx_data data;
+       int rc;
+
+       mutex_lock(&wm->codec_mutex);
+
+       if (wm->mach_ops && wm->mach_ops->acc_enabled)
+               rc = wm->mach_ops->acc_pen_down(wm);
+       else
+               rc = wm->codec->poll_touch(wm, &data);
+
+       if (rc & RC_PENUP) {
+               if (wm->pen_is_down) {
+                       wm->pen_is_down = 0;
+                       dev_dbg(wm->dev, "pen up\n");
+                       input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
+                       input_sync(wm->input_dev);
+               } else if (!(rc & RC_AGAIN)) {
+                       /* We need high frequency updates only while
+                       * pen is down, the user never will be able to
+                       * touch screen faster than a few times per
+                       * second... On the other hand, when the user
+                       * is actively working with the touchscreen we
+                       * don't want to lose the quick response. So we
+                       * will slowly increase sleep time after the
+                       * pen is up and quicky restore it to ~one task
+                       * switch when pen is down again.
+                       */
+                       if (wm->ts_reader_interval < HZ / 10)
+                               wm->ts_reader_interval++;
+               }
+
+       } else if (rc & RC_VALID) {
+               dev_dbg(wm->dev,
+                       "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n",
+                       data.x >> 12, data.x & 0xfff, data.y >> 12,
+                       data.y & 0xfff, data.p >> 12, data.p & 0xfff);
+               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_sync(wm->input_dev);
+               wm->pen_is_down = 1;
+               wm->ts_reader_interval = wm->ts_reader_min_interval;
+       } else if (rc & RC_PENDOWN) {
+               dev_dbg(wm->dev, "pen down\n");
+               wm->pen_is_down = 1;
+               wm->ts_reader_interval = wm->ts_reader_min_interval;
+       }
+
+       mutex_unlock(&wm->codec_mutex);
+       return rc;
+}
+
+/*
+* The touchscreen sample reader.
+*/
+static void wm97xx_ts_reader(struct work_struct *work)
+{
+       int rc;
+       struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work);
+
+       BUG_ON(!wm->codec);
+
+       do {
+               rc = wm97xx_read_samples(wm);
+       } while (rc & RC_AGAIN);
+
+       if (wm->pen_is_down || !wm->pen_irq)
+               queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+                                  wm->ts_reader_interval);
+}
+
+/**
+ * wm97xx_ts_input_open - Open the touch screen input device.
+ * @idev:      Input device to be opened.
+ *
+ * Called by the input sub system to open a wm97xx touchscreen device.
+ * Starts the touchscreen thread and touch digitiser.
+ */
+static int wm97xx_ts_input_open(struct input_dev *idev)
+{
+       struct wm97xx *wm = input_get_drvdata(idev);
+
+       wm->ts_workq = create_singlethread_workqueue("kwm97xx");
+       if (wm->ts_workq == NULL) {
+               dev_err(wm->dev,
+                       "Failed to create workqueue\n");
+               return -EINVAL;
+       }
+
+       /* start digitiser */
+       if (wm->mach_ops && wm->mach_ops->acc_enabled)
+               wm->codec->acc_enable(wm, 1);
+       wm->codec->dig_enable(wm, 1);
+
+       INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader);
+       INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker);
+
+       wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1;
+       if (wm->ts_reader_min_interval < 1)
+               wm->ts_reader_min_interval = 1;
+       wm->ts_reader_interval = wm->ts_reader_min_interval;
+
+       wm->pen_is_down = 0;
+       if (wm->pen_irq)
+               wm97xx_init_pen_irq(wm);
+       else
+               dev_err(wm->dev, "No IRQ specified\n");
+
+       /* If we either don't have an interrupt for pen down events or
+        * failed to acquire it then we need to poll.
+        */
+       if (wm->pen_irq == 0)
+               queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+                                  wm->ts_reader_interval);
+
+       return 0;
+}
+
+/**
+ * wm97xx_ts_input_close - Close the touch screen input device.
+ * @idev:      Input device to be closed.
+ *
+ * Called by the input sub system to close a wm97xx touchscreen
+ * device.  Kills the touchscreen thread and stops the touch
+ * digitiser.
+ */
+
+static void wm97xx_ts_input_close(struct input_dev *idev)
+{
+       struct wm97xx *wm = input_get_drvdata(idev);
+       u16 reg;
+
+       if (wm->pen_irq) {
+               /* Return the interrupt to GPIO usage (disabling it) */
+               if (wm->id != WM9705_ID2) {
+                       BUG_ON(!wm->mach_ops->irq_gpio);
+                       reg = wm97xx_reg_read(wm, AC97_MISC_AFE);
+                       wm97xx_reg_write(wm, AC97_MISC_AFE,
+                                        reg | wm->mach_ops->irq_gpio);
+               }
+
+               free_irq(wm->pen_irq, wm);
+       }
+
+       wm->pen_is_down = 0;
+
+       /* Balance out interrupt disables/enables */
+       if (cancel_work_sync(&wm->pen_event_work))
+               wm->mach_ops->irq_enable(wm, 1);
+
+       /* ts_reader rearms itself so we need to explicitly stop it
+        * before we destroy the workqueue.
+        */
+       cancel_delayed_work_sync(&wm->ts_reader);
+
+       destroy_workqueue(wm->ts_workq);
+
+       /* stop digitiser */
+       wm->codec->dig_enable(wm, 0);
+       if (wm->mach_ops && wm->mach_ops->acc_enabled)
+               wm->codec->acc_enable(wm, 0);
+}
+
+static int wm97xx_probe(struct device *dev)
+{
+       struct wm97xx *wm;
+       int ret = 0, id = 0;
+
+       wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
+       if (!wm)
+               return -ENOMEM;
+       mutex_init(&wm->codec_mutex);
+
+       wm->dev = dev;
+       dev->driver_data = wm;
+       wm->ac97 = to_ac97_t(dev);
+
+       /* check that we have a supported codec */
+       id = wm97xx_reg_read(wm, AC97_VENDOR_ID1);
+       if (id != WM97XX_ID1) {
+               dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id);
+               ret = -ENODEV;
+               goto alloc_err;
+       }
+
+       wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2);
+
+       dev_info(wm->dev, "detected a wm97%02x codec\n", wm->id & 0xff);
+
+       switch (wm->id & 0xff) {
+#ifdef CONFIG_TOUCHSCREEN_WM9705
+       case 0x05:
+               wm->codec = &wm9705_codec;
+               break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9712
+       case 0x12:
+               wm->codec = &wm9712_codec;
+               break;
+#endif
+#ifdef CONFIG_TOUCHSCREEN_WM9713
+       case 0x13:
+               wm->codec = &wm9713_codec;
+               break;
+#endif
+       default:
+               dev_err(wm->dev, "Support for wm97%02x not compiled in.\n",
+                       wm->id & 0xff);
+               ret = -ENODEV;
+               goto alloc_err;
+       }
+
+       wm->input_dev = input_allocate_device();
+       if (wm->input_dev == NULL) {
+               ret = -ENOMEM;
+               goto alloc_err;
+       }
+
+       /* set up touch configuration */
+       wm->input_dev->name = "wm97xx touchscreen";
+       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);
+       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;
+
+       /* set up physical characteristics */
+       wm->codec->phy_init(wm);
+
+       /* load gpio cache */
+       wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG);
+       wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY);
+       wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY);
+       wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP);
+       wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS);
+       wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE);
+
+       /* register our battery device */
+       wm->battery_dev = platform_device_alloc("wm97xx-battery", -1);
+       if (!wm->battery_dev) {
+               ret = -ENOMEM;
+               goto batt_err;
+       }
+       platform_set_drvdata(wm->battery_dev, wm);
+       wm->battery_dev->dev.parent = dev;
+       ret = platform_device_add(wm->battery_dev);
+       if (ret < 0)
+               goto batt_reg_err;
+
+       /* register our extended touch device (for machine specific
+        * extensions) */
+       wm->touch_dev = platform_device_alloc("wm97xx-touch", -1);
+       if (!wm->touch_dev) {
+               ret = -ENOMEM;
+               goto touch_err;
+       }
+       platform_set_drvdata(wm->touch_dev, wm);
+       wm->touch_dev->dev.parent = dev;
+       ret = platform_device_add(wm->touch_dev);
+       if (ret < 0)
+               goto touch_reg_err;
+
+       return ret;
+
+ touch_reg_err:
+       platform_device_put(wm->touch_dev);
+ touch_err:
+       platform_device_unregister(wm->battery_dev);
+       wm->battery_dev = NULL;
+ batt_reg_err:
+       platform_device_put(wm->battery_dev);
+ batt_err:
+       input_unregister_device(wm->input_dev);
+       wm->input_dev = NULL;
+ dev_alloc_err:
+       input_free_device(wm->input_dev);
+ alloc_err:
+       kfree(wm);
+
+       return ret;
+}
+
+static int wm97xx_remove(struct device *dev)
+{
+       struct wm97xx *wm = dev_get_drvdata(dev);
+
+       platform_device_unregister(wm->battery_dev);
+       platform_device_unregister(wm->touch_dev);
+       input_unregister_device(wm->input_dev);
+       kfree(wm);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int wm97xx_suspend(struct device *dev, pm_message_t state)
+{
+       struct wm97xx *wm = dev_get_drvdata(dev);
+       u16 reg;
+       int suspend_mode;
+
+       if (device_may_wakeup(&wm->input_dev->dev))
+               suspend_mode = wm->suspend_mode;
+       else
+               suspend_mode = 0;
+
+       if (wm->input_dev->users)
+               cancel_delayed_work_sync(&wm->ts_reader);
+
+       /* Power down the digitiser (bypassing the cache for resume) */
+       reg = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER2);
+       reg &= ~WM97XX_PRP_DET_DIG;
+       if (wm->input_dev->users)
+               reg |= suspend_mode;
+       wm->ac97->bus->ops->write(wm->ac97, AC97_WM97XX_DIGITISER2, reg);
+
+       /* WM9713 has an additional power bit - turn it off if there
+        * are no users or if suspend mode is zero. */
+       if (wm->id == WM9713_ID2 &&
+           (!wm->input_dev->users || !suspend_mode)) {
+               reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) | 0x8000;
+               wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+       }
+
+       return 0;
+}
+
+static int wm97xx_resume(struct device *dev)
+{
+       struct wm97xx *wm = dev_get_drvdata(dev);
+
+       /* restore digitiser and gpios */
+       if (wm->id == WM9713_ID2) {
+               wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]);
+               wm97xx_reg_write(wm, 0x5a, wm->misc);
+               if (wm->input_dev->users) {
+                       u16 reg;
+                       reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff;
+                       wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg);
+               }
+       }
+
+       wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]);
+       wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]);
+
+       wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]);
+       wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]);
+       wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]);
+       wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]);
+       wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]);
+       wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]);
+
+       if (wm->input_dev->users && !wm->pen_irq) {
+               wm->ts_reader_interval = wm->ts_reader_min_interval;
+               queue_delayed_work(wm->ts_workq, &wm->ts_reader,
+                                  wm->ts_reader_interval);
+       }
+
+       return 0;
+}
+
+#else
+#define wm97xx_suspend         NULL
+#define wm97xx_resume          NULL
+#endif
+
+/*
+ * Machine specific operations
+ */
+int wm97xx_register_mach_ops(struct wm97xx *wm,
+                            struct wm97xx_mach_ops *mach_ops)
+{
+       mutex_lock(&wm->codec_mutex);
+       if (wm->mach_ops) {
+               mutex_unlock(&wm->codec_mutex);
+               return -EINVAL;
+       }
+       wm->mach_ops = mach_ops;
+       mutex_unlock(&wm->codec_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops);
+
+void wm97xx_unregister_mach_ops(struct wm97xx *wm)
+{
+       mutex_lock(&wm->codec_mutex);
+       wm->mach_ops = NULL;
+       mutex_unlock(&wm->codec_mutex);
+}
+EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops);
+
+static struct device_driver wm97xx_driver = {
+       .name =         "ac97",
+       .bus =          &ac97_bus_type,
+       .owner =        THIS_MODULE,
+       .probe =        wm97xx_probe,
+       .remove =       wm97xx_remove,
+       .suspend =      wm97xx_suspend,
+       .resume =       wm97xx_resume,
+};
+
+static int __init wm97xx_init(void)
+{
+       return driver_register(&wm97xx_driver);
+}
+
+static void __exit wm97xx_exit(void)
+{
+       driver_unregister(&wm97xx_driver);
+}
+
+module_init(wm97xx_init);
+module_exit(wm97xx_exit);
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>");
+MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver");
+MODULE_LICENSE("GPL");
index 8930230..f972ff3 100644 (file)
@@ -103,6 +103,9 @@ int mac_hid_mouse_emulate_buttons(int caller, unsigned int keycode, int down)
        return 0;
 }
 
+static struct lock_class_key emumousebtn_event_class;
+static struct lock_class_key emumousebtn_mutex_class;
+
 static int emumousebtn_input_register(void)
 {
        int ret;
@@ -111,6 +114,9 @@ static int emumousebtn_input_register(void)
        if (!emumousebtn)
                return -ENOMEM;
 
+       lockdep_set_class(emumousebtn->event_lock, &emumousebtn_event_class);
+       lockdep_set_class(emumousebtn->mutex, &emumousebtn_mutex_class);
+
        emumousebtn->name = "Macintosh mouse button emulation";
        emumousebtn->id.bustype = BUS_ADB;
        emumousebtn->id.vendor = 0x0001;
index cae2c35..28a094f 100644 (file)
@@ -1025,10 +1025,6 @@ struct ff_effect {
  * @node: used to place the device onto input_dev_list
  */
 struct input_dev {
-       /* private: */
-       void *private;  /* do not use */
-       /* public: */
-
        const char *name;
        const char *phys;
        const char *uniq;
@@ -1238,12 +1234,12 @@ static inline void input_put_device(struct input_dev *dev)
 
 static inline void *input_get_drvdata(struct input_dev *dev)
 {
-       return dev->private;
+       return dev_get_drvdata(&dev->dev);
 }
 
 static inline void input_set_drvdata(struct input_dev *dev, void *data)
 {
-       dev->private = data;
+       dev_set_drvdata(&dev->dev, data);
 }
 
 int __must_check input_register_device(struct input_dev *);
index 65c2d70..a3c984d 100644 (file)
@@ -33,6 +33,7 @@ struct keyboard_notifier_param {
        struct vc_data *vc;     /* VC on which the keyboard press was done */
        int down;               /* Pressure of the key? */
        int shift;              /* Current shift mask */
+       int ledstate;           /* Current led state */
        unsigned int value;     /* keycode, unicode value or keysym */
 };
 
index 9f38250..95674d9 100644 (file)
@@ -211,5 +211,6 @@ static inline void serio_unpin_driver(struct serio *serio)
 #define SERIO_TOUCHWIN 0x33
 #define SERIO_TAOSEVM  0x34
 #define SERIO_FUJITSU  0x35
+#define SERIO_ZHENHUA  0x36
 
 #endif
index 334d314..daf7440 100644 (file)
@@ -14,7 +14,8 @@ enum ads7846_filter {
 struct ads7846_platform_data {
        u16     model;                  /* 7843, 7845, 7846. */
        u16     vref_delay_usecs;       /* 0 for external vref; etc */
-       int     keep_vref_on:1;         /* set to keep vref on for differential
+       u16     vref_mv;                /* external vref value, milliVolts */
+       bool    keep_vref_on;           /* set to keep vref on for differential
                                         * measurements as well */
 
        /* Settling time of the analog signals; a function of Vcc and the
diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h
new file mode 100644 (file)
index 0000000..4d13732
--- /dev/null
@@ -0,0 +1,314 @@
+
+/*
+ * Register bits and API for Wolfson WM97xx series of codecs
+ */
+
+#ifndef _LINUX_WM97XX_H
+#define _LINUX_WM97XX_H
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/ac97_codec.h>
+#include <sound/initval.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/input.h>       /* Input device layer */
+#include <linux/platform_device.h>
+
+/*
+ * WM97xx AC97 Touchscreen registers
+ */
+#define AC97_WM97XX_DIGITISER1         0x76
+#define AC97_WM97XX_DIGITISER2         0x78
+#define AC97_WM97XX_DIGITISER_RD       0x7a
+#define AC97_WM9713_DIG1               0x74
+#define AC97_WM9713_DIG2               AC97_WM97XX_DIGITISER1
+#define AC97_WM9713_DIG3               AC97_WM97XX_DIGITISER2
+
+/*
+ * WM97xx register bits
+ */
+#define WM97XX_POLL            0x8000  /* initiate a polling measurement */
+#define WM97XX_ADCSEL_X                0x1000  /* x coord measurement */
+#define WM97XX_ADCSEL_Y                0x2000  /* y coord measurement */
+#define WM97XX_ADCSEL_PRES     0x3000  /* pressure measurement */
+#define WM97XX_ADCSEL_MASK     0x7000
+#define WM97XX_COO             0x0800  /* enable coordinate mode */
+#define WM97XX_CTC             0x0400  /* enable continuous mode */
+#define WM97XX_CM_RATE_93      0x0000  /* 93.75Hz continuous rate */
+#define WM97XX_CM_RATE_187     0x0100  /* 187.5Hz continuous rate */
+#define WM97XX_CM_RATE_375     0x0200  /* 375Hz continuous rate */
+#define WM97XX_CM_RATE_750     0x0300  /* 750Hz continuous rate */
+#define WM97XX_CM_RATE_8K      0x00f0  /* 8kHz continuous rate */
+#define WM97XX_CM_RATE_12K     0x01f0  /* 12kHz continuous rate */
+#define WM97XX_CM_RATE_24K     0x02f0  /* 24kHz continuous rate */
+#define WM97XX_CM_RATE_48K     0x03f0  /* 48kHz continuous rate */
+#define WM97XX_CM_RATE_MASK    0x03f0
+#define WM97XX_RATE(i)         (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0))
+#define WM97XX_DELAY(i)                ((i << 4) & 0x00f0)     /* sample delay times */
+#define WM97XX_DELAY_MASK      0x00f0
+#define WM97XX_SLEN            0x0008  /* slot read back enable */
+#define WM97XX_SLT(i)          ((i - 5) & 0x7) /* panel slot (5-11) */
+#define WM97XX_SLT_MASK                0x0007
+#define WM97XX_PRP_DETW                0x4000  /* detect on, digitise off, wake */
+#define WM97XX_PRP_DET         0x8000  /* detect on, digitise off, no wake */
+#define WM97XX_PRP_DET_DIG     0xc000  /* setect on, digitise on */
+#define WM97XX_RPR             0x2000  /* wake up on pen down */
+#define WM97XX_PEN_DOWN                0x8000  /* pen is down */
+#define WM97XX_ADCSRC_MASK     0x7000  /* ADC source mask */
+
+#define WM97XX_AUX_ID1         0x8001
+#define WM97XX_AUX_ID2         0x8002
+#define WM97XX_AUX_ID3         0x8003
+#define WM97XX_AUX_ID4         0x8004
+
+
+/* WM9712 Bits */
+#define WM9712_45W             0x1000  /* set for 5-wire touchscreen */
+#define WM9712_PDEN            0x0800  /* measure only when pen down */
+#define WM9712_WAIT            0x0200  /* wait until adc is read before next sample */
+#define WM9712_PIL             0x0100  /* current used for pressure measurement. set 400uA else 200uA */
+#define WM9712_MASK_HI         0x0040  /* hi on mask pin (47) stops conversions */
+#define WM9712_MASK_EDGE       0x0080  /* rising/falling edge on pin delays sample */
+#define        WM9712_MASK_SYNC        0x00c0  /* rising/falling edge on mask initiates sample */
+#define WM9712_RPU(i)          (i&0x3f)        /* internal pull up on pen detect (64k / rpu) */
+#define WM9712_PD(i)           (0x1 << i)      /* power management */
+
+/* WM9712 Registers */
+#define AC97_WM9712_POWER      0x24
+#define AC97_WM9712_REV                0x58
+
+/* WM9705 Bits */
+#define WM9705_PDEN            0x1000  /* measure only when pen is down */
+#define WM9705_PINV            0x0800  /* inverts sense of pen down output */
+#define WM9705_BSEN            0x0400  /* BUSY flag enable, pin47 is 1 when busy */
+#define WM9705_BINV            0x0200  /* invert BUSY (pin47) output */
+#define WM9705_WAIT            0x0100  /* wait until adc is read before next sample */
+#define WM9705_PIL             0x0080  /* current used for pressure measurement. set 400uA else 200uA */
+#define WM9705_PHIZ            0x0040  /* set PHONE and PCBEEP inputs to high impedance */
+#define WM9705_MASK_HI         0x0010  /* hi on mask stops conversions */
+#define WM9705_MASK_EDGE       0x0020  /* rising/falling edge on pin delays sample */
+#define        WM9705_MASK_SYNC        0x0030  /* rising/falling edge on mask initiates sample */
+#define WM9705_PDD(i)          (i & 0x000f)    /* pen detect comparator threshold */
+
+
+/* WM9713 Bits */
+#define WM9713_PDPOL           0x0400  /* Pen down polarity */
+#define WM9713_POLL            0x0200  /* initiate a polling measurement */
+#define WM9713_CTC             0x0100  /* enable continuous mode */
+#define WM9713_ADCSEL_X                0x0002  /* X measurement */
+#define WM9713_ADCSEL_Y                0x0004  /* Y measurement */
+#define WM9713_ADCSEL_PRES     0x0008  /* Pressure measurement */
+#define WM9713_COO             0x0001  /* enable coordinate mode */
+#define WM9713_PDEN            0x0800  /* measure only when pen down */
+#define WM9713_ADCSEL_MASK     0x00fe  /* ADC selection mask */
+#define WM9713_WAIT            0x0200  /* coordinate wait */
+
+/* AUX ADC ID's */
+#define TS_COMP1               0x0
+#define TS_COMP2               0x1
+#define TS_BMON                        0x2
+#define TS_WIPER               0x3
+
+/* ID numbers */
+#define WM97XX_ID1             0x574d
+#define WM9712_ID2             0x4c12
+#define WM9705_ID2             0x4c05
+#define WM9713_ID2             0x4c13
+
+/* Codec GPIO's */
+#define WM97XX_MAX_GPIO                16
+#define WM97XX_GPIO_1          (1 << 1)
+#define WM97XX_GPIO_2          (1 << 2)
+#define WM97XX_GPIO_3          (1 << 3)
+#define WM97XX_GPIO_4          (1 << 4)
+#define WM97XX_GPIO_5          (1 << 5)
+#define WM97XX_GPIO_6          (1 << 6)
+#define WM97XX_GPIO_7          (1 << 7)
+#define WM97XX_GPIO_8          (1 << 8)
+#define WM97XX_GPIO_9          (1 << 9)
+#define WM97XX_GPIO_10         (1 << 10)
+#define WM97XX_GPIO_11         (1 << 11)
+#define WM97XX_GPIO_12         (1 << 12)
+#define WM97XX_GPIO_13         (1 << 13)
+#define WM97XX_GPIO_14         (1 << 14)
+#define WM97XX_GPIO_15         (1 << 15)
+
+
+#define AC97_LINK_FRAME                21      /* time in uS for AC97 link frame */
+
+
+/*---------------- Return codes from sample reading functions ---------------*/
+
+/* More data is available; call the sample gathering function again */
+#define RC_AGAIN                       0x00000001
+/* The returned sample is valid */
+#define RC_VALID                       0x00000002
+/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */
+#define RC_PENUP                       0x00000004
+/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful
+   to tell the handler that the pen is down but we don't know yet his coords,
+   so the handler should not sleep or wait for pendown irq) */
+#define RC_PENDOWN                     0x00000008
+
+/*
+ * The wm97xx driver provides a private API for writing platform-specific
+ * drivers.
+ */
+
+/* The structure used to return arch specific sampled data into */
+struct wm97xx_data {
+    int x;
+    int y;
+    int p;
+};
+
+/*
+ * Codec GPIO status
+ */
+enum wm97xx_gpio_status {
+    WM97XX_GPIO_HIGH,
+    WM97XX_GPIO_LOW
+};
+
+/*
+ * Codec GPIO direction
+ */
+enum wm97xx_gpio_dir {
+    WM97XX_GPIO_IN,
+    WM97XX_GPIO_OUT
+};
+
+/*
+ * Codec GPIO polarity
+ */
+enum wm97xx_gpio_pol {
+    WM97XX_GPIO_POL_HIGH,
+    WM97XX_GPIO_POL_LOW
+};
+
+/*
+ * Codec GPIO sticky
+ */
+enum wm97xx_gpio_sticky {
+    WM97XX_GPIO_STICKY,
+    WM97XX_GPIO_NOTSTICKY
+};
+
+/*
+ * Codec GPIO wake
+ */
+enum wm97xx_gpio_wake {
+    WM97XX_GPIO_WAKE,
+    WM97XX_GPIO_NOWAKE
+};
+
+/*
+ * Digitiser ioctl commands
+ */
+#define WM97XX_DIG_START       0x1
+#define WM97XX_DIG_STOP                0x2
+#define WM97XX_PHY_INIT                0x3
+#define WM97XX_AUX_PREPARE     0x4
+#define WM97XX_DIG_RESTORE     0x5
+
+struct wm97xx;
+
+extern struct wm97xx_codec_drv wm9705_codec;
+extern struct wm97xx_codec_drv wm9712_codec;
+extern struct wm97xx_codec_drv wm9713_codec;
+
+/*
+ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs
+ */
+struct wm97xx_codec_drv {
+       u16 id;
+       char *name;
+
+       /* read 1 sample */
+       int (*poll_sample) (struct wm97xx *, int adcsel, int *sample);
+
+       /* read X,Y,[P] in poll */
+       int (*poll_touch) (struct wm97xx *, struct wm97xx_data *);
+
+       int (*acc_enable) (struct wm97xx *, int enable);
+       void (*phy_init) (struct wm97xx *);
+       void (*dig_enable) (struct wm97xx *, int enable);
+       void (*dig_restore) (struct wm97xx *);
+       void (*aux_prepare) (struct wm97xx *);
+};
+
+
+/* Machine specific and accelerated touch operations */
+struct wm97xx_mach_ops {
+
+       /* accelerated touch readback - coords are transmited on AC97 link */
+       int acc_enabled;
+       void (*acc_pen_up) (struct wm97xx *);
+       int (*acc_pen_down) (struct wm97xx *);
+       int (*acc_startup) (struct wm97xx *);
+       void (*acc_shutdown) (struct wm97xx *);
+
+       /* interrupt mask control - required for accelerated operation */
+       void (*irq_enable) (struct wm97xx *, int enable);
+
+       /* GPIO pin used for accelerated operation */
+       int irq_gpio;
+
+       /* pre and post sample - can be used to minimise any analog noise */
+       void (*pre_sample) (int);  /* function to run before sampling */
+       void (*post_sample) (int);  /* function to run after sampling */
+};
+
+struct wm97xx {
+       u16 dig[3], id, gpio[6], misc;  /* Cached codec registers */
+       u16 dig_save[3];                /* saved during aux reading */
+       struct wm97xx_codec_drv *codec; /* attached codec driver*/
+       struct input_dev *input_dev;    /* touchscreen input device */
+       struct snd_ac97 *ac97;          /* ALSA codec access */
+       struct device *dev;             /* ALSA device */
+       struct platform_device *battery_dev;
+       struct platform_device *touch_dev;
+       struct wm97xx_mach_ops *mach_ops;
+       struct mutex codec_mutex;
+       struct delayed_work ts_reader;  /* Used to poll touchscreen */
+       unsigned long ts_reader_interval; /* Current interval for timer */
+       unsigned long ts_reader_min_interval; /* Minimum interval */
+       unsigned int pen_irq;           /* Pen IRQ number in use */
+       struct workqueue_struct *ts_workq;
+       struct work_struct pen_event_work;
+       u16 acc_slot;                   /* AC97 slot used for acc touch data */
+       u16 acc_rate;                   /* acc touch data rate */
+       unsigned pen_is_down:1;         /* Pen is down */
+       unsigned aux_waiting:1;         /* aux measurement waiting */
+       unsigned pen_probably_down:1;   /* used in polling mode */
+       u16 suspend_mode;               /* PRP in suspend mode */
+};
+
+/*
+ * Codec GPIO access (not supported on WM9705)
+ * This can be used to set/get codec GPIO and Virtual GPIO status.
+ */
+enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio);
+void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio,
+                         enum wm97xx_gpio_status status);
+void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio,
+                                    enum wm97xx_gpio_dir dir,
+                                    enum wm97xx_gpio_pol pol,
+                                    enum wm97xx_gpio_sticky sticky,
+                                    enum wm97xx_gpio_wake wake);
+
+void wm97xx_set_suspend_mode(struct wm97xx *wm, u16 mode);
+
+/* codec AC97 IO access */
+int wm97xx_reg_read(struct wm97xx *wm, u16 reg);
+void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val);
+
+/* aux adc readback */
+int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel);
+
+/* machine ops */
+int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *);
+void wm97xx_unregister_mach_ops(struct wm97xx *);
+
+#endif