Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits) Input: wacom - add support for Cintiq 20WSX Input: ucb1400_ts - IRQ probe fix Input: at32psif - update MODULE_AUTHOR with new email Input: mac_hid - add lockdep annotation to emumousebtn Input: i8042 - fix incorrect usage of strncpy and strncat Input: bf54x-keys - add infrastructure for keypad wakeups Input: add MODULE_ALIAS() to hotpluggable platform modules Input: drivers/char/keyboard.c - use time_after Input: fix ordering in joystick Makefile Input: wm97xx-core - support use as a wakeup source Input: wm97xx-core - use IRQF_SAMPLE_RANDOM Input: wm97xx-core - only schedule interrupt handler if not already scheduled Input: add Zhen Hua driver Input: aiptek - add support for Genius G-PEN 560 tablet Input: wacom - implement suspend and autosuspend Input: xpad - set proper buffer length for outgoing requests Input: omap-keypad - fix build warning Input: gpio_keys - irq handling cleanup Input: add PS/2 serio driver for AVR32 devices Input: put ledstate in the keyboard notifier ...
This commit is contained in:
commit
ce1d5b23a8
10
MAINTAINERS
10
MAINTAINERS
|
@ -4356,6 +4356,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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -1238,6 +1240,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, ¶m) == NOTIFY_STOP || !key_map) {
|
||||
|
@ -1286,6 +1289,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, ¶m);
|
||||
|
||||
if (type != KT_SLOCK)
|
||||
|
|
|
@ -405,6 +405,9 @@
|
|||
#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.
|
||||
*/
|
||||
|
@ -698,6 +701,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 }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -87,10 +88,12 @@
|
|||
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,6 +683,7 @@ static void xpad_close(struct input_dev *dev)
|
|||
{
|
||||
struct usb_xpad *xpad = input_get_drvdata(dev);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -393,6 +393,7 @@ static struct platform_driver corgikbd_driver = {
|
|||
.resume = corgikbd_resume,
|
||||
.driver = {
|
||||
.name = "corgi-keyboard",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -412,3 +413,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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -495,3 +495,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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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++;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -184,6 +184,7 @@
|
|||
*/
|
||||
|
||||
#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)},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
wacom->irq->dev = wacom->usbdev;
|
||||
if (usb_submit_urb(wacom->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
mutex_lock(&wacom->lock);
|
||||
|
||||
wacom->irq->dev = wacom->usbdev;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -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) },
|
||||
{ }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -80,6 +80,7 @@ struct ads7846 {
|
|||
#endif
|
||||
|
||||
u16 model;
|
||||
u16 vref_mv;
|
||||
u16 vref_delay_usecs;
|
||||
u16 x_plate_ohms;
|
||||
u16 pressure_max;
|
||||
|
@ -177,9 +178,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;
|
||||
|
@ -206,7 +204,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)
|
||||
|
@ -263,13 +260,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)
|
||||
|
@ -310,7 +307,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;
|
||||
}
|
||||
|
@ -368,14 +365,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);
|
||||
|
@ -868,6 +865,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;
|
||||
|
|
|
@ -362,6 +362,7 @@ static struct platform_driver corgits_driver = {
|
|||
.resume = corgits_resume,
|
||||
.driver = {
|
||||
.name = "corgi-ts",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -381,3 +382,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");
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
err_out:
|
||||
kfree(buf);
|
||||
err_nobuf:
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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;
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue