mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 02:32:09 -04:00
Merge branch 'next' into for-linus
This commit is contained in:
commit
49327ad2bb
75 changed files with 5781 additions and 1845 deletions
|
|
@ -534,6 +534,80 @@ static int handle_eviocgbit(struct input_dev *dev,
|
|||
}
|
||||
#undef OLD_KEY_MAX
|
||||
|
||||
static int evdev_handle_get_keycode(struct input_dev *dev,
|
||||
void __user *p, size_t size)
|
||||
{
|
||||
struct input_keymap_entry ke;
|
||||
int error;
|
||||
|
||||
memset(&ke, 0, sizeof(ke));
|
||||
|
||||
if (size == sizeof(unsigned int[2])) {
|
||||
/* legacy case */
|
||||
int __user *ip = (int __user *)p;
|
||||
|
||||
if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
|
||||
return -EFAULT;
|
||||
|
||||
ke.len = sizeof(unsigned int);
|
||||
ke.flags = 0;
|
||||
|
||||
error = input_get_keycode(dev, &ke);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (put_user(ke.keycode, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
} else {
|
||||
size = min(size, sizeof(ke));
|
||||
|
||||
if (copy_from_user(&ke, p, size))
|
||||
return -EFAULT;
|
||||
|
||||
error = input_get_keycode(dev, &ke);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (copy_to_user(p, &ke, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int evdev_handle_set_keycode(struct input_dev *dev,
|
||||
void __user *p, size_t size)
|
||||
{
|
||||
struct input_keymap_entry ke;
|
||||
|
||||
memset(&ke, 0, sizeof(ke));
|
||||
|
||||
if (size == sizeof(unsigned int[2])) {
|
||||
/* legacy case */
|
||||
int __user *ip = (int __user *)p;
|
||||
|
||||
if (copy_from_user(ke.scancode, p, sizeof(unsigned int)))
|
||||
return -EFAULT;
|
||||
|
||||
if (get_user(ke.keycode, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
ke.len = sizeof(unsigned int);
|
||||
ke.flags = 0;
|
||||
|
||||
} else {
|
||||
size = min(size, sizeof(ke));
|
||||
|
||||
if (copy_from_user(&ke, p, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (ke.len > sizeof(ke.scancode))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return input_set_keycode(dev, &ke);
|
||||
}
|
||||
|
||||
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
||||
void __user *p, int compat_mode)
|
||||
{
|
||||
|
|
@ -580,25 +654,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
|
||||
return 0;
|
||||
|
||||
case EVIOCGKEYCODE:
|
||||
if (get_user(t, ip))
|
||||
return -EFAULT;
|
||||
|
||||
error = input_get_keycode(dev, t, &v);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (put_user(v, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
|
||||
case EVIOCSKEYCODE:
|
||||
if (get_user(t, ip) || get_user(v, ip + 1))
|
||||
return -EFAULT;
|
||||
|
||||
return input_set_keycode(dev, t, v);
|
||||
|
||||
case EVIOCRMFF:
|
||||
return input_ff_erase(dev, (int)(unsigned long) p, file);
|
||||
|
||||
|
|
@ -620,7 +675,6 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
|
||||
/* Now check variable-length commands */
|
||||
#define EVIOC_MASK_SIZE(nr) ((nr) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
|
||||
|
||||
switch (EVIOC_MASK_SIZE(cmd)) {
|
||||
|
||||
case EVIOCGKEY(0):
|
||||
|
|
@ -654,6 +708,12 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||
return -EFAULT;
|
||||
|
||||
return error;
|
||||
|
||||
case EVIOC_MASK_SIZE(EVIOCGKEYCODE):
|
||||
return evdev_handle_get_keycode(dev, p, size);
|
||||
|
||||
case EVIOC_MASK_SIZE(EVIOCSKEYCODE):
|
||||
return evdev_handle_set_keycode(dev, p, size);
|
||||
}
|
||||
|
||||
/* Multi-number variable-length handlers */
|
||||
|
|
|
|||
|
|
@ -59,44 +59,52 @@ MODULE_DEVICE_TABLE(pci, emu_tbl);
|
|||
|
||||
static int __devinit emu_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
int ioport, iolen;
|
||||
struct emu *emu;
|
||||
struct gameport *port;
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
return -EBUSY;
|
||||
|
||||
ioport = pci_resource_start(pdev, 0);
|
||||
iolen = pci_resource_len(pdev, 0);
|
||||
|
||||
if (!request_region(ioport, iolen, "emu10k1-gp"))
|
||||
return -EBUSY;
|
||||
int error;
|
||||
|
||||
emu = kzalloc(sizeof(struct emu), GFP_KERNEL);
|
||||
port = gameport_allocate_port();
|
||||
if (!emu || !port) {
|
||||
printk(KERN_ERR "emu10k1-gp: Memory allocation failed\n");
|
||||
release_region(ioport, iolen);
|
||||
kfree(emu);
|
||||
gameport_free_port(port);
|
||||
return -ENOMEM;
|
||||
error = -ENOMEM;
|
||||
goto err_out_free;
|
||||
}
|
||||
|
||||
emu->io = ioport;
|
||||
emu->size = iolen;
|
||||
error = pci_enable_device(pdev);
|
||||
if (error)
|
||||
goto err_out_free;
|
||||
|
||||
emu->io = pci_resource_start(pdev, 0);
|
||||
emu->size = pci_resource_len(pdev, 0);
|
||||
|
||||
emu->dev = pdev;
|
||||
emu->gameport = port;
|
||||
|
||||
gameport_set_name(port, "EMU10K1");
|
||||
gameport_set_phys(port, "pci%s/gameport0", pci_name(pdev));
|
||||
port->dev.parent = &pdev->dev;
|
||||
port->io = ioport;
|
||||
port->io = emu->io;
|
||||
|
||||
if (!request_region(emu->io, emu->size, "emu10k1-gp")) {
|
||||
printk(KERN_ERR "emu10k1-gp: unable to grab region 0x%x-0x%x\n",
|
||||
emu->io, emu->io + emu->size - 1);
|
||||
error = -EBUSY;
|
||||
goto err_out_disable_dev;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, emu);
|
||||
|
||||
gameport_register_port(port);
|
||||
|
||||
return 0;
|
||||
|
||||
err_out_disable_dev:
|
||||
pci_disable_device(pdev);
|
||||
err_out_free:
|
||||
gameport_free_port(port);
|
||||
kfree(emu);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void __devexit emu_remove(struct pci_dev *pdev)
|
||||
|
|
@ -106,6 +114,8 @@ static void __devexit emu_remove(struct pci_dev *pdev)
|
|||
gameport_unregister_port(emu->gameport);
|
||||
release_region(emu->io, emu->size);
|
||||
kfree(emu);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
}
|
||||
|
||||
static struct pci_driver emu_driver = {
|
||||
|
|
|
|||
|
|
@ -133,11 +133,11 @@ static void __devexit fm801_gp_remove(struct pci_dev *pci)
|
|||
{
|
||||
struct fm801_gp *gp = pci_get_drvdata(pci);
|
||||
|
||||
if (gp) {
|
||||
gameport_unregister_port(gp->gameport);
|
||||
release_resource(gp->res_port);
|
||||
kfree(gp);
|
||||
}
|
||||
gameport_unregister_port(gp->gameport);
|
||||
release_resource(gp->res_port);
|
||||
kfree(gp);
|
||||
|
||||
pci_disable_device(pci);
|
||||
}
|
||||
|
||||
static const struct pci_device_id fm801_gp_id_table[] = {
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ static int input_handle_abs_event(struct input_dev *dev,
|
|||
if (code == ABS_MT_SLOT) {
|
||||
/*
|
||||
* "Stage" the event; we'll flush it later, when we
|
||||
* get actiual touch data.
|
||||
* get actual touch data.
|
||||
*/
|
||||
if (*pval >= 0 && *pval < dev->mtsize)
|
||||
dev->slot = *pval;
|
||||
|
|
@ -188,7 +188,7 @@ static int input_handle_abs_event(struct input_dev *dev,
|
|||
pold = &mtslot->abs[code - ABS_MT_FIRST];
|
||||
} else {
|
||||
/*
|
||||
* Bypass filtering for multitouch events when
|
||||
* Bypass filtering for multi-touch events when
|
||||
* not employing slots.
|
||||
*/
|
||||
pold = NULL;
|
||||
|
|
@ -634,78 +634,141 @@ static void input_disconnect_device(struct input_dev *dev)
|
|||
spin_unlock_irq(&dev->event_lock);
|
||||
}
|
||||
|
||||
static int input_fetch_keycode(struct input_dev *dev, int scancode)
|
||||
/**
|
||||
* input_scancode_to_scalar() - converts scancode in &struct input_keymap_entry
|
||||
* @ke: keymap entry containing scancode to be converted.
|
||||
* @scancode: pointer to the location where converted scancode should
|
||||
* be stored.
|
||||
*
|
||||
* This function is used to convert scancode stored in &struct keymap_entry
|
||||
* into scalar form understood by legacy keymap handling methods. These
|
||||
* methods expect scancodes to be represented as 'unsigned int'.
|
||||
*/
|
||||
int input_scancode_to_scalar(const struct input_keymap_entry *ke,
|
||||
unsigned int *scancode)
|
||||
{
|
||||
switch (ke->len) {
|
||||
case 1:
|
||||
*scancode = *((u8 *)ke->scancode);
|
||||
break;
|
||||
|
||||
case 2:
|
||||
*scancode = *((u16 *)ke->scancode);
|
||||
break;
|
||||
|
||||
case 4:
|
||||
*scancode = *((u32 *)ke->scancode);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(input_scancode_to_scalar);
|
||||
|
||||
/*
|
||||
* Those routines handle the default case where no [gs]etkeycode() is
|
||||
* defined. In this case, an array indexed by the scancode is used.
|
||||
*/
|
||||
|
||||
static unsigned int input_fetch_keycode(struct input_dev *dev,
|
||||
unsigned int index)
|
||||
{
|
||||
switch (dev->keycodesize) {
|
||||
case 1:
|
||||
return ((u8 *)dev->keycode)[scancode];
|
||||
case 1:
|
||||
return ((u8 *)dev->keycode)[index];
|
||||
|
||||
case 2:
|
||||
return ((u16 *)dev->keycode)[scancode];
|
||||
case 2:
|
||||
return ((u16 *)dev->keycode)[index];
|
||||
|
||||
default:
|
||||
return ((u32 *)dev->keycode)[scancode];
|
||||
default:
|
||||
return ((u32 *)dev->keycode)[index];
|
||||
}
|
||||
}
|
||||
|
||||
static int input_default_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
unsigned int index;
|
||||
int error;
|
||||
|
||||
if (!dev->keycodesize)
|
||||
return -EINVAL;
|
||||
|
||||
if (scancode >= dev->keycodemax)
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX)
|
||||
index = ke->index;
|
||||
else {
|
||||
error = input_scancode_to_scalar(ke, &index);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (index >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
*keycode = input_fetch_keycode(dev, scancode);
|
||||
ke->keycode = input_fetch_keycode(dev, index);
|
||||
ke->index = index;
|
||||
ke->len = sizeof(index);
|
||||
memcpy(ke->scancode, &index, sizeof(index));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int input_default_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
int old_keycode;
|
||||
unsigned int index;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (scancode >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
if (!dev->keycodesize)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8)))
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
index = ke->index;
|
||||
} else {
|
||||
error = input_scancode_to_scalar(ke, &index);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (index >= dev->keycodemax)
|
||||
return -EINVAL;
|
||||
|
||||
if (dev->keycodesize < sizeof(dev->keycode) &&
|
||||
(ke->keycode >> (dev->keycodesize * 8)))
|
||||
return -EINVAL;
|
||||
|
||||
switch (dev->keycodesize) {
|
||||
case 1: {
|
||||
u8 *k = (u8 *)dev->keycode;
|
||||
old_keycode = k[scancode];
|
||||
k[scancode] = keycode;
|
||||
*old_keycode = k[index];
|
||||
k[index] = ke->keycode;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
u16 *k = (u16 *)dev->keycode;
|
||||
old_keycode = k[scancode];
|
||||
k[scancode] = keycode;
|
||||
*old_keycode = k[index];
|
||||
k[index] = ke->keycode;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
u32 *k = (u32 *)dev->keycode;
|
||||
old_keycode = k[scancode];
|
||||
k[scancode] = keycode;
|
||||
*old_keycode = k[index];
|
||||
k[index] = ke->keycode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
__clear_bit(old_keycode, dev->keybit);
|
||||
__set_bit(keycode, dev->keybit);
|
||||
__clear_bit(*old_keycode, dev->keybit);
|
||||
__set_bit(ke->keycode, dev->keybit);
|
||||
|
||||
for (i = 0; i < dev->keycodemax; i++) {
|
||||
if (input_fetch_keycode(dev, i) == old_keycode) {
|
||||
__set_bit(old_keycode, dev->keybit);
|
||||
if (input_fetch_keycode(dev, i) == *old_keycode) {
|
||||
__set_bit(*old_keycode, dev->keybit);
|
||||
break; /* Setting the bit twice is useless, so break */
|
||||
}
|
||||
}
|
||||
|
|
@ -716,53 +779,86 @@ static int input_default_setkeycode(struct input_dev *dev,
|
|||
/**
|
||||
* input_get_keycode - retrieve keycode currently mapped to a given scancode
|
||||
* @dev: input device which keymap is being queried
|
||||
* @scancode: scancode (or its equivalent for device in question) for which
|
||||
* keycode is needed
|
||||
* @keycode: result
|
||||
* @ke: keymap entry
|
||||
*
|
||||
* This function should be called by anyone interested in retrieving current
|
||||
* keymap. Presently keyboard and evdev handlers use it.
|
||||
* keymap. Presently evdev handlers use it.
|
||||
*/
|
||||
int input_get_keycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
int input_get_keycode(struct input_dev *dev, struct input_keymap_entry *ke)
|
||||
{
|
||||
unsigned long flags;
|
||||
int retval;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
retval = dev->getkeycode(dev, scancode, keycode);
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
|
||||
if (dev->getkeycode) {
|
||||
/*
|
||||
* Support for legacy drivers, that don't implement the new
|
||||
* ioctls
|
||||
*/
|
||||
u32 scancode = ke->index;
|
||||
|
||||
memcpy(ke->scancode, &scancode, sizeof(scancode));
|
||||
ke->len = sizeof(scancode);
|
||||
retval = dev->getkeycode(dev, scancode, &ke->keycode);
|
||||
} else {
|
||||
retval = dev->getkeycode_new(dev, ke);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dev->event_lock, flags);
|
||||
return retval;
|
||||
}
|
||||
EXPORT_SYMBOL(input_get_keycode);
|
||||
|
||||
/**
|
||||
* input_get_keycode - assign new keycode to a given scancode
|
||||
* input_set_keycode - attribute a keycode to a given scancode
|
||||
* @dev: input device which keymap is being updated
|
||||
* @scancode: scancode (or its equivalent for device in question)
|
||||
* @keycode: new keycode to be assigned to the scancode
|
||||
* @ke: new keymap entry
|
||||
*
|
||||
* This function should be called by anyone needing to update current
|
||||
* keymap. Presently keyboard and evdev handlers use it.
|
||||
*/
|
||||
int input_set_keycode(struct input_dev *dev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
const struct input_keymap_entry *ke)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int old_keycode;
|
||||
int retval;
|
||||
|
||||
if (keycode > KEY_MAX)
|
||||
if (ke->keycode > KEY_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
|
||||
retval = dev->getkeycode(dev, scancode, &old_keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
if (dev->setkeycode) {
|
||||
/*
|
||||
* Support for legacy drivers, that don't implement the new
|
||||
* ioctls
|
||||
*/
|
||||
unsigned int scancode;
|
||||
|
||||
retval = input_scancode_to_scalar(ke, &scancode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* We need to know the old scancode, in order to generate a
|
||||
* keyup effect, if the set operation happens successfully
|
||||
*/
|
||||
if (!dev->getkeycode) {
|
||||
retval = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = dev->getkeycode(dev, scancode, &old_keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
retval = dev->setkeycode(dev, scancode, ke->keycode);
|
||||
} else {
|
||||
retval = dev->setkeycode_new(dev, ke, &old_keycode);
|
||||
}
|
||||
|
||||
retval = dev->setkeycode(dev, scancode, keycode);
|
||||
if (retval)
|
||||
goto out;
|
||||
|
||||
|
|
@ -1601,7 +1697,7 @@ EXPORT_SYMBOL(input_free_device);
|
|||
*
|
||||
* This function allocates all necessary memory for MT slot handling in the
|
||||
* input device, and adds ABS_MT_SLOT to the device capabilities. All slots
|
||||
* are initially marked as unused iby setting ABS_MT_TRACKING_ID to -1.
|
||||
* are initially marked as unused by setting ABS_MT_TRACKING_ID to -1.
|
||||
*/
|
||||
int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots)
|
||||
{
|
||||
|
|
@ -1759,11 +1855,11 @@ int input_register_device(struct input_dev *dev)
|
|||
dev->rep[REP_PERIOD] = 33;
|
||||
}
|
||||
|
||||
if (!dev->getkeycode)
|
||||
dev->getkeycode = input_default_getkeycode;
|
||||
if (!dev->getkeycode && !dev->getkeycode_new)
|
||||
dev->getkeycode_new = input_default_getkeycode;
|
||||
|
||||
if (!dev->setkeycode)
|
||||
dev->setkeycode = input_default_setkeycode;
|
||||
if (!dev->setkeycode && !dev->setkeycode_new)
|
||||
dev->setkeycode_new = input_default_setkeycode;
|
||||
|
||||
dev_set_name(&dev->dev, "input%ld",
|
||||
(unsigned long) atomic_inc_return(&input_no) - 1);
|
||||
|
|
|
|||
|
|
@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called newtonkbd.
|
||||
|
||||
config KEYBOARD_NOMADIK
|
||||
tristate "ST-Ericsson Nomadik SKE keyboard"
|
||||
depends on PLAT_NOMADIK
|
||||
help
|
||||
Say Y here if you want to use a keypad provided on the SKE controller
|
||||
used on the Ux500 and Nomadik platforms
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nmk-ske-keypad.
|
||||
|
||||
config KEYBOARD_OPENCORES
|
||||
tristate "OpenCores Keyboard Controller"
|
||||
help
|
||||
|
|
@ -424,6 +434,15 @@ config KEYBOARD_OMAP
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap-keypad.
|
||||
|
||||
config KEYBOARD_OMAP4
|
||||
tristate "TI OMAP4 keypad support"
|
||||
depends on ARCH_OMAP4
|
||||
help
|
||||
Say Y here if you want to use the OMAP4 keypad.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap4-keypad.
|
||||
|
||||
config KEYBOARD_TWL4030
|
||||
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
|
||||
depends on TWL4030_CORE
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
|||
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
|
||||
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||
|
|
|
|||
|
|
@ -660,7 +660,7 @@ static const struct dev_pm_ops adp5588_dev_pm_ops = {
|
|||
#endif
|
||||
|
||||
static const struct i2c_device_id adp5588_id[] = {
|
||||
{ KBUILD_MODNAME, 0 },
|
||||
{ "adp5588-keys", 0 },
|
||||
{ "adp5587-keys", 0 },
|
||||
{ }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -570,6 +570,8 @@ static struct serio_device_id hil_dev_ids[] = {
|
|||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, hil_dev_ids);
|
||||
|
||||
static struct serio_driver hil_serio_drv = {
|
||||
.driver = {
|
||||
.name = "hil_dev",
|
||||
|
|
|
|||
408
drivers/input/keyboard/nomadik-ske-keypad.c
Normal file
408
drivers/input/keyboard/nomadik-ske-keypad.c
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* License terms:GNU General Public License (GPL) version 2
|
||||
*
|
||||
* Keypad controller driver for the SKE (Scroll Key Encoder) module used in
|
||||
* the Nomadik 8815 and Ux500 platforms.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <plat/ske.h>
|
||||
|
||||
/* SKE_CR bits */
|
||||
#define SKE_KPMLT (0x1 << 6)
|
||||
#define SKE_KPCN (0x7 << 3)
|
||||
#define SKE_KPASEN (0x1 << 2)
|
||||
#define SKE_KPASON (0x1 << 7)
|
||||
|
||||
/* SKE_IMSC bits */
|
||||
#define SKE_KPIMA (0x1 << 2)
|
||||
|
||||
/* SKE_ICR bits */
|
||||
#define SKE_KPICS (0x1 << 3)
|
||||
#define SKE_KPICA (0x1 << 2)
|
||||
|
||||
/* SKE_RIS bits */
|
||||
#define SKE_KPRISA (0x1 << 2)
|
||||
|
||||
#define SKE_KEYPAD_ROW_SHIFT 3
|
||||
#define SKE_KPD_KEYMAP_SIZE (8 * 8)
|
||||
|
||||
/* keypad auto scan registers */
|
||||
#define SKE_ASR0 0x20
|
||||
#define SKE_ASR1 0x24
|
||||
#define SKE_ASR2 0x28
|
||||
#define SKE_ASR3 0x2C
|
||||
|
||||
#define SKE_NUM_ASRX_REGISTERS (4)
|
||||
|
||||
/**
|
||||
* struct ske_keypad - data structure used by keypad driver
|
||||
* @irq: irq no
|
||||
* @reg_base: ske regsiters base address
|
||||
* @input: pointer to input device object
|
||||
* @board: keypad platform device
|
||||
* @keymap: matrix scan code table for keycodes
|
||||
* @clk: clock structure pointer
|
||||
*/
|
||||
struct ske_keypad {
|
||||
int irq;
|
||||
void __iomem *reg_base;
|
||||
struct input_dev *input;
|
||||
const struct ske_keypad_platform_data *board;
|
||||
unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
|
||||
struct clk *clk;
|
||||
spinlock_t ske_keypad_lock;
|
||||
};
|
||||
|
||||
static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
|
||||
u8 mask, u8 data)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
spin_lock(&keypad->ske_keypad_lock);
|
||||
|
||||
ret = readl(keypad->reg_base + addr);
|
||||
ret &= ~mask;
|
||||
ret |= data;
|
||||
writel(ret, keypad->reg_base + addr);
|
||||
|
||||
spin_unlock(&keypad->ske_keypad_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* ske_keypad_chip_init: init keypad controller configuration
|
||||
*
|
||||
* Enable Multi key press detection, auto scan mode
|
||||
*/
|
||||
static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
|
||||
{
|
||||
u32 value;
|
||||
int timeout = 50;
|
||||
|
||||
/* check SKE_RIS to be 0 */
|
||||
while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
|
||||
cpu_relax();
|
||||
|
||||
if (!timeout)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* set debounce value
|
||||
* keypad dbounce is configured in DBCR[15:8]
|
||||
* dbounce value in steps of 32/32.768 ms
|
||||
*/
|
||||
spin_lock(&keypad->ske_keypad_lock);
|
||||
value = readl(keypad->reg_base + SKE_DBCR);
|
||||
value = value & 0xff;
|
||||
value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
|
||||
writel(value, keypad->reg_base + SKE_DBCR);
|
||||
spin_unlock(&keypad->ske_keypad_lock);
|
||||
|
||||
/* enable multi key detection */
|
||||
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
|
||||
|
||||
/*
|
||||
* set up the number of columns
|
||||
* KPCN[5:3] defines no. of keypad columns to be auto scanned
|
||||
*/
|
||||
value = (keypad->board->kcol - 1) << 3;
|
||||
ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
|
||||
|
||||
/* clear keypad interrupt for auto(and pending SW) scans */
|
||||
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
|
||||
|
||||
/* un-mask keypad interrupts */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
/* enable automatic scan */
|
||||
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ske_keypad_read_data(struct ske_keypad *keypad)
|
||||
{
|
||||
struct input_dev *input = keypad->input;
|
||||
u16 status;
|
||||
int col = 0, row = 0, code;
|
||||
int ske_asr, ske_ris, key_pressed, i;
|
||||
|
||||
/*
|
||||
* Read the auto scan registers
|
||||
*
|
||||
* Each SKE_ASRx (x=0 to x=3) contains two row values.
|
||||
* lower byte contains row value for column 2*x,
|
||||
* upper byte contains row value for column 2*x + 1
|
||||
*/
|
||||
for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
|
||||
ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
|
||||
if (!ske_asr)
|
||||
continue;
|
||||
|
||||
/* now that ASRx is zero, find out the column x and row y*/
|
||||
if (ske_asr & 0xff) {
|
||||
col = i * 2;
|
||||
status = ske_asr & 0xff;
|
||||
} else {
|
||||
col = (i * 2) + 1;
|
||||
status = (ske_asr & 0xff00) >> 8;
|
||||
}
|
||||
|
||||
/* find out the row */
|
||||
row = __ffs(status);
|
||||
|
||||
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
|
||||
ske_ris = readl(keypad->reg_base + SKE_RIS);
|
||||
key_pressed = ske_ris & SKE_KPRISA;
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input, keypad->keymap[code], key_pressed);
|
||||
input_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct ske_keypad *keypad = dev_id;
|
||||
int retries = 20;
|
||||
|
||||
/* disable auto scan interrupt; mask the interrupt generated */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
|
||||
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
|
||||
|
||||
while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
|
||||
msleep(5);
|
||||
|
||||
if (retries) {
|
||||
/* SKEx registers are stable and can be read */
|
||||
ske_keypad_read_data(keypad);
|
||||
}
|
||||
|
||||
/* enable auto scan interrupts */
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit ske_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
|
||||
struct ske_keypad *keypad;
|
||||
struct input_dev *input;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
if (!plat) {
|
||||
dev_err(&pdev->dev, "invalid keypad platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get keypad irq\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "missing platform resources\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!keypad || !input) {
|
||||
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
keypad->irq = irq;
|
||||
keypad->board = plat;
|
||||
keypad->input = input;
|
||||
spin_lock_init(&keypad->ske_keypad_lock);
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
|
||||
dev_err(&pdev->dev, "failed to request I/O memory\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
keypad->reg_base = ioremap(res->start, resource_size(res));
|
||||
if (!keypad->reg_base) {
|
||||
dev_err(&pdev->dev, "failed to remap I/O memory\n");
|
||||
error = -ENXIO;
|
||||
goto err_free_mem_region;
|
||||
}
|
||||
|
||||
keypad->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(keypad->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get clk\n");
|
||||
error = PTR_ERR(keypad->clk);
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->name = "ux500-ske-keypad";
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input->keycode = keypad->keymap;
|
||||
input->keycodesize = sizeof(keypad->keymap[0]);
|
||||
input->keycodemax = ARRAY_SIZE(keypad->keymap);
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
if (!plat->no_autorepeat)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,
|
||||
input->keycode, input->keybit);
|
||||
|
||||
clk_enable(keypad->clk);
|
||||
|
||||
/* go through board initialization helpers */
|
||||
if (keypad->board->init)
|
||||
keypad->board->init();
|
||||
|
||||
error = ske_keypad_chip_init(keypad);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "unable to init keypad hardware\n");
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
|
||||
IRQF_ONESHOT, "ske-keypad", keypad);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register input device: %d\n", error);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
if (plat->wakeup_enable)
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(keypad->irq, keypad);
|
||||
err_clk_disable:
|
||||
clk_disable(keypad->clk);
|
||||
clk_put(keypad->clk);
|
||||
err_iounmap:
|
||||
iounmap(keypad->reg_base);
|
||||
err_free_mem_region:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(keypad);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit ske_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
free_irq(keypad->irq, keypad);
|
||||
|
||||
input_unregister_device(keypad->input);
|
||||
|
||||
clk_disable(keypad->clk);
|
||||
clk_put(keypad->clk);
|
||||
|
||||
if (keypad->board->exit)
|
||||
keypad->board->exit();
|
||||
|
||||
iounmap(keypad->reg_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
kfree(keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ske_keypad_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(irq);
|
||||
else
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ske_keypad_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ske_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(irq);
|
||||
else
|
||||
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
|
||||
.suspend = ske_keypad_suspend,
|
||||
.resume = ske_keypad_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
struct platform_driver ske_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "nmk-ske-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &ske_keypad_dev_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = ske_keypad_probe,
|
||||
.remove = __devexit_p(ske_keypad_remove),
|
||||
};
|
||||
|
||||
static int __init ske_keypad_init(void)
|
||||
{
|
||||
return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
|
||||
}
|
||||
module_init(ske_keypad_init);
|
||||
|
||||
static void __exit ske_keypad_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ske_keypad_driver);
|
||||
}
|
||||
module_exit(ske_keypad_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Naveen Kumar <[email protected]> / Sundar Iyer <[email protected]>");
|
||||
MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
|
||||
MODULE_ALIAS("platform:nomadik-ske-keypad");
|
||||
318
drivers/input/keyboard/omap4-keypad.c
Normal file
318
drivers/input/keyboard/omap4-keypad.c
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* OMAP4 Keypad Driver
|
||||
*
|
||||
* Copyright (C) 2010 Texas Instruments
|
||||
*
|
||||
* Author: Abraham Arce <x0066660@ti.com>
|
||||
* Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <plat/omap4-keypad.h>
|
||||
|
||||
/* OMAP4 registers */
|
||||
#define OMAP4_KBD_REVISION 0x00
|
||||
#define OMAP4_KBD_SYSCONFIG 0x10
|
||||
#define OMAP4_KBD_SYSSTATUS 0x14
|
||||
#define OMAP4_KBD_IRQSTATUS 0x18
|
||||
#define OMAP4_KBD_IRQENABLE 0x1C
|
||||
#define OMAP4_KBD_WAKEUPENABLE 0x20
|
||||
#define OMAP4_KBD_PENDING 0x24
|
||||
#define OMAP4_KBD_CTRL 0x28
|
||||
#define OMAP4_KBD_DEBOUNCINGTIME 0x2C
|
||||
#define OMAP4_KBD_LONGKEYTIME 0x30
|
||||
#define OMAP4_KBD_TIMEOUT 0x34
|
||||
#define OMAP4_KBD_STATEMACHINE 0x38
|
||||
#define OMAP4_KBD_ROWINPUTS 0x3C
|
||||
#define OMAP4_KBD_COLUMNOUTPUTS 0x40
|
||||
#define OMAP4_KBD_FULLCODE31_0 0x44
|
||||
#define OMAP4_KBD_FULLCODE63_32 0x48
|
||||
|
||||
/* OMAP4 bit definitions */
|
||||
#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0)
|
||||
#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1)
|
||||
#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2)
|
||||
#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0)
|
||||
#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1)
|
||||
#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1)
|
||||
#define OMAP4_DEF_CTRLPTVVALUE (1 << 2)
|
||||
#define OMAP4_DEF_CTRLPTV (1 << 1)
|
||||
|
||||
/* OMAP4 values */
|
||||
#define OMAP4_VAL_IRQDISABLE 0x00
|
||||
#define OMAP4_VAL_DEBOUNCINGTIME 0x07
|
||||
#define OMAP4_VAL_FUNCTIONALCFG 0x1E
|
||||
|
||||
#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF
|
||||
|
||||
struct omap4_keypad {
|
||||
struct input_dev *input;
|
||||
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
|
||||
unsigned int rows;
|
||||
unsigned int cols;
|
||||
unsigned int row_shift;
|
||||
unsigned char key_state[8];
|
||||
unsigned short keymap[];
|
||||
};
|
||||
|
||||
static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
|
||||
{
|
||||
__raw_writel(OMAP4_VAL_FUNCTIONALCFG,
|
||||
keypad_data->base + OMAP4_KBD_CTRL);
|
||||
__raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
|
||||
keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
|
||||
__raw_writel(OMAP4_VAL_IRQDISABLE,
|
||||
keypad_data->base + OMAP4_KBD_IRQSTATUS);
|
||||
__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
__raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
|
||||
keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
|
||||
}
|
||||
|
||||
/* Interrupt handler */
|
||||
static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = dev_id;
|
||||
struct input_dev *input_dev = keypad_data->input;
|
||||
unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
|
||||
unsigned int col, row, code, changed;
|
||||
u32 *new_state = (u32 *) key_state;
|
||||
|
||||
/* Disable interrupts */
|
||||
__raw_writel(OMAP4_VAL_IRQDISABLE,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
|
||||
*new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = __raw_readl(keypad_data->base
|
||||
+ OMAP4_KBD_FULLCODE63_32);
|
||||
|
||||
for (row = 0; row < keypad_data->rows; row++) {
|
||||
changed = key_state[row] ^ keypad_data->key_state[row];
|
||||
if (!changed)
|
||||
continue;
|
||||
|
||||
for (col = 0; col < keypad_data->cols; col++) {
|
||||
if (changed & (1 << col)) {
|
||||
code = MATRIX_SCAN_CODE(row, col,
|
||||
keypad_data->row_shift);
|
||||
input_event(input_dev, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input_dev,
|
||||
keypad_data->keymap[code],
|
||||
key_state[row] & (1 << col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input_sync(input_dev);
|
||||
|
||||
memcpy(keypad_data->key_state, key_state,
|
||||
sizeof(keypad_data->key_state));
|
||||
|
||||
/* clear pending interrupts */
|
||||
__raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
|
||||
keypad_data->base + OMAP4_KBD_IRQSTATUS);
|
||||
|
||||
/* enable interrupts */
|
||||
__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct omap4_keypad_platform_data *pdata;
|
||||
struct omap4_keypad *keypad_data;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
unsigned int row_shift, max_keys;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
/* platform data */
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no base address specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "no keyboard irq assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->keymap_data) {
|
||||
dev_err(&pdev->dev, "no keymap data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
row_shift = get_count_order(pdata->cols);
|
||||
max_keys = pdata->rows << row_shift;
|
||||
|
||||
keypad_data = kzalloc(sizeof(struct omap4_keypad) +
|
||||
max_keys * sizeof(keypad_data->keymap[0]),
|
||||
GFP_KERNEL);
|
||||
if (!keypad_data) {
|
||||
dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
res = request_mem_region(res->start, size, pdev->name);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "can't request mem region\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_keypad;
|
||||
}
|
||||
|
||||
keypad_data->base = ioremap(res->start, resource_size(res));
|
||||
if (!keypad_data->base) {
|
||||
dev_err(&pdev->dev, "can't ioremap mem resource\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
keypad_data->irq = irq;
|
||||
keypad_data->row_shift = row_shift;
|
||||
keypad_data->rows = pdata->rows;
|
||||
keypad_data->cols = pdata->cols;
|
||||
|
||||
/* input device allocation */
|
||||
keypad_data->input = input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0001;
|
||||
|
||||
input_dev->keycode = keypad_data->keymap;
|
||||
input_dev->keycodesize = sizeof(keypad_data->keymap[0]);
|
||||
input_dev->keycodemax = max_keys;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_set_drvdata(input_dev, keypad_data);
|
||||
|
||||
matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
|
||||
omap4_keypad_config(keypad_data);
|
||||
|
||||
error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"omap4-keypad", keypad_data);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to register interrupt\n");
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
error = input_register_device(keypad_data->input);
|
||||
if (error < 0) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
|
||||
platform_set_drvdata(pdev, keypad_data);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
err_unmap:
|
||||
iounmap(keypad_data->base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, size);
|
||||
err_free_keypad:
|
||||
kfree(keypad_data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit omap4_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
input_unregister_device(keypad_data->input);
|
||||
|
||||
iounmap(keypad_data->base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(keypad_data);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap4_keypad_driver = {
|
||||
.probe = omap4_keypad_probe,
|
||||
.remove = __devexit_p(omap4_keypad_remove),
|
||||
.driver = {
|
||||
.name = "omap4-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init omap4_keypad_init(void)
|
||||
{
|
||||
return platform_driver_register(&omap4_keypad_driver);
|
||||
}
|
||||
module_init(omap4_keypad_init);
|
||||
|
||||
static void __exit omap4_keypad_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap4_keypad_driver);
|
||||
}
|
||||
module_exit(omap4_keypad_exit);
|
||||
|
||||
MODULE_AUTHOR("Texas Instruments");
|
||||
MODULE_DESCRIPTION("OMAP4 Keypad Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:omap4-keypad");
|
||||
|
|
@ -406,23 +406,22 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
|
|||
if (error) {
|
||||
dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
|
||||
kp->irq);
|
||||
goto err3;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
/* Enable KP and TO interrupts now. */
|
||||
reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
|
||||
if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
|
||||
error = -EIO;
|
||||
goto err4;
|
||||
goto err3;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, kp);
|
||||
return 0;
|
||||
|
||||
err4:
|
||||
err3:
|
||||
/* mask all events - we don't care about the result */
|
||||
(void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
|
||||
err3:
|
||||
free_irq(kp->irq, NULL);
|
||||
err2:
|
||||
input_unregister_device(input);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,16 @@ config INPUT_88PM860X_ONKEY
|
|||
To compile this driver as a module, choose M here: the module
|
||||
will be called 88pm860x_onkey.
|
||||
|
||||
config INPUT_AB8500_PONKEY
|
||||
tristate "AB8500 Pon (PowerOn) Key"
|
||||
depends on AB8500_CORE
|
||||
help
|
||||
Say Y here to use the PowerOn Key for ST-Ericsson's AB8500
|
||||
Mix-Sig PMIC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called ab8500-ponkey.
|
||||
|
||||
config INPUT_AD714X
|
||||
tristate "Analog Devices AD714x Capacitance Touch Sensor"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
|
||||
obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
|
||||
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
|
||||
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
|
||||
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
|
||||
|
|
|
|||
157
drivers/input/misc/ab8500-ponkey.c
Normal file
157
drivers/input/misc/ab8500-ponkey.c
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
*
|
||||
* License Terms: GNU General Public License v2
|
||||
* Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
|
||||
*
|
||||
* AB8500 Power-On Key handler
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/ab8500.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/**
|
||||
* struct ab8500_ponkey - ab8500 ponkey information
|
||||
* @input_dev: pointer to input device
|
||||
* @ab8500: ab8500 parent
|
||||
* @irq_dbf: irq number for falling transition
|
||||
* @irq_dbr: irq number for rising transition
|
||||
*/
|
||||
struct ab8500_ponkey {
|
||||
struct input_dev *idev;
|
||||
struct ab8500 *ab8500;
|
||||
int irq_dbf;
|
||||
int irq_dbr;
|
||||
};
|
||||
|
||||
/* AB8500 gives us an interrupt when ONKEY is held */
|
||||
static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
|
||||
{
|
||||
struct ab8500_ponkey *ponkey = data;
|
||||
|
||||
if (irq == ponkey->irq_dbf)
|
||||
input_report_key(ponkey->idev, KEY_POWER, true);
|
||||
else if (irq == ponkey->irq_dbr)
|
||||
input_report_key(ponkey->idev, KEY_POWER, false);
|
||||
|
||||
input_sync(ponkey->idev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct ab8500_ponkey *ponkey;
|
||||
struct input_dev *input;
|
||||
int irq_dbf, irq_dbr;
|
||||
int error;
|
||||
|
||||
irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
|
||||
if (irq_dbf < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
|
||||
return irq_dbf;
|
||||
}
|
||||
|
||||
irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
|
||||
if (irq_dbr < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
|
||||
return irq_dbr;
|
||||
}
|
||||
|
||||
ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!ponkey || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ponkey->idev = input;
|
||||
ponkey->ab8500 = ab8500;
|
||||
ponkey->irq_dbf = irq_dbf;
|
||||
ponkey->irq_dbr = irq_dbr;
|
||||
|
||||
input->name = "AB8500 POn(PowerOn) Key";
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||
|
||||
error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler,
|
||||
0, "ab8500-ponkey-dbf", ponkey);
|
||||
if (error < 0) {
|
||||
dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n",
|
||||
ponkey->irq_dbf, error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler,
|
||||
0, "ab8500-ponkey-dbr", ponkey);
|
||||
if (error < 0) {
|
||||
dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n",
|
||||
ponkey->irq_dbr, error);
|
||||
goto err_free_dbf_irq;
|
||||
}
|
||||
|
||||
error = input_register_device(ponkey->idev);
|
||||
if (error) {
|
||||
dev_err(ab8500->dev, "Can't register input device: %d\n", error);
|
||||
goto err_free_dbr_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ponkey);
|
||||
return 0;
|
||||
|
||||
err_free_dbr_irq:
|
||||
free_irq(ponkey->irq_dbr, ponkey);
|
||||
err_free_dbf_irq:
|
||||
free_irq(ponkey->irq_dbf, ponkey);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(ponkey);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit ab8500_ponkey_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(ponkey->irq_dbf, ponkey);
|
||||
free_irq(ponkey->irq_dbr, ponkey);
|
||||
input_unregister_device(ponkey->idev);
|
||||
kfree(ponkey);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ab8500_ponkey_driver = {
|
||||
.driver = {
|
||||
.name = "ab8500-poweron-key",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ab8500_ponkey_probe,
|
||||
.remove = __devexit_p(ab8500_ponkey_remove),
|
||||
};
|
||||
|
||||
static int __init ab8500_ponkey_init(void)
|
||||
{
|
||||
return platform_driver_register(&ab8500_ponkey_driver);
|
||||
}
|
||||
module_init(ab8500_ponkey_init);
|
||||
|
||||
static void __exit ab8500_ponkey_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ab8500_ponkey_driver);
|
||||
}
|
||||
module_exit(ab8500_ponkey_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Sundar Iyer <[email protected]>");
|
||||
MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
|
||||
|
|
@ -483,51 +483,88 @@ static void ati_remote2_complete_key(struct urb *urb)
|
|||
}
|
||||
|
||||
static int ati_remote2_getkeycode(struct input_dev *idev,
|
||||
unsigned int scancode, unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
unsigned int mode;
|
||||
int index;
|
||||
int offset;
|
||||
unsigned int index;
|
||||
unsigned int scancode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
|
||||
return -EINVAL;
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
index = ke->index;
|
||||
if (index >= ATI_REMOTE2_MODES *
|
||||
ARRAY_SIZE(ati_remote2_key_table))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
|
||||
offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
|
||||
scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code;
|
||||
} else {
|
||||
if (input_scancode_to_scalar(ke, &scancode))
|
||||
return -EINVAL;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC)
|
||||
return -EINVAL;
|
||||
|
||||
offset = ati_remote2_lookup(scancode & 0xff);
|
||||
if (offset < 0)
|
||||
return -EINVAL;
|
||||
|
||||
index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset;
|
||||
}
|
||||
|
||||
ke->keycode = ar2->keycode[mode][offset];
|
||||
ke->len = sizeof(scancode);
|
||||
memcpy(&ke->scancode, &scancode, sizeof(scancode));
|
||||
ke->index = index;
|
||||
|
||||
*keycode = ar2->keycode[mode][index];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_setkeycode(struct input_dev *idev,
|
||||
unsigned int scancode, unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
struct ati_remote2 *ar2 = input_get_drvdata(idev);
|
||||
unsigned int mode, old_keycode;
|
||||
int index;
|
||||
unsigned int mode;
|
||||
int offset;
|
||||
unsigned int index;
|
||||
unsigned int scancode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
|
||||
return -EINVAL;
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
|
||||
if (ke->index >= ATI_REMOTE2_MODES *
|
||||
ARRAY_SIZE(ati_remote2_key_table))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
if (index < 0)
|
||||
return -EINVAL;
|
||||
mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
|
||||
offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
|
||||
} else {
|
||||
if (input_scancode_to_scalar(ke, &scancode))
|
||||
return -EINVAL;
|
||||
|
||||
old_keycode = ar2->keycode[mode][index];
|
||||
ar2->keycode[mode][index] = keycode;
|
||||
__set_bit(keycode, idev->keybit);
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC)
|
||||
return -EINVAL;
|
||||
|
||||
offset = ati_remote2_lookup(scancode & 0xff);
|
||||
if (offset < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*old_keycode = ar2->keycode[mode][offset];
|
||||
ar2->keycode[mode][offset] = ke->keycode;
|
||||
__set_bit(ke->keycode, idev->keybit);
|
||||
|
||||
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
|
||||
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
|
||||
if (ar2->keycode[mode][index] == old_keycode)
|
||||
if (ar2->keycode[mode][index] == *old_keycode)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
__clear_bit(old_keycode, idev->keybit);
|
||||
__clear_bit(*old_keycode, idev->keybit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -575,8 +612,8 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
|
|||
idev->open = ati_remote2_open;
|
||||
idev->close = ati_remote2_close;
|
||||
|
||||
idev->getkeycode = ati_remote2_getkeycode;
|
||||
idev->setkeycode = ati_remote2_setkeycode;
|
||||
idev->getkeycode_new = ati_remote2_getkeycode;
|
||||
idev->setkeycode_new = ati_remote2_setkeycode;
|
||||
|
||||
idev->name = ar2->name;
|
||||
idev->phys = ar2->phys;
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_dev
|
|||
|
||||
pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL);
|
||||
if (!pm->configcr)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -699,7 +699,7 @@ int elantech_init(struct psmouse *psmouse)
|
|||
|
||||
psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
|
||||
if (!etd)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
etd->parity[0] = 1;
|
||||
for (i = 1; i < 256; i++)
|
||||
|
|
|
|||
|
|
@ -1584,10 +1584,10 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
|
|||
if (!new_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
while (serio->child) {
|
||||
while (!list_empty(&serio->children)) {
|
||||
if (++retry > 3) {
|
||||
printk(KERN_WARNING
|
||||
"psmouse: failed to destroy child port, "
|
||||
"psmouse: failed to destroy children ports, "
|
||||
"protocol change aborted.\n");
|
||||
input_free_device(new_dev);
|
||||
return -EIO;
|
||||
|
|
|
|||
|
|
@ -294,7 +294,29 @@ static int synaptics_pt_write(struct serio *serio, unsigned char c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int synaptics_is_pt_packet(unsigned char *buf)
|
||||
static int synaptics_pt_start(struct serio *serio)
|
||||
{
|
||||
struct psmouse *parent = serio_get_drvdata(serio->parent);
|
||||
struct synaptics_data *priv = parent->private;
|
||||
|
||||
serio_pause_rx(parent->ps2dev.serio);
|
||||
priv->pt_port = serio;
|
||||
serio_continue_rx(parent->ps2dev.serio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void synaptics_pt_stop(struct serio *serio)
|
||||
{
|
||||
struct psmouse *parent = serio_get_drvdata(serio->parent);
|
||||
struct synaptics_data *priv = parent->private;
|
||||
|
||||
serio_pause_rx(parent->ps2dev.serio);
|
||||
priv->pt_port = NULL;
|
||||
serio_continue_rx(parent->ps2dev.serio);
|
||||
}
|
||||
|
||||
static int synaptics_is_pt_packet(unsigned char *buf)
|
||||
{
|
||||
return (buf[0] & 0xFC) == 0x84 && (buf[3] & 0xCC) == 0xC4;
|
||||
}
|
||||
|
|
@ -315,9 +337,8 @@ static void synaptics_pass_pt_packet(struct serio *ptport, unsigned char *packet
|
|||
|
||||
static void synaptics_pt_activate(struct psmouse *psmouse)
|
||||
{
|
||||
struct serio *ptport = psmouse->ps2dev.serio->child;
|
||||
struct psmouse *child = serio_get_drvdata(ptport);
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
struct psmouse *child = serio_get_drvdata(priv->pt_port);
|
||||
|
||||
/* adjust the touchpad to child's choice of protocol */
|
||||
if (child) {
|
||||
|
|
@ -345,6 +366,8 @@ static void synaptics_pt_create(struct psmouse *psmouse)
|
|||
strlcpy(serio->name, "Synaptics pass-through", sizeof(serio->name));
|
||||
strlcpy(serio->phys, "synaptics-pt/serio0", sizeof(serio->name));
|
||||
serio->write = synaptics_pt_write;
|
||||
serio->start = synaptics_pt_start;
|
||||
serio->stop = synaptics_pt_stop;
|
||||
serio->parent = psmouse->ps2dev.serio;
|
||||
|
||||
psmouse->pt_activate = synaptics_pt_activate;
|
||||
|
|
@ -578,9 +601,10 @@ static psmouse_ret_t synaptics_process_byte(struct psmouse *psmouse)
|
|||
if (unlikely(priv->pkt_type == SYN_NEWABS))
|
||||
priv->pkt_type = synaptics_detect_pkt_type(psmouse);
|
||||
|
||||
if (SYN_CAP_PASS_THROUGH(priv->capabilities) && synaptics_is_pt_packet(psmouse->packet)) {
|
||||
if (psmouse->ps2dev.serio->child)
|
||||
synaptics_pass_pt_packet(psmouse->ps2dev.serio->child, psmouse->packet);
|
||||
if (SYN_CAP_PASS_THROUGH(priv->capabilities) &&
|
||||
synaptics_is_pt_packet(psmouse->packet)) {
|
||||
if (priv->pt_port)
|
||||
synaptics_pass_pt_packet(priv->pt_port, psmouse->packet);
|
||||
} else
|
||||
synaptics_process_packet(psmouse);
|
||||
|
||||
|
|
@ -731,7 +755,7 @@ int synaptics_init(struct psmouse *psmouse)
|
|||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct synaptics_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ struct synaptics_data {
|
|||
unsigned char pkt_type; /* packet type - old, new, etc */
|
||||
unsigned char mode; /* current mode byte */
|
||||
int scroll;
|
||||
|
||||
struct serio *pt_port; /* Pass-through serio port */
|
||||
};
|
||||
|
||||
void synaptics_module_init(void);
|
||||
|
|
|
|||
|
|
@ -303,7 +303,7 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties)
|
|||
|
||||
psmouse->private = kzalloc(sizeof(struct trackpoint_data), GFP_KERNEL);
|
||||
if (!psmouse->private)
|
||||
return -1;
|
||||
return -ENOMEM;
|
||||
|
||||
psmouse->vendor = "IBM";
|
||||
psmouse->name = "TrackPoint";
|
||||
|
|
|
|||
|
|
@ -866,7 +866,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
|
|||
spin_lock_init(&mousedev->client_lock);
|
||||
mutex_init(&mousedev->mutex);
|
||||
lockdep_set_subclass(&mousedev->mutex,
|
||||
minor == MOUSEDEV_MIX ? MOUSEDEV_MIX : 0);
|
||||
minor == MOUSEDEV_MIX ? SINGLE_DEPTH_NESTING : 0);
|
||||
init_waitqueue_head(&mousedev->wait);
|
||||
|
||||
if (minor == MOUSEDEV_MIX)
|
||||
|
|
|
|||
|
|
@ -226,4 +226,13 @@ config SERIO_AMS_DELTA
|
|||
To compile this driver as a module, choose M here;
|
||||
the module will be called ams_delta_serio.
|
||||
|
||||
config SERIO_PS2MULT
|
||||
tristate "TQC PS/2 multiplexer"
|
||||
help
|
||||
Say Y here if you have the PS/2 line multiplexer like the one
|
||||
present on TQC boads.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ps2mult.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ obj-$(CONFIG_SERIO_GSCPS2) += gscps2.o
|
|||
obj-$(CONFIG_HP_SDC) += hp_sdc.o
|
||||
obj-$(CONFIG_HIL_MLC) += hp_sdc_mlc.o hil_mlc.o
|
||||
obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o
|
||||
obj-$(CONFIG_SERIO_PS2MULT) += ps2mult.o
|
||||
obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o
|
||||
obj-$(CONFIG_SERIO_LIBPS2) += libps2.o
|
||||
obj-$(CONFIG_SERIO_RAW) += serio_raw.o
|
||||
|
|
|
|||
|
|
@ -1063,7 +1063,7 @@ static long i8042_panic_blink(int state)
|
|||
#ifdef CONFIG_X86
|
||||
static void i8042_dritek_enable(void)
|
||||
{
|
||||
char param = 0x90;
|
||||
unsigned char param = 0x90;
|
||||
int error;
|
||||
|
||||
error = i8042_command(¶m, 0x1059);
|
||||
|
|
|
|||
318
drivers/input/serio/ps2mult.c
Normal file
318
drivers/input/serio/ps2mult.c
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* TQC PS/2 Multiplexer driver
|
||||
*
|
||||
* Copyright (C) 2010 Dmitry Eremin-Solenikov
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
MODULE_AUTHOR("Dmitry Eremin-Solenikov <[email protected]>");
|
||||
MODULE_DESCRIPTION("TQC PS/2 Multiplexer driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define PS2MULT_KB_SELECTOR 0xA0
|
||||
#define PS2MULT_MS_SELECTOR 0xA1
|
||||
#define PS2MULT_ESCAPE 0x7D
|
||||
#define PS2MULT_BSYNC 0x7E
|
||||
#define PS2MULT_SESSION_START 0x55
|
||||
#define PS2MULT_SESSION_END 0x56
|
||||
|
||||
struct ps2mult_port {
|
||||
struct serio *serio;
|
||||
unsigned char sel;
|
||||
bool registered;
|
||||
};
|
||||
|
||||
#define PS2MULT_NUM_PORTS 2
|
||||
#define PS2MULT_KBD_PORT 0
|
||||
#define PS2MULT_MOUSE_PORT 1
|
||||
|
||||
struct ps2mult {
|
||||
struct serio *mx_serio;
|
||||
struct ps2mult_port ports[PS2MULT_NUM_PORTS];
|
||||
|
||||
spinlock_t lock;
|
||||
struct ps2mult_port *in_port;
|
||||
struct ps2mult_port *out_port;
|
||||
bool escape;
|
||||
};
|
||||
|
||||
/* First MUST come PS2MULT_NUM_PORTS selectors */
|
||||
static const unsigned char ps2mult_controls[] = {
|
||||
PS2MULT_KB_SELECTOR, PS2MULT_MS_SELECTOR,
|
||||
PS2MULT_ESCAPE, PS2MULT_BSYNC,
|
||||
PS2MULT_SESSION_START, PS2MULT_SESSION_END,
|
||||
};
|
||||
|
||||
static const struct serio_device_id ps2mult_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_PS2MULT,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, ps2mult_serio_ids);
|
||||
|
||||
static void ps2mult_select_port(struct ps2mult *psm, struct ps2mult_port *port)
|
||||
{
|
||||
struct serio *mx_serio = psm->mx_serio;
|
||||
|
||||
serio_write(mx_serio, port->sel);
|
||||
psm->out_port = port;
|
||||
dev_dbg(&mx_serio->dev, "switched to sel %02x\n", port->sel);
|
||||
}
|
||||
|
||||
static int ps2mult_serio_write(struct serio *serio, unsigned char data)
|
||||
{
|
||||
struct serio *mx_port = serio->parent;
|
||||
struct ps2mult *psm = serio_get_drvdata(mx_port);
|
||||
struct ps2mult_port *port = serio->port_data;
|
||||
bool need_escape;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
|
||||
if (psm->out_port != port)
|
||||
ps2mult_select_port(psm, port);
|
||||
|
||||
need_escape = memchr(ps2mult_controls, data, sizeof(ps2mult_controls));
|
||||
|
||||
dev_dbg(&serio->dev,
|
||||
"write: %s%02x\n", need_escape ? "ESC " : "", data);
|
||||
|
||||
if (need_escape)
|
||||
serio_write(mx_port, PS2MULT_ESCAPE);
|
||||
|
||||
serio_write(mx_port, data);
|
||||
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ps2mult_serio_start(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio->parent);
|
||||
struct ps2mult_port *port = serio->port_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
port->registered = true;
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps2mult_serio_stop(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio->parent);
|
||||
struct ps2mult_port *port = serio->port_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
port->registered = false;
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
}
|
||||
|
||||
static int ps2mult_create_port(struct ps2mult *psm, int i)
|
||||
{
|
||||
struct serio *mx_serio = psm->mx_serio;
|
||||
struct serio *serio;
|
||||
|
||||
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
|
||||
if (!serio)
|
||||
return -ENOMEM;
|
||||
|
||||
strlcpy(serio->name, "TQC PS/2 Multiplexer", sizeof(serio->name));
|
||||
snprintf(serio->phys, sizeof(serio->phys),
|
||||
"%s/port%d", mx_serio->phys, i);
|
||||
serio->id.type = SERIO_8042;
|
||||
serio->write = ps2mult_serio_write;
|
||||
serio->start = ps2mult_serio_start;
|
||||
serio->stop = ps2mult_serio_stop;
|
||||
serio->parent = psm->mx_serio;
|
||||
serio->port_data = &psm->ports[i];
|
||||
|
||||
psm->ports[i].serio = serio;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ps2mult_reset(struct ps2mult *psm)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
|
||||
serio_write(psm->mx_serio, PS2MULT_SESSION_END);
|
||||
serio_write(psm->mx_serio, PS2MULT_SESSION_START);
|
||||
|
||||
ps2mult_select_port(psm, &psm->ports[PS2MULT_KBD_PORT]);
|
||||
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
}
|
||||
|
||||
static int ps2mult_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct ps2mult *psm;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
if (!serio->write)
|
||||
return -EINVAL;
|
||||
|
||||
psm = kzalloc(sizeof(*psm), GFP_KERNEL);
|
||||
if (!psm)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&psm->lock);
|
||||
psm->mx_serio = serio;
|
||||
|
||||
for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
|
||||
psm->ports[i].sel = ps2mult_controls[i];
|
||||
error = ps2mult_create_port(psm, i);
|
||||
if (error)
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
psm->in_port = psm->out_port = &psm->ports[PS2MULT_KBD_PORT];
|
||||
|
||||
serio_set_drvdata(serio, psm);
|
||||
error = serio_open(serio, drv);
|
||||
if (error)
|
||||
goto err_out;
|
||||
|
||||
ps2mult_reset(psm);
|
||||
|
||||
for (i = 0; i < PS2MULT_NUM_PORTS; i++) {
|
||||
struct serio *s = psm->ports[i].serio;
|
||||
|
||||
dev_info(&serio->dev, "%s port at %s\n", s->name, serio->phys);
|
||||
serio_register_port(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
while (--i >= 0)
|
||||
kfree(psm->ports[i].serio);
|
||||
kfree(serio);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void ps2mult_disconnect(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio);
|
||||
|
||||
/* Note that serio core already take care of children ports */
|
||||
serio_write(serio, PS2MULT_SESSION_END);
|
||||
serio_close(serio);
|
||||
kfree(psm);
|
||||
|
||||
serio_set_drvdata(serio, NULL);
|
||||
}
|
||||
|
||||
static int ps2mult_reconnect(struct serio *serio)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio);
|
||||
|
||||
ps2mult_reset(psm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t ps2mult_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int dfl)
|
||||
{
|
||||
struct ps2mult *psm = serio_get_drvdata(serio);
|
||||
struct ps2mult_port *in_port;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&serio->dev, "Received %02x flags %02x\n", data, dfl);
|
||||
|
||||
spin_lock_irqsave(&psm->lock, flags);
|
||||
|
||||
if (psm->escape) {
|
||||
psm->escape = false;
|
||||
in_port = psm->in_port;
|
||||
if (in_port->registered)
|
||||
serio_interrupt(in_port->serio, data, dfl);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case PS2MULT_ESCAPE:
|
||||
dev_dbg(&serio->dev, "ESCAPE\n");
|
||||
psm->escape = true;
|
||||
break;
|
||||
|
||||
case PS2MULT_BSYNC:
|
||||
dev_dbg(&serio->dev, "BSYNC\n");
|
||||
psm->in_port = psm->out_port;
|
||||
break;
|
||||
|
||||
case PS2MULT_SESSION_START:
|
||||
dev_dbg(&serio->dev, "SS\n");
|
||||
break;
|
||||
|
||||
case PS2MULT_SESSION_END:
|
||||
dev_dbg(&serio->dev, "SE\n");
|
||||
break;
|
||||
|
||||
case PS2MULT_KB_SELECTOR:
|
||||
dev_dbg(&serio->dev, "KB\n");
|
||||
psm->in_port = &psm->ports[PS2MULT_KBD_PORT];
|
||||
break;
|
||||
|
||||
case PS2MULT_MS_SELECTOR:
|
||||
dev_dbg(&serio->dev, "MS\n");
|
||||
psm->in_port = &psm->ports[PS2MULT_MOUSE_PORT];
|
||||
break;
|
||||
|
||||
default:
|
||||
in_port = psm->in_port;
|
||||
if (in_port->registered)
|
||||
serio_interrupt(in_port->serio, data, dfl);
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&psm->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static struct serio_driver ps2mult_drv = {
|
||||
.driver = {
|
||||
.name = "ps2mult",
|
||||
},
|
||||
.description = "TQC PS/2 Multiplexer driver",
|
||||
.id_table = ps2mult_serio_ids,
|
||||
.interrupt = ps2mult_interrupt,
|
||||
.connect = ps2mult_connect,
|
||||
.disconnect = ps2mult_disconnect,
|
||||
.reconnect = ps2mult_reconnect,
|
||||
};
|
||||
|
||||
static int __init ps2mult_init(void)
|
||||
{
|
||||
return serio_register_driver(&ps2mult_drv);
|
||||
}
|
||||
|
||||
static void __exit ps2mult_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&ps2mult_drv);
|
||||
}
|
||||
|
||||
module_init(ps2mult_init);
|
||||
module_exit(ps2mult_exit);
|
||||
|
|
@ -37,7 +37,6 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <[email protected]>");
|
||||
MODULE_DESCRIPTION("Serio abstraction core");
|
||||
|
|
@ -56,7 +55,7 @@ static struct bus_type serio_bus;
|
|||
static void serio_add_port(struct serio *serio);
|
||||
static int serio_reconnect_port(struct serio *serio);
|
||||
static void serio_disconnect_port(struct serio *serio);
|
||||
static void serio_reconnect_chain(struct serio *serio);
|
||||
static void serio_reconnect_subtree(struct serio *serio);
|
||||
static void serio_attach_driver(struct serio_driver *drv);
|
||||
|
||||
static int serio_connect_driver(struct serio *serio, struct serio_driver *drv)
|
||||
|
|
@ -152,7 +151,7 @@ static void serio_find_driver(struct serio *serio)
|
|||
enum serio_event_type {
|
||||
SERIO_RESCAN_PORT,
|
||||
SERIO_RECONNECT_PORT,
|
||||
SERIO_RECONNECT_CHAIN,
|
||||
SERIO_RECONNECT_SUBTREE,
|
||||
SERIO_REGISTER_PORT,
|
||||
SERIO_ATTACH_DRIVER,
|
||||
};
|
||||
|
|
@ -292,8 +291,8 @@ static void serio_handle_event(void)
|
|||
serio_find_driver(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_RECONNECT_CHAIN:
|
||||
serio_reconnect_chain(event->object);
|
||||
case SERIO_RECONNECT_SUBTREE:
|
||||
serio_reconnect_subtree(event->object);
|
||||
break;
|
||||
|
||||
case SERIO_ATTACH_DRIVER:
|
||||
|
|
@ -330,12 +329,10 @@ static void serio_remove_pending_events(void *object)
|
|||
}
|
||||
|
||||
/*
|
||||
* Destroy child serio port (if any) that has not been fully registered yet.
|
||||
* Locate child serio port (if any) that has not been fully registered yet.
|
||||
*
|
||||
* Note that we rely on the fact that port can have only one child and therefore
|
||||
* only one child registration request can be pending. Additionally, children
|
||||
* are registered by driver's connect() handler so there can't be a grandchild
|
||||
* pending registration together with a child.
|
||||
* Children are registered by driver's connect() handler so there can't be a
|
||||
* grandchild pending registration together with a child.
|
||||
*/
|
||||
static struct serio *serio_get_pending_child(struct serio *parent)
|
||||
{
|
||||
|
|
@ -449,7 +446,7 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute *
|
|||
if (!strncmp(buf, "none", count)) {
|
||||
serio_disconnect_port(serio);
|
||||
} else if (!strncmp(buf, "reconnect", count)) {
|
||||
serio_reconnect_chain(serio);
|
||||
serio_reconnect_subtree(serio);
|
||||
} else if (!strncmp(buf, "rescan", count)) {
|
||||
serio_disconnect_port(serio);
|
||||
serio_find_driver(serio);
|
||||
|
|
@ -516,6 +513,8 @@ static void serio_init_port(struct serio *serio)
|
|||
__module_get(THIS_MODULE);
|
||||
|
||||
INIT_LIST_HEAD(&serio->node);
|
||||
INIT_LIST_HEAD(&serio->child_node);
|
||||
INIT_LIST_HEAD(&serio->children);
|
||||
spin_lock_init(&serio->lock);
|
||||
mutex_init(&serio->drv_mutex);
|
||||
device_initialize(&serio->dev);
|
||||
|
|
@ -538,12 +537,13 @@ static void serio_init_port(struct serio *serio)
|
|||
*/
|
||||
static void serio_add_port(struct serio *serio)
|
||||
{
|
||||
struct serio *parent = serio->parent;
|
||||
int error;
|
||||
|
||||
if (serio->parent) {
|
||||
serio_pause_rx(serio->parent);
|
||||
serio->parent->child = serio;
|
||||
serio_continue_rx(serio->parent);
|
||||
if (parent) {
|
||||
serio_pause_rx(parent);
|
||||
list_add_tail(&serio->child_node, &parent->children);
|
||||
serio_continue_rx(parent);
|
||||
}
|
||||
|
||||
list_add_tail(&serio->node, &serio_list);
|
||||
|
|
@ -559,15 +559,14 @@ static void serio_add_port(struct serio *serio)
|
|||
}
|
||||
|
||||
/*
|
||||
* serio_destroy_port() completes deregistration process and removes
|
||||
* serio_destroy_port() completes unregistration process and removes
|
||||
* port from the system
|
||||
*/
|
||||
static void serio_destroy_port(struct serio *serio)
|
||||
{
|
||||
struct serio *child;
|
||||
|
||||
child = serio_get_pending_child(serio);
|
||||
if (child) {
|
||||
while ((child = serio_get_pending_child(serio)) != NULL) {
|
||||
serio_remove_pending_events(child);
|
||||
put_device(&child->dev);
|
||||
}
|
||||
|
|
@ -577,7 +576,7 @@ static void serio_destroy_port(struct serio *serio)
|
|||
|
||||
if (serio->parent) {
|
||||
serio_pause_rx(serio->parent);
|
||||
serio->parent->child = NULL;
|
||||
list_del_init(&serio->child_node);
|
||||
serio_continue_rx(serio->parent);
|
||||
serio->parent = NULL;
|
||||
}
|
||||
|
|
@ -609,46 +608,82 @@ static int serio_reconnect_port(struct serio *serio)
|
|||
}
|
||||
|
||||
/*
|
||||
* Reconnect serio port and all its children (re-initialize attached devices)
|
||||
* Reconnect serio port and all its children (re-initialize attached
|
||||
* devices).
|
||||
*/
|
||||
static void serio_reconnect_chain(struct serio *serio)
|
||||
static void serio_reconnect_subtree(struct serio *root)
|
||||
{
|
||||
struct serio *s = root;
|
||||
int error;
|
||||
|
||||
do {
|
||||
if (serio_reconnect_port(serio)) {
|
||||
/* Ok, old children are now gone, we are done */
|
||||
break;
|
||||
error = serio_reconnect_port(s);
|
||||
if (!error) {
|
||||
/*
|
||||
* Reconnect was successful, move on to do the
|
||||
* first child.
|
||||
*/
|
||||
if (!list_empty(&s->children)) {
|
||||
s = list_first_entry(&s->children,
|
||||
struct serio, child_node);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
serio = serio->child;
|
||||
} while (serio);
|
||||
|
||||
/*
|
||||
* Either it was a leaf node or reconnect failed and it
|
||||
* became a leaf node. Continue reconnecting starting with
|
||||
* the next sibling of the parent node.
|
||||
*/
|
||||
while (s != root) {
|
||||
struct serio *parent = s->parent;
|
||||
|
||||
if (!list_is_last(&s->child_node, &parent->children)) {
|
||||
s = list_entry(s->child_node.next,
|
||||
struct serio, child_node);
|
||||
break;
|
||||
}
|
||||
|
||||
s = parent;
|
||||
}
|
||||
} while (s != root);
|
||||
}
|
||||
|
||||
/*
|
||||
* serio_disconnect_port() unbinds a port from its driver. As a side effect
|
||||
* all child ports are unbound and destroyed.
|
||||
* all children ports are unbound and destroyed.
|
||||
*/
|
||||
static void serio_disconnect_port(struct serio *serio)
|
||||
{
|
||||
struct serio *s, *parent;
|
||||
struct serio *s = serio;
|
||||
|
||||
/*
|
||||
* Children ports should be disconnected and destroyed
|
||||
* first; we travel the tree in depth-first order.
|
||||
*/
|
||||
while (!list_empty(&serio->children)) {
|
||||
|
||||
/* Locate a leaf */
|
||||
while (!list_empty(&s->children))
|
||||
s = list_first_entry(&s->children,
|
||||
struct serio, child_node);
|
||||
|
||||
if (serio->child) {
|
||||
/*
|
||||
* Children ports should be disconnected and destroyed
|
||||
* first, staring with the leaf one, since we don't want
|
||||
* to do recursion
|
||||
* Prune this leaf node unless it is the one we
|
||||
* started with.
|
||||
*/
|
||||
for (s = serio; s->child; s = s->child)
|
||||
/* empty */;
|
||||
|
||||
do {
|
||||
parent = s->parent;
|
||||
if (s != serio) {
|
||||
struct serio *parent = s->parent;
|
||||
|
||||
device_release_driver(&s->dev);
|
||||
serio_destroy_port(s);
|
||||
} while ((s = parent) != serio);
|
||||
|
||||
s = parent;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, no children left, now disconnect this port
|
||||
* OK, no children left, now disconnect this port.
|
||||
*/
|
||||
device_release_driver(&serio->dev);
|
||||
}
|
||||
|
|
@ -661,7 +696,7 @@ EXPORT_SYMBOL(serio_rescan);
|
|||
|
||||
void serio_reconnect(struct serio *serio)
|
||||
{
|
||||
serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
|
||||
serio_queue_event(serio, NULL, SERIO_RECONNECT_SUBTREE);
|
||||
}
|
||||
EXPORT_SYMBOL(serio_reconnect);
|
||||
|
||||
|
|
@ -689,14 +724,16 @@ void serio_unregister_port(struct serio *serio)
|
|||
EXPORT_SYMBOL(serio_unregister_port);
|
||||
|
||||
/*
|
||||
* Safely unregisters child port if one is present.
|
||||
* Safely unregisters children ports if they are present.
|
||||
*/
|
||||
void serio_unregister_child_port(struct serio *serio)
|
||||
{
|
||||
struct serio *s, *next;
|
||||
|
||||
mutex_lock(&serio_mutex);
|
||||
if (serio->child) {
|
||||
serio_disconnect_port(serio->child);
|
||||
serio_destroy_port(serio->child);
|
||||
list_for_each_entry_safe(s, next, &serio->children, child_node) {
|
||||
serio_disconnect_port(s);
|
||||
serio_destroy_port(s);
|
||||
}
|
||||
mutex_unlock(&serio_mutex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,37 @@ MODULE_DESCRIPTION("Generic support for sparse keymaps");
|
|||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION("0.1");
|
||||
|
||||
static unsigned int sparse_keymap_get_key_index(struct input_dev *dev,
|
||||
const struct key_entry *k)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int idx = 0;
|
||||
|
||||
for (key = dev->keycode; key->type != KE_END; key++) {
|
||||
if (key->type == KE_KEY) {
|
||||
if (key == k)
|
||||
break;
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static struct key_entry *sparse_keymap_entry_by_index(struct input_dev *dev,
|
||||
unsigned int index)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int key_cnt = 0;
|
||||
|
||||
for (key = dev->keycode; key->type != KE_END; key++)
|
||||
if (key->type == KE_KEY)
|
||||
if (key_cnt++ == index)
|
||||
return key;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sparse_keymap_entry_from_scancode - perform sparse keymap lookup
|
||||
* @dev: Input device using sparse keymap
|
||||
|
|
@ -64,16 +95,36 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
|
|||
}
|
||||
EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
|
||||
|
||||
static struct key_entry *sparse_keymap_locate(struct input_dev *dev,
|
||||
const struct input_keymap_entry *ke)
|
||||
{
|
||||
struct key_entry *key;
|
||||
unsigned int scancode;
|
||||
|
||||
if (ke->flags & INPUT_KEYMAP_BY_INDEX)
|
||||
key = sparse_keymap_entry_by_index(dev, ke->index);
|
||||
else if (input_scancode_to_scalar(ke, &scancode) == 0)
|
||||
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||
else
|
||||
key = NULL;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static int sparse_keymap_getkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int *keycode)
|
||||
struct input_keymap_entry *ke)
|
||||
{
|
||||
const struct key_entry *key;
|
||||
|
||||
if (dev->keycode) {
|
||||
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||
key = sparse_keymap_locate(dev, ke);
|
||||
if (key && key->type == KE_KEY) {
|
||||
*keycode = key->keycode;
|
||||
ke->keycode = key->keycode;
|
||||
if (!(ke->flags & INPUT_KEYMAP_BY_INDEX))
|
||||
ke->index =
|
||||
sparse_keymap_get_key_index(dev, key);
|
||||
ke->len = sizeof(key->code);
|
||||
memcpy(ke->scancode, &key->code, sizeof(key->code));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -82,20 +133,19 @@ static int sparse_keymap_getkeycode(struct input_dev *dev,
|
|||
}
|
||||
|
||||
static int sparse_keymap_setkeycode(struct input_dev *dev,
|
||||
unsigned int scancode,
|
||||
unsigned int keycode)
|
||||
const struct input_keymap_entry *ke,
|
||||
unsigned int *old_keycode)
|
||||
{
|
||||
struct key_entry *key;
|
||||
int old_keycode;
|
||||
|
||||
if (dev->keycode) {
|
||||
key = sparse_keymap_entry_from_scancode(dev, scancode);
|
||||
key = sparse_keymap_locate(dev, ke);
|
||||
if (key && key->type == KE_KEY) {
|
||||
old_keycode = key->keycode;
|
||||
key->keycode = keycode;
|
||||
set_bit(keycode, dev->keybit);
|
||||
if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
|
||||
clear_bit(old_keycode, dev->keybit);
|
||||
*old_keycode = key->keycode;
|
||||
key->keycode = ke->keycode;
|
||||
set_bit(ke->keycode, dev->keybit);
|
||||
if (!sparse_keymap_entry_from_keycode(dev, *old_keycode))
|
||||
clear_bit(*old_keycode, dev->keybit);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -159,15 +209,14 @@ int sparse_keymap_setup(struct input_dev *dev,
|
|||
|
||||
dev->keycode = map;
|
||||
dev->keycodemax = map_size;
|
||||
dev->getkeycode = sparse_keymap_getkeycode;
|
||||
dev->setkeycode = sparse_keymap_setkeycode;
|
||||
dev->getkeycode_new = sparse_keymap_getkeycode;
|
||||
dev->setkeycode_new = sparse_keymap_setkeycode;
|
||||
|
||||
return 0;
|
||||
|
||||
err_out:
|
||||
kfree(map);
|
||||
return error;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(sparse_keymap_setup);
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,17 @@ config TABLET_USB_GTCO
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called gtco.
|
||||
|
||||
config TABLET_USB_HANWANG
|
||||
tristate "Hanwang Art Master III tablet support (USB)"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select USB
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Hanwang Art
|
||||
Master III tablet.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called hanwang.
|
||||
|
||||
config TABLET_USB_KBTAB
|
||||
tristate "KB Gear JamStudio tablet support (USB)"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
|
|
|
|||
|
|
@ -8,5 +8,6 @@ wacom-objs := wacom_wac.o wacom_sys.o
|
|||
obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
|
||||
obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
|
||||
obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
|
||||
obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
|
||||
|
|
|
|||
446
drivers/input/tablet/hanwang.c
Normal file
446
drivers/input/tablet/hanwang.c
Normal file
|
|
@ -0,0 +1,446 @@
|
|||
/*
|
||||
* USB Hanwang tablet support
|
||||
*
|
||||
* Copyright (c) 2010 Xing Wei <weixing@hanwang.com.cn>
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb/input.h>
|
||||
|
||||
#define DRIVER_AUTHOR "Xing Wei <[email protected]>"
|
||||
#define DRIVER_DESC "USB Hanwang tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_HANWANG 0x0b57
|
||||
#define HANWANG_TABLET_INT_CLASS 0x0003
|
||||
#define HANWANG_TABLET_INT_SUB_CLASS 0x0001
|
||||
#define HANWANG_TABLET_INT_PROTOCOL 0x0002
|
||||
|
||||
#define ART_MASTER_PKGLEN_MAX 10
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define TOUCH_DEVICE_ID 0x03
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
/* match vendor and interface info */
|
||||
#define HANWANG_TABLET_DEVICE(vend, cl, sc, pr) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_VENDOR \
|
||||
| USB_DEVICE_ID_MATCH_INT_INFO, \
|
||||
.idVendor = (vend), \
|
||||
.bInterfaceClass = (cl), \
|
||||
.bInterfaceSubClass = (sc), \
|
||||
.bInterfaceProtocol = (pr)
|
||||
|
||||
enum hanwang_tablet_type {
|
||||
HANWANG_ART_MASTER_III,
|
||||
HANWANG_ART_MASTER_HD,
|
||||
};
|
||||
|
||||
struct hanwang {
|
||||
unsigned char *data;
|
||||
dma_addr_t data_dma;
|
||||
struct input_dev *dev;
|
||||
struct usb_device *usbdev;
|
||||
struct urb *irq;
|
||||
const struct hanwang_features *features;
|
||||
unsigned int current_tool;
|
||||
unsigned int current_id;
|
||||
char name[64];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
struct hanwang_features {
|
||||
unsigned short pid;
|
||||
char *name;
|
||||
enum hanwang_tablet_type type;
|
||||
int pkg_len;
|
||||
int max_x;
|
||||
int max_y;
|
||||
int max_tilt_x;
|
||||
int max_tilt_y;
|
||||
int max_pressure;
|
||||
};
|
||||
|
||||
static const struct hanwang_features features_array[] = {
|
||||
{ 0x8528, "Hanwang Art Master III 0906", HANWANG_ART_MASTER_III,
|
||||
ART_MASTER_PKGLEN_MAX, 0x5757, 0x3692, 0x3f, 0x7f, 2048 },
|
||||
{ 0x8529, "Hanwang Art Master III 0604", HANWANG_ART_MASTER_III,
|
||||
ART_MASTER_PKGLEN_MAX, 0x3d84, 0x2672, 0x3f, 0x7f, 2048 },
|
||||
{ 0x852a, "Hanwang Art Master III 1308", HANWANG_ART_MASTER_III,
|
||||
ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 },
|
||||
{ 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD,
|
||||
ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 },
|
||||
};
|
||||
|
||||
static const int hw_eventtypes[] = {
|
||||
EV_KEY, EV_ABS, EV_MSC,
|
||||
};
|
||||
|
||||
static const int hw_absevents[] = {
|
||||
ABS_X, ABS_Y, ABS_TILT_X, ABS_TILT_Y, ABS_WHEEL,
|
||||
ABS_RX, ABS_RY, ABS_PRESSURE, ABS_MISC,
|
||||
};
|
||||
|
||||
static const int hw_btnevents[] = {
|
||||
BTN_STYLUS, BTN_STYLUS2, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
|
||||
BTN_TOOL_MOUSE, BTN_TOOL_FINGER,
|
||||
BTN_0, BTN_1, BTN_2, BTN_3, BTN_4, BTN_5, BTN_6, BTN_7, BTN_8,
|
||||
};
|
||||
|
||||
static const int hw_mscevents[] = {
|
||||
MSC_SERIAL,
|
||||
};
|
||||
|
||||
static void hanwang_parse_packet(struct hanwang *hanwang)
|
||||
{
|
||||
unsigned char *data = hanwang->data;
|
||||
struct input_dev *input_dev = hanwang->dev;
|
||||
struct usb_device *dev = hanwang->usbdev;
|
||||
enum hanwang_tablet_type type = hanwang->features->type;
|
||||
int i;
|
||||
u16 x, y, p;
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x02: /* data packet */
|
||||
switch (data[1]) {
|
||||
case 0x80: /* tool prox out */
|
||||
hanwang->current_id = 0;
|
||||
input_report_key(input_dev, hanwang->current_tool, 0);
|
||||
break;
|
||||
|
||||
case 0xc2: /* first time tool prox in */
|
||||
switch (data[3] & 0xf0) {
|
||||
case 0x20: /* art_master III */
|
||||
case 0x30: /* art_master_HD */
|
||||
hanwang->current_id = STYLUS_DEVICE_ID;
|
||||
hanwang->current_tool = BTN_TOOL_PEN;
|
||||
input_report_key(input_dev, BTN_TOOL_PEN, 1);
|
||||
break;
|
||||
case 0xa0: /* art_master III */
|
||||
case 0xb0: /* art_master_HD */
|
||||
hanwang->current_id = ERASER_DEVICE_ID;
|
||||
hanwang->current_tool = BTN_TOOL_RUBBER;
|
||||
input_report_key(input_dev, BTN_TOOL_RUBBER, 1);
|
||||
break;
|
||||
default:
|
||||
hanwang->current_id = 0;
|
||||
dev_dbg(&dev->dev,
|
||||
"unknown tablet tool %02x ", data[0]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: /* tool data packet */
|
||||
x = (data[2] << 8) | data[3];
|
||||
y = (data[4] << 8) | data[5];
|
||||
|
||||
switch (type) {
|
||||
case HANWANG_ART_MASTER_III:
|
||||
p = (data[6] << 3) |
|
||||
((data[7] & 0xc0) >> 5) |
|
||||
(data[1] & 0x01);
|
||||
break;
|
||||
|
||||
case HANWANG_ART_MASTER_HD:
|
||||
p = (data[7] >> 6) | (data[6] << 2);
|
||||
break;
|
||||
|
||||
default:
|
||||
p = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(input_dev, ABS_X,
|
||||
le16_to_cpup((__le16 *)&x));
|
||||
input_report_abs(input_dev, ABS_Y,
|
||||
le16_to_cpup((__le16 *)&y));
|
||||
input_report_abs(input_dev, ABS_PRESSURE,
|
||||
le16_to_cpup((__le16 *)&p));
|
||||
input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f);
|
||||
input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f);
|
||||
input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04);
|
||||
break;
|
||||
}
|
||||
input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
|
||||
input_event(input_dev, EV_MSC, MSC_SERIAL,
|
||||
hanwang->features->pid);
|
||||
break;
|
||||
|
||||
case 0x0c:
|
||||
/* roll wheel */
|
||||
hanwang->current_id = PAD_DEVICE_ID;
|
||||
|
||||
switch (type) {
|
||||
case HANWANG_ART_MASTER_III:
|
||||
input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
|
||||
data[2] || data[3]);
|
||||
input_report_abs(input_dev, ABS_WHEEL, data[1]);
|
||||
input_report_key(input_dev, BTN_0, data[2]);
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(input_dev,
|
||||
BTN_1 + i, data[3] & (1 << i));
|
||||
break;
|
||||
|
||||
case HANWANG_ART_MASTER_HD:
|
||||
input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
|
||||
data[2] || data[3] || data[4] ||
|
||||
data[5] || data[6]);
|
||||
input_report_abs(input_dev, ABS_RX,
|
||||
((data[1] & 0x1f) << 8) | data[2]);
|
||||
input_report_abs(input_dev, ABS_RY,
|
||||
((data[3] & 0x1f) << 8) | data[4]);
|
||||
input_report_key(input_dev, BTN_0, data[5] & 0x01);
|
||||
for (i = 0; i < 4; i++) {
|
||||
input_report_key(input_dev,
|
||||
BTN_1 + i, data[5] & (1 << i));
|
||||
input_report_key(input_dev,
|
||||
BTN_5 + i, data[6] & (1 << i));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
|
||||
input_event(input_dev, EV_MSC, MSC_SERIAL, 0xffffffff);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_dbg(&dev->dev, "error packet %02x ", data[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void hanwang_irq(struct urb *urb)
|
||||
{
|
||||
struct hanwang *hanwang = urb->context;
|
||||
struct usb_device *dev = hanwang->usbdev;
|
||||
int retval;
|
||||
|
||||
switch (urb->status) {
|
||||
case 0:
|
||||
/* success */;
|
||||
hanwang_parse_packet(hanwang);
|
||||
break;
|
||||
case -ECONNRESET:
|
||||
case -ENOENT:
|
||||
case -ESHUTDOWN:
|
||||
/* this urb is terminated, clean up */
|
||||
dev_err(&dev->dev, "%s - urb shutting down with status: %d",
|
||||
__func__, urb->status);
|
||||
return;
|
||||
default:
|
||||
dev_err(&dev->dev, "%s - nonzero urb status received: %d",
|
||||
__func__, urb->status);
|
||||
break;
|
||||
}
|
||||
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval)
|
||||
dev_err(&dev->dev, "%s - usb_submit_urb failed with result %d",
|
||||
__func__, retval);
|
||||
}
|
||||
|
||||
static int hanwang_open(struct input_dev *dev)
|
||||
{
|
||||
struct hanwang *hanwang = input_get_drvdata(dev);
|
||||
|
||||
hanwang->irq->dev = hanwang->usbdev;
|
||||
if (usb_submit_urb(hanwang->irq, GFP_KERNEL))
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void hanwang_close(struct input_dev *dev)
|
||||
{
|
||||
struct hanwang *hanwang = input_get_drvdata(dev);
|
||||
|
||||
usb_kill_urb(hanwang->irq);
|
||||
}
|
||||
|
||||
static bool get_features(struct usb_device *dev, struct hanwang *hanwang)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(features_array); i++) {
|
||||
if (le16_to_cpu(dev->descriptor.idProduct) ==
|
||||
features_array[i].pid) {
|
||||
hanwang->features = &features_array[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int hanwang_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *dev = interface_to_usbdev(intf);
|
||||
struct usb_endpoint_descriptor *endpoint;
|
||||
struct hanwang *hanwang;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
hanwang = kzalloc(sizeof(struct hanwang), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!hanwang || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (!get_features(dev, hanwang)) {
|
||||
error = -ENXIO;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
hanwang->data = usb_alloc_coherent(dev, hanwang->features->pkg_len,
|
||||
GFP_KERNEL, &hanwang->data_dma);
|
||||
if (!hanwang->data) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
hanwang->irq = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!hanwang->irq) {
|
||||
error = -ENOMEM;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
hanwang->usbdev = dev;
|
||||
hanwang->dev = input_dev;
|
||||
|
||||
usb_make_path(dev, hanwang->phys, sizeof(hanwang->phys));
|
||||
strlcat(hanwang->phys, "/input0", sizeof(hanwang->phys));
|
||||
|
||||
strlcpy(hanwang->name, hanwang->features->name, sizeof(hanwang->name));
|
||||
input_dev->name = hanwang->name;
|
||||
input_dev->phys = hanwang->phys;
|
||||
usb_to_input_id(dev, &input_dev->id);
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
|
||||
input_set_drvdata(input_dev, hanwang);
|
||||
|
||||
input_dev->open = hanwang_open;
|
||||
input_dev->close = hanwang_close;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_eventtypes); ++i)
|
||||
__set_bit(hw_eventtypes[i], input_dev->evbit);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_absevents); ++i)
|
||||
__set_bit(hw_absevents[i], input_dev->absbit);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_btnevents); ++i)
|
||||
__set_bit(hw_btnevents[i], input_dev->keybit);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_mscevents); ++i)
|
||||
__set_bit(hw_mscevents[i], input_dev->mscbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
0, hanwang->features->max_x, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
0, hanwang->features->max_y, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_X,
|
||||
0, hanwang->features->max_tilt_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_Y,
|
||||
0, hanwang->features->max_tilt_y, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
0, hanwang->features->max_pressure, 0, 0);
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
usb_fill_int_urb(hanwang->irq, dev,
|
||||
usb_rcvintpipe(dev, endpoint->bEndpointAddress),
|
||||
hanwang->data, hanwang->features->pkg_len,
|
||||
hanwang_irq, hanwang, endpoint->bInterval);
|
||||
hanwang->irq->transfer_dma = hanwang->data_dma;
|
||||
hanwang->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
|
||||
error = input_register_device(hanwang->dev);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(intf, hanwang);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: usb_free_urb(hanwang->irq);
|
||||
fail2: usb_free_coherent(dev, hanwang->features->pkg_len,
|
||||
hanwang->data, hanwang->data_dma);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(hanwang);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
static void hanwang_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
struct hanwang *hanwang = usb_get_intfdata(intf);
|
||||
|
||||
input_unregister_device(hanwang->dev);
|
||||
usb_free_urb(hanwang->irq);
|
||||
usb_free_coherent(interface_to_usbdev(intf),
|
||||
hanwang->features->pkg_len, hanwang->data,
|
||||
hanwang->data_dma);
|
||||
kfree(hanwang);
|
||||
usb_set_intfdata(intf, NULL);
|
||||
}
|
||||
|
||||
static const struct usb_device_id hanwang_ids[] = {
|
||||
{ HANWANG_TABLET_DEVICE(USB_VENDOR_ID_HANWANG, HANWANG_TABLET_INT_CLASS,
|
||||
HANWANG_TABLET_INT_SUB_CLASS, HANWANG_TABLET_INT_PROTOCOL) },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, hanwang_ids);
|
||||
|
||||
static struct usb_driver hanwang_driver = {
|
||||
.name = "hanwang",
|
||||
.probe = hanwang_probe,
|
||||
.disconnect = hanwang_disconnect,
|
||||
.id_table = hanwang_ids,
|
||||
};
|
||||
|
||||
static int __init hanwang_init(void)
|
||||
{
|
||||
return usb_register(&hanwang_driver);
|
||||
}
|
||||
|
||||
static void __exit hanwang_exit(void)
|
||||
{
|
||||
usb_deregister(&hanwang_driver);
|
||||
}
|
||||
|
||||
module_init(hanwang_init);
|
||||
module_exit(hanwang_exit);
|
||||
|
|
@ -118,6 +118,7 @@ struct wacom {
|
|||
extern const struct usb_device_id wacom_ids[];
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
|
||||
void wacom_setup_device_quirks(struct wacom_features *features);
|
||||
void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -120,14 +120,16 @@ static int wacom_open(struct input_dev *dev)
|
|||
|
||||
out:
|
||||
mutex_unlock(&wacom->lock);
|
||||
if (retval)
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void wacom_close(struct input_dev *dev)
|
||||
{
|
||||
struct wacom *wacom = input_get_drvdata(dev);
|
||||
int autopm_error;
|
||||
|
||||
autopm_error = usb_autopm_get_interface(wacom->intf);
|
||||
|
||||
mutex_lock(&wacom->lock);
|
||||
usb_kill_urb(wacom->irq);
|
||||
|
|
@ -135,7 +137,8 @@ static void wacom_close(struct input_dev *dev)
|
|||
wacom->intf->needs_remote_wakeup = 0;
|
||||
mutex_unlock(&wacom->lock);
|
||||
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
if (!autopm_error)
|
||||
usb_autopm_put_interface(wacom->intf);
|
||||
}
|
||||
|
||||
static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hid_desc,
|
||||
|
|
@ -196,17 +199,30 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
|
|||
features->pktlen = WACOM_PKGLEN_TPC2FG;
|
||||
features->device_type = BTN_TOOL_TRIPLETAP;
|
||||
}
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->x_phy =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
features->unit = report[i + 9];
|
||||
features->unitExpo = report[i + 11];
|
||||
i += 12;
|
||||
if (features->type == BAMBOO_PT) {
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH;
|
||||
features->device_type = BTN_TOOL_TRIPLETAP;
|
||||
features->x_phy =
|
||||
get_unaligned_le16(&report[i + 5]);
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 8]);
|
||||
i += 15;
|
||||
} else {
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->x_phy =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
features->unit = report[i + 9];
|
||||
features->unitExpo = report[i + 11];
|
||||
i += 12;
|
||||
}
|
||||
} else if (pen) {
|
||||
/* penabled only accepts exact bytes of data */
|
||||
if (features->type == TABLETPC2FG)
|
||||
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
|
||||
if (features->type == BAMBOO_PT)
|
||||
features->pktlen = WACOM_PKGLEN_BBFUN;
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->x_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
|
|
@ -235,6 +251,15 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
|
|||
features->y_phy =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
i += 7;
|
||||
} else if (features->type == BAMBOO_PT) {
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH;
|
||||
features->device_type = BTN_TOOL_TRIPLETAP;
|
||||
features->y_phy =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->y_max =
|
||||
get_unaligned_le16(&report[i + 6]);
|
||||
i += 12;
|
||||
} else {
|
||||
features->y_max =
|
||||
features->x_max;
|
||||
|
|
@ -246,6 +271,8 @@ static int wacom_parse_hid(struct usb_interface *intf, struct hid_descriptor *hi
|
|||
/* penabled only accepts exact bytes of data */
|
||||
if (features->type == TABLETPC2FG)
|
||||
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
|
||||
if (features->type == BAMBOO_PT)
|
||||
features->pktlen = WACOM_PKGLEN_BBFUN;
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->y_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
|
|
@ -296,8 +323,9 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
|
|||
if (!rep_data)
|
||||
return error;
|
||||
|
||||
/* ask to report tablet data if it is 2FGT or not a Tablet PC */
|
||||
if (features->device_type == BTN_TOOL_TRIPLETAP) {
|
||||
/* ask to report tablet data if it is 2FGT Tablet PC or
|
||||
* not a Tablet PC */
|
||||
if (features->type == TABLETPC2FG) {
|
||||
do {
|
||||
rep_data[0] = 3;
|
||||
rep_data[1] = 4;
|
||||
|
|
@ -309,7 +337,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
|
|||
WAC_HID_FEATURE_REPORT, report_id,
|
||||
rep_data, 3);
|
||||
} while ((error < 0 || rep_data[1] != 4) && limit++ < 5);
|
||||
} else if (features->type != TABLETPC && features->type != TABLETPC2FG) {
|
||||
} else if (features->type != TABLETPC) {
|
||||
do {
|
||||
rep_data[0] = 2;
|
||||
rep_data[1] = 2;
|
||||
|
|
@ -334,11 +362,16 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
|
|||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
struct hid_descriptor *hid_desc;
|
||||
|
||||
/* default device to penabled */
|
||||
/* default features */
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->x_fuzz = 4;
|
||||
features->y_fuzz = 4;
|
||||
features->pressure_fuzz = 0;
|
||||
features->distance_fuzz = 0;
|
||||
|
||||
/* only Tablet PCs need to retrieve the info */
|
||||
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG))
|
||||
/* only Tablet PCs and Bamboo P&T need to retrieve the info */
|
||||
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
|
||||
(features->type != BAMBOO_PT))
|
||||
goto out;
|
||||
|
||||
if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
|
||||
|
|
@ -353,12 +386,6 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
|
|||
if (error)
|
||||
goto out;
|
||||
|
||||
/* touch device found but size is not defined. use default */
|
||||
if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
|
||||
features->x_max = 1023;
|
||||
features->y_max = 1023;
|
||||
}
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
|
@ -494,9 +521,11 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
|
|||
if (error)
|
||||
goto fail2;
|
||||
|
||||
wacom_setup_device_quirks(features);
|
||||
|
||||
strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
|
||||
|
||||
if (features->type == TABLETPC || features->type == TABLETPC2FG) {
|
||||
if (features->quirks & WACOM_QUIRK_MULTI_INPUT) {
|
||||
/* Append the device type to the name */
|
||||
strlcat(wacom_wac->name,
|
||||
features->device_type == BTN_TOOL_PEN ?
|
||||
|
|
|
|||
|
|
@ -857,6 +857,134 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
|
|||
return retval;
|
||||
}
|
||||
|
||||
static int wacom_bpt_touch(struct wacom_wac *wacom)
|
||||
{
|
||||
struct wacom_features *features = &wacom->features;
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *data = wacom->data;
|
||||
int sp = 0, sx = 0, sy = 0, count = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
int p = data[9 * i + 2];
|
||||
input_mt_slot(input, i);
|
||||
/*
|
||||
* Touch events need to be disabled while stylus is
|
||||
* in proximity because user's hand is resting on touchpad
|
||||
* and sending unwanted events. User expects tablet buttons
|
||||
* to continue working though.
|
||||
*/
|
||||
if (p && !wacom->shared->stylus_in_proximity) {
|
||||
int x = get_unaligned_be16(&data[9 * i + 3]) & 0x7ff;
|
||||
int y = get_unaligned_be16(&data[9 * i + 5]) & 0x7ff;
|
||||
if (features->quirks & WACOM_QUIRK_BBTOUCH_LOWRES) {
|
||||
x <<= 5;
|
||||
y <<= 5;
|
||||
}
|
||||
input_report_abs(input, ABS_MT_PRESSURE, p);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
if (wacom->id[i] < 0)
|
||||
wacom->id[i] = wacom->trk_id++ & MAX_TRACKING_ID;
|
||||
if (!count++)
|
||||
sp = p, sx = x, sy = y;
|
||||
} else {
|
||||
wacom->id[i] = -1;
|
||||
}
|
||||
input_report_abs(input, ABS_MT_TRACKING_ID, wacom->id[i]);
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_TOUCH, count > 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, count == 1);
|
||||
input_report_key(input, BTN_TOOL_DOUBLETAP, count == 2);
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, sp);
|
||||
input_report_abs(input, ABS_X, sx);
|
||||
input_report_abs(input, ABS_Y, sy);
|
||||
|
||||
input_report_key(input, BTN_LEFT, (data[1] & 0x08) != 0);
|
||||
input_report_key(input, BTN_FORWARD, (data[1] & 0x04) != 0);
|
||||
input_report_key(input, BTN_BACK, (data[1] & 0x02) != 0);
|
||||
input_report_key(input, BTN_RIGHT, (data[1] & 0x01) != 0);
|
||||
|
||||
input_sync(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_bpt_pen(struct wacom_wac *wacom)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
unsigned char *data = wacom->data;
|
||||
int prox = 0, x = 0, y = 0, p = 0, d = 0, pen = 0, btn1 = 0, btn2 = 0;
|
||||
|
||||
/*
|
||||
* Similar to Graphire protocol, data[1] & 0x20 is proximity and
|
||||
* data[1] & 0x18 is tool ID. 0x30 is safety check to ignore
|
||||
* 2 unused tool ID's.
|
||||
*/
|
||||
prox = (data[1] & 0x30) == 0x30;
|
||||
|
||||
/*
|
||||
* All reports shared between PEN and RUBBER tool must be
|
||||
* forced to a known starting value (zero) when transitioning to
|
||||
* out-of-prox.
|
||||
*
|
||||
* If not reset then, to userspace, it will look like lost events
|
||||
* if new tool comes in-prox with same values as previous tool sent.
|
||||
*
|
||||
* Hardware does report zero in most out-of-prox cases but not all.
|
||||
*/
|
||||
if (prox) {
|
||||
if (!wacom->shared->stylus_in_proximity) {
|
||||
if (data[1] & 0x08) {
|
||||
wacom->tool[0] = BTN_TOOL_RUBBER;
|
||||
wacom->id[0] = ERASER_DEVICE_ID;
|
||||
} else {
|
||||
wacom->tool[0] = BTN_TOOL_PEN;
|
||||
wacom->id[0] = STYLUS_DEVICE_ID;
|
||||
}
|
||||
wacom->shared->stylus_in_proximity = true;
|
||||
}
|
||||
x = le16_to_cpup((__le16 *)&data[2]);
|
||||
y = le16_to_cpup((__le16 *)&data[4]);
|
||||
p = le16_to_cpup((__le16 *)&data[6]);
|
||||
d = data[8];
|
||||
pen = data[1] & 0x01;
|
||||
btn1 = data[1] & 0x02;
|
||||
btn2 = data[1] & 0x04;
|
||||
}
|
||||
|
||||
input_report_key(input, BTN_TOUCH, pen);
|
||||
input_report_key(input, BTN_STYLUS, btn1);
|
||||
input_report_key(input, BTN_STYLUS2, btn2);
|
||||
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, p);
|
||||
input_report_abs(input, ABS_DISTANCE, d);
|
||||
|
||||
if (!prox) {
|
||||
wacom->id[0] = 0;
|
||||
wacom->shared->stylus_in_proximity = false;
|
||||
}
|
||||
|
||||
input_report_key(input, wacom->tool[0], prox); /* PEN or RUBBER */
|
||||
input_report_abs(input, ABS_MISC, wacom->id[0]); /* TOOL ID */
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
|
||||
{
|
||||
if (len == WACOM_PKGLEN_BBTOUCH)
|
||||
return wacom_bpt_touch(wacom);
|
||||
else if (len == WACOM_PKGLEN_BBFUN)
|
||||
return wacom_bpt_pen(wacom);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
{
|
||||
bool sync;
|
||||
|
|
@ -902,6 +1030,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
|||
sync = wacom_tpc_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
case BAMBOO_PT:
|
||||
sync = wacom_bpt_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
sync = false;
|
||||
break;
|
||||
|
|
@ -911,26 +1043,17 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
|||
input_sync(wacom_wac->input);
|
||||
}
|
||||
|
||||
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
||||
static void wacom_setup_cintiq(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct input_dev *input_dev = wacom_wac->input;
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SERIAL);
|
||||
input_set_capability(input_dev, EV_REL, REL_WHEEL);
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
__set_bit(BTN_MIDDLE, input_dev->keybit);
|
||||
__set_bit(BTN_SIDE, input_dev->keybit);
|
||||
__set_bit(BTN_EXTRA, input_dev->keybit);
|
||||
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_BRUSH, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PENCIL, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_AIRBRUSH, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_LENS, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS2, input_dev->keybit);
|
||||
|
||||
|
|
@ -939,10 +1062,55 @@ static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
|||
input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
|
||||
}
|
||||
|
||||
static void wacom_setup_intuos(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct input_dev *input_dev = wacom_wac->input;
|
||||
|
||||
input_set_capability(input_dev, EV_REL, REL_WHEEL);
|
||||
|
||||
wacom_setup_cintiq(wacom_wac);
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
__set_bit(BTN_MIDDLE, input_dev->keybit);
|
||||
__set_bit(BTN_SIDE, input_dev->keybit);
|
||||
__set_bit(BTN_EXTRA, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_LENS, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
|
||||
}
|
||||
|
||||
void wacom_setup_device_quirks(struct wacom_features *features)
|
||||
{
|
||||
|
||||
/* touch device found but size is not defined. use default */
|
||||
if (features->device_type == BTN_TOOL_DOUBLETAP && !features->x_max) {
|
||||
features->x_max = 1023;
|
||||
features->y_max = 1023;
|
||||
}
|
||||
|
||||
/* these device have multiple inputs */
|
||||
if (features->type == TABLETPC || features->type == TABLETPC2FG ||
|
||||
features->type == BAMBOO_PT)
|
||||
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
|
||||
|
||||
/* quirks for bamboo touch */
|
||||
if (features->type == BAMBOO_PT &&
|
||||
features->device_type == BTN_TOOL_TRIPLETAP) {
|
||||
features->x_max <<= 5;
|
||||
features->y_max <<= 5;
|
||||
features->x_fuzz <<= 5;
|
||||
features->y_fuzz <<= 5;
|
||||
features->pressure_max = 256;
|
||||
features->pressure_fuzz = 16;
|
||||
features->quirks |= WACOM_QUIRK_BBTOUCH_LOWRES;
|
||||
}
|
||||
}
|
||||
|
||||
void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac)
|
||||
{
|
||||
|
|
@ -953,9 +1121,12 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, features->x_max, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, features->y_max,
|
||||
features->y_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max,
|
||||
features->pressure_fuzz, 0);
|
||||
|
||||
__set_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
|
|
@ -1005,9 +1176,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
__set_bit(BTN_9, input_dev->keybit);
|
||||
/* fall through */
|
||||
|
||||
case CINTIQ:
|
||||
for (i = 0; i < 8; i++)
|
||||
__set_bit(BTN_0 + i, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_RX, 0, 4096, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
|
||||
wacom_setup_cintiq(wacom_wac);
|
||||
break;
|
||||
|
||||
case INTUOS3:
|
||||
case INTUOS3L:
|
||||
case CINTIQ:
|
||||
__set_bit(BTN_4, input_dev->keybit);
|
||||
__set_bit(BTN_5, input_dev->keybit);
|
||||
__set_bit(BTN_6, input_dev->keybit);
|
||||
|
|
@ -1078,6 +1259,38 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||
case PENPARTNER:
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
break;
|
||||
|
||||
case BAMBOO_PT:
|
||||
__clear_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
if (features->device_type == BTN_TOOL_TRIPLETAP) {
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_FORWARD, input_dev->keybit);
|
||||
__set_bit(BTN_BACK, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
|
||||
input_mt_create_slots(input_dev, 2);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, features->x_max,
|
||||
features->x_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, features->y_max,
|
||||
features->y_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_PRESSURE,
|
||||
0, features->pressure_max,
|
||||
features->pressure_fuzz, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0,
|
||||
MAX_TRACKING_ID, 0, 0);
|
||||
} else if (features->device_type == BTN_TOOL_PEN) {
|
||||
__set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
__set_bit(BTN_STYLUS2, input_dev->keybit);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1215,6 +1428,14 @@ static const struct wacom_features wacom_features_0xE3 =
|
|||
{ "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255, 0, TABLETPC2FG };
|
||||
static const struct wacom_features wacom_features_0x47 =
|
||||
{ "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023, 31, INTUOS };
|
||||
static struct wacom_features wacom_features_0xD0 =
|
||||
{ "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xD1 =
|
||||
{ "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xD2 =
|
||||
{ "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023, 63, BAMBOO_PT };
|
||||
static struct wacom_features wacom_features_0xD3 =
|
||||
{ "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13530, 1023, 63, BAMBOO_PT };
|
||||
|
||||
#define USB_DEVICE_WACOM(prod) \
|
||||
USB_DEVICE(USB_VENDOR_ID_WACOM, prod), \
|
||||
|
|
@ -1279,6 +1500,10 @@ const struct usb_device_id wacom_ids[] = {
|
|||
{ USB_DEVICE_WACOM(0xC6) },
|
||||
{ USB_DEVICE_WACOM(0xC7) },
|
||||
{ USB_DEVICE_WACOM(0xCE) },
|
||||
{ USB_DEVICE_WACOM(0xD0) },
|
||||
{ USB_DEVICE_WACOM(0xD1) },
|
||||
{ USB_DEVICE_WACOM(0xD2) },
|
||||
{ USB_DEVICE_WACOM(0xD3) },
|
||||
{ USB_DEVICE_WACOM(0xF0) },
|
||||
{ USB_DEVICE_WACOM(0xCC) },
|
||||
{ USB_DEVICE_WACOM(0x90) },
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#define WACOM_PKGLEN_INTUOS 10
|
||||
#define WACOM_PKGLEN_TPC1FG 5
|
||||
#define WACOM_PKGLEN_TPC2FG 14
|
||||
#define WACOM_PKGLEN_BBTOUCH 20
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
|
|
@ -37,6 +38,13 @@
|
|||
#define WACOM_REPORT_TPC1FG 6
|
||||
#define WACOM_REPORT_TPC2FG 13
|
||||
|
||||
/* device quirks */
|
||||
#define WACOM_QUIRK_MULTI_INPUT 0x0001
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
|
||||
|
||||
/* largest reported tracking id */
|
||||
#define MAX_TRACKING_ID 0xfff
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
GRAPHIRE,
|
||||
|
|
@ -44,6 +52,7 @@ enum {
|
|||
PTU,
|
||||
PL,
|
||||
DTU,
|
||||
BAMBOO_PT,
|
||||
INTUOS,
|
||||
INTUOS3S,
|
||||
INTUOS3,
|
||||
|
|
@ -73,6 +82,11 @@ struct wacom_features {
|
|||
int y_phy;
|
||||
unsigned char unit;
|
||||
unsigned char unitExpo;
|
||||
int x_fuzz;
|
||||
int y_fuzz;
|
||||
int pressure_fuzz;
|
||||
int distance_fuzz;
|
||||
unsigned quirks;
|
||||
};
|
||||
|
||||
struct wacom_shared {
|
||||
|
|
@ -86,6 +100,7 @@ struct wacom_wac {
|
|||
int id[3];
|
||||
__u32 serial[2];
|
||||
int last_finger;
|
||||
int trk_id;
|
||||
struct wacom_features features;
|
||||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
|
|
|
|||
|
|
@ -98,6 +98,18 @@ config TOUCHSCREEN_BITSY
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called h3600_ts_input.
|
||||
|
||||
config TOUCHSCREEN_BU21013
|
||||
tristate "BU21013 based touch panel controllers"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have a bu21013 touchscreen connected to
|
||||
your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bu21013_ts.
|
||||
|
||||
config TOUCHSCREEN_CY8CTMG110
|
||||
tristate "cy8ctmg110 touchscreen"
|
||||
depends on I2C
|
||||
|
|
@ -214,6 +226,16 @@ config TOUCHSCREEN_WACOM_W8001
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom_w8001.
|
||||
|
||||
config TOUCHSCREEN_LPC32XX
|
||||
tristate "LPC32XX touchscreen controller"
|
||||
depends on ARCH_LPC32XX
|
||||
help
|
||||
Say Y here if you have a LPC32XX device and want
|
||||
to support the built-in touchscreen.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lpc32xx_ts.
|
||||
|
||||
config TOUCHSCREEN_MCS5000
|
||||
tristate "MELFAS MCS-5000 touchscreen"
|
||||
depends on I2C
|
||||
|
|
@ -250,6 +272,18 @@ config TOUCHSCREEN_INEXIO
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called inexio.
|
||||
|
||||
config TOUCHSCREEN_INTEL_MID
|
||||
tristate "Intel MID platform resistive touchscreen"
|
||||
depends on INTEL_SCU_IPC
|
||||
help
|
||||
Say Y here if you have a Intel MID based touchscreen in
|
||||
your system.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called intel_mid_touch.
|
||||
|
||||
config TOUCHSCREEN_MK712
|
||||
tristate "ICS MicroClock MK712 touchscreen"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
|
||||
|
|
@ -23,6 +24,8 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
|
||||
|
|
|
|||
|
|
@ -191,13 +191,12 @@ struct ad7877 {
|
|||
struct spi_message msg;
|
||||
|
||||
struct mutex mutex;
|
||||
unsigned disabled:1; /* P: mutex */
|
||||
unsigned gpio3:1; /* P: mutex */
|
||||
unsigned gpio4:1; /* P: mutex */
|
||||
bool disabled; /* P: mutex */
|
||||
bool gpio3; /* P: mutex */
|
||||
bool gpio4; /* P: mutex */
|
||||
|
||||
spinlock_t lock;
|
||||
struct timer_list timer; /* P: lock */
|
||||
unsigned pending:1; /* P: lock */
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
|
|
@ -206,8 +205,8 @@ struct ad7877 {
|
|||
u16 conversion_data[AD7877_NR_SENSE] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int gpio3;
|
||||
module_param(gpio3, int, 0);
|
||||
static bool gpio3;
|
||||
module_param(gpio3, bool, 0);
|
||||
MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
|
||||
|
||||
/*
|
||||
|
|
@ -230,6 +229,7 @@ static int ad7877_read(struct spi_device *spi, u16 reg)
|
|||
AD7877_READADD(reg));
|
||||
req->xfer[0].tx_buf = &req->command;
|
||||
req->xfer[0].len = 2;
|
||||
req->xfer[0].cs_change = 1;
|
||||
|
||||
req->xfer[1].rx_buf = &req->sample;
|
||||
req->xfer[1].len = 2;
|
||||
|
|
@ -295,20 +295,25 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
|
|||
|
||||
req->xfer[0].tx_buf = &req->reset;
|
||||
req->xfer[0].len = 2;
|
||||
req->xfer[0].cs_change = 1;
|
||||
|
||||
req->xfer[1].tx_buf = &req->ref_on;
|
||||
req->xfer[1].len = 2;
|
||||
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
|
||||
req->xfer[1].cs_change = 1;
|
||||
|
||||
req->xfer[2].tx_buf = &req->command;
|
||||
req->xfer[2].len = 2;
|
||||
req->xfer[2].delay_usecs = ts->vref_delay_usecs;
|
||||
req->xfer[2].cs_change = 1;
|
||||
|
||||
req->xfer[3].rx_buf = &req->sample;
|
||||
req->xfer[3].len = 2;
|
||||
req->xfer[3].cs_change = 1;
|
||||
|
||||
req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/
|
||||
req->xfer[4].len = 2;
|
||||
req->xfer[4].cs_change = 1;
|
||||
|
||||
req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/
|
||||
req->xfer[5].len = 2;
|
||||
|
|
@ -327,7 +332,7 @@ static int ad7877_read_adc(struct spi_device *spi, unsigned command)
|
|||
return status ? : sample;
|
||||
}
|
||||
|
||||
static void ad7877_rx(struct ad7877 *ts)
|
||||
static int ad7877_process_data(struct ad7877 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
unsigned Rt;
|
||||
|
|
@ -354,11 +359,25 @@ static void ad7877_rx(struct ad7877 *ts)
|
|||
Rt /= z1;
|
||||
Rt = (Rt + 2047) >> 12;
|
||||
|
||||
/*
|
||||
* Sample found inconsistent, pressure is beyond
|
||||
* the maximum. Don't report it to user space.
|
||||
*/
|
||||
if (Rt > ts->pressure_max)
|
||||
return -EINVAL;
|
||||
|
||||
if (!timer_pending(&ts->timer))
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
|
||||
input_report_abs(input_dev, ABS_X, x);
|
||||
input_report_abs(input_dev, ABS_Y, y);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, Rt);
|
||||
input_sync(input_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static inline void ad7877_ts_event_release(struct ad7877 *ts)
|
||||
|
|
@ -366,72 +385,56 @@ static inline void ad7877_ts_event_release(struct ad7877 *ts)
|
|||
struct input_dev *input_dev = ts->input;
|
||||
|
||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void ad7877_timer(unsigned long handle)
|
||||
{
|
||||
struct ad7877 *ts = (void *)handle;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
ad7877_ts_event_release(ts);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7877_irq(int irq, void *handle)
|
||||
{
|
||||
struct ad7877 *ts = handle;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* The repeated conversion sequencer controlled by TMR kicked off
|
||||
* too fast. We ignore the last and process the sample sequence
|
||||
* currently in the queue. It can't be older than 9.4ms, and we
|
||||
* need to avoid that ts->msg doesn't get issued twice while in work.
|
||||
*/
|
||||
error = spi_sync(ts->spi, &ts->msg);
|
||||
if (error) {
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
if (!ts->pending) {
|
||||
ts->pending = 1;
|
||||
|
||||
status = spi_async(ts->spi, &ts->msg);
|
||||
if (status)
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", status);
|
||||
}
|
||||
error = ad7877_process_data(ts);
|
||||
if (!error)
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7877_callback(void *_ts)
|
||||
{
|
||||
struct ad7877 *ts = _ts;
|
||||
|
||||
spin_lock_irq(&ts->lock);
|
||||
|
||||
ad7877_rx(ts);
|
||||
ts->pending = 0;
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
|
||||
spin_unlock_irq(&ts->lock);
|
||||
}
|
||||
|
||||
static void ad7877_disable(struct ad7877 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (!ts->disabled) {
|
||||
ts->disabled = 1;
|
||||
ts->disabled = true;
|
||||
disable_irq(ts->spi->irq);
|
||||
|
||||
/* Wait for spi_async callback */
|
||||
while (ts->pending)
|
||||
msleep(1);
|
||||
|
||||
if (del_timer_sync(&ts->timer))
|
||||
ad7877_ts_event_release(ts);
|
||||
}
|
||||
|
||||
/* we know the chip's in lowpower mode since we always
|
||||
/*
|
||||
* We know the chip's in lowpower mode since we always
|
||||
* leave it that way after every request
|
||||
*/
|
||||
|
||||
|
|
@ -443,7 +446,7 @@ static void ad7877_enable(struct ad7877 *ts)
|
|||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (ts->disabled) {
|
||||
ts->disabled = 0;
|
||||
ts->disabled = false;
|
||||
enable_irq(ts->spi->irq);
|
||||
}
|
||||
|
||||
|
|
@ -453,7 +456,7 @@ static void ad7877_enable(struct ad7877 *ts)
|
|||
#define SHOW(name) static ssize_t \
|
||||
name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct ad7877 *ts = dev_get_drvdata(dev); \
|
||||
struct ad7877 *ts = dev_get_drvdata(dev); \
|
||||
ssize_t v = ad7877_read_adc(ts->spi, \
|
||||
AD7877_READ_CHAN(name)); \
|
||||
if (v < 0) \
|
||||
|
|
@ -473,7 +476,7 @@ SHOW(temp2)
|
|||
static ssize_t ad7877_disable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->disabled);
|
||||
}
|
||||
|
|
@ -503,7 +506,7 @@ static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
|
|||
static ssize_t ad7877_dac_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->dac);
|
||||
}
|
||||
|
|
@ -533,7 +536,7 @@ static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
|
|||
static ssize_t ad7877_gpio3_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio3);
|
||||
}
|
||||
|
|
@ -564,7 +567,7 @@ static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
|
|||
static ssize_t ad7877_gpio4_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio4);
|
||||
}
|
||||
|
|
@ -597,16 +600,35 @@ static struct attribute *ad7877_attributes[] = {
|
|||
&dev_attr_temp2.attr,
|
||||
&dev_attr_aux1.attr,
|
||||
&dev_attr_aux2.attr,
|
||||
&dev_attr_aux3.attr,
|
||||
&dev_attr_bat1.attr,
|
||||
&dev_attr_bat2.attr,
|
||||
&dev_attr_disable.attr,
|
||||
&dev_attr_dac.attr,
|
||||
&dev_attr_gpio3.attr,
|
||||
&dev_attr_gpio4.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static mode_t ad7877_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
mode_t mode = attr->mode;
|
||||
|
||||
if (attr == &dev_attr_aux3.attr) {
|
||||
if (gpio3)
|
||||
mode = 0;
|
||||
} else if (attr == &dev_attr_gpio3.attr) {
|
||||
if (!gpio3)
|
||||
mode = 0;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group ad7877_attr_group = {
|
||||
.attrs = ad7877_attributes,
|
||||
.is_visible = ad7877_attr_is_visible,
|
||||
.attrs = ad7877_attributes,
|
||||
};
|
||||
|
||||
static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
|
||||
|
|
@ -635,22 +657,25 @@ static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
|
|||
|
||||
spi_message_init(m);
|
||||
|
||||
m->complete = ad7877_callback;
|
||||
m->context = ts;
|
||||
|
||||
ts->xfer[0].tx_buf = &ts->cmd_crtl1;
|
||||
ts->xfer[0].len = 2;
|
||||
ts->xfer[0].cs_change = 1;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[0], m);
|
||||
|
||||
ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
|
||||
ts->xfer[1].len = 2;
|
||||
ts->xfer[1].cs_change = 1;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[1], m);
|
||||
|
||||
for (i = 0; i < 11; i++) {
|
||||
for (i = 0; i < AD7877_NR_SENSE; i++) {
|
||||
ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
|
||||
ts->xfer[i + 2].len = 2;
|
||||
if (i < (AD7877_NR_SENSE - 1))
|
||||
ts->xfer[i + 2].cs_change = 1;
|
||||
spi_message_add_tail(&ts->xfer[i + 2], m);
|
||||
}
|
||||
}
|
||||
|
|
@ -718,6 +743,8 @@ static int __devinit ad7877_probe(struct spi_device *spi)
|
|||
input_dev->phys = ts->phys;
|
||||
input_dev->dev.parent = &spi->dev;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(ABS_X, input_dev->absbit);
|
||||
__set_bit(ABS_Y, input_dev->absbit);
|
||||
|
|
@ -752,8 +779,9 @@ static int __devinit ad7877_probe(struct spi_device *spi)
|
|||
|
||||
/* Request AD7877 /DAV GPIO interrupt */
|
||||
|
||||
err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING,
|
||||
spi->dev.driver->name, ts);
|
||||
err = request_threaded_irq(spi->irq, NULL, ad7877_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
spi->dev.driver->name, ts);
|
||||
if (err) {
|
||||
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
|
||||
goto err_free_mem;
|
||||
|
|
@ -763,20 +791,12 @@ static int __devinit ad7877_probe(struct spi_device *spi)
|
|||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
err = device_create_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr_group;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_attr:
|
||||
device_remove_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
err_remove_attr_group:
|
||||
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
err_free_irq:
|
||||
|
|
@ -790,11 +810,9 @@ err_free_mem:
|
|||
|
||||
static int __devexit ad7877_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
device_remove_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
|
||||
ad7877_disable(ts);
|
||||
free_irq(ts->spi->irq, ts);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
648
drivers/input/touchscreen/bu21013_ts.c
Normal file
648
drivers/input/touchscreen/bu21013_ts.c
Normal file
|
|
@ -0,0 +1,648 @@
|
|||
/*
|
||||
* Copyright (C) ST-Ericsson SA 2010
|
||||
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
|
||||
* License terms:GNU General Public License (GPL) version 2
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/bu21013.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define PEN_DOWN_INTR 0
|
||||
#define MAX_FINGERS 2
|
||||
#define RESET_DELAY 30
|
||||
#define PENUP_TIMEOUT (10)
|
||||
#define DELTA_MIN 16
|
||||
#define MASK_BITS 0x03
|
||||
#define SHIFT_8 8
|
||||
#define SHIFT_2 2
|
||||
#define LENGTH_OF_BUFFER 11
|
||||
#define I2C_RETRY_COUNT 5
|
||||
|
||||
#define BU21013_SENSORS_BTN_0_7_REG 0x70
|
||||
#define BU21013_SENSORS_BTN_8_15_REG 0x71
|
||||
#define BU21013_SENSORS_BTN_16_23_REG 0x72
|
||||
#define BU21013_X1_POS_MSB_REG 0x73
|
||||
#define BU21013_X1_POS_LSB_REG 0x74
|
||||
#define BU21013_Y1_POS_MSB_REG 0x75
|
||||
#define BU21013_Y1_POS_LSB_REG 0x76
|
||||
#define BU21013_X2_POS_MSB_REG 0x77
|
||||
#define BU21013_X2_POS_LSB_REG 0x78
|
||||
#define BU21013_Y2_POS_MSB_REG 0x79
|
||||
#define BU21013_Y2_POS_LSB_REG 0x7A
|
||||
#define BU21013_INT_CLR_REG 0xE8
|
||||
#define BU21013_INT_MODE_REG 0xE9
|
||||
#define BU21013_GAIN_REG 0xEA
|
||||
#define BU21013_OFFSET_MODE_REG 0xEB
|
||||
#define BU21013_XY_EDGE_REG 0xEC
|
||||
#define BU21013_RESET_REG 0xED
|
||||
#define BU21013_CALIB_REG 0xEE
|
||||
#define BU21013_DONE_REG 0xEF
|
||||
#define BU21013_SENSOR_0_7_REG 0xF0
|
||||
#define BU21013_SENSOR_8_15_REG 0xF1
|
||||
#define BU21013_SENSOR_16_23_REG 0xF2
|
||||
#define BU21013_POS_MODE1_REG 0xF3
|
||||
#define BU21013_POS_MODE2_REG 0xF4
|
||||
#define BU21013_CLK_MODE_REG 0xF5
|
||||
#define BU21013_IDLE_REG 0xFA
|
||||
#define BU21013_FILTER_REG 0xFB
|
||||
#define BU21013_TH_ON_REG 0xFC
|
||||
#define BU21013_TH_OFF_REG 0xFD
|
||||
|
||||
|
||||
#define BU21013_RESET_ENABLE 0x01
|
||||
|
||||
#define BU21013_SENSORS_EN_0_7 0x3F
|
||||
#define BU21013_SENSORS_EN_8_15 0xFC
|
||||
#define BU21013_SENSORS_EN_16_23 0x1F
|
||||
|
||||
#define BU21013_POS_MODE1_0 0x02
|
||||
#define BU21013_POS_MODE1_1 0x04
|
||||
#define BU21013_POS_MODE1_2 0x08
|
||||
|
||||
#define BU21013_POS_MODE2_ZERO 0x01
|
||||
#define BU21013_POS_MODE2_AVG1 0x02
|
||||
#define BU21013_POS_MODE2_AVG2 0x04
|
||||
#define BU21013_POS_MODE2_EN_XY 0x08
|
||||
#define BU21013_POS_MODE2_EN_RAW 0x10
|
||||
#define BU21013_POS_MODE2_MULTI 0x80
|
||||
|
||||
#define BU21013_CLK_MODE_DIV 0x01
|
||||
#define BU21013_CLK_MODE_EXT 0x02
|
||||
#define BU21013_CLK_MODE_CALIB 0x80
|
||||
|
||||
#define BU21013_IDLET_0 0x01
|
||||
#define BU21013_IDLET_1 0x02
|
||||
#define BU21013_IDLET_2 0x04
|
||||
#define BU21013_IDLET_3 0x08
|
||||
#define BU21013_IDLE_INTERMIT_EN 0x10
|
||||
|
||||
#define BU21013_DELTA_0_6 0x7F
|
||||
#define BU21013_FILTER_EN 0x80
|
||||
|
||||
#define BU21013_INT_MODE_LEVEL 0x00
|
||||
#define BU21013_INT_MODE_EDGE 0x01
|
||||
|
||||
#define BU21013_GAIN_0 0x01
|
||||
#define BU21013_GAIN_1 0x02
|
||||
#define BU21013_GAIN_2 0x04
|
||||
|
||||
#define BU21013_OFFSET_MODE_DEFAULT 0x00
|
||||
#define BU21013_OFFSET_MODE_MOVE 0x01
|
||||
#define BU21013_OFFSET_MODE_DISABLE 0x02
|
||||
|
||||
#define BU21013_TH_ON_0 0x01
|
||||
#define BU21013_TH_ON_1 0x02
|
||||
#define BU21013_TH_ON_2 0x04
|
||||
#define BU21013_TH_ON_3 0x08
|
||||
#define BU21013_TH_ON_4 0x10
|
||||
#define BU21013_TH_ON_5 0x20
|
||||
#define BU21013_TH_ON_6 0x40
|
||||
#define BU21013_TH_ON_7 0x80
|
||||
#define BU21013_TH_ON_MAX 0xFF
|
||||
|
||||
#define BU21013_TH_OFF_0 0x01
|
||||
#define BU21013_TH_OFF_1 0x02
|
||||
#define BU21013_TH_OFF_2 0x04
|
||||
#define BU21013_TH_OFF_3 0x08
|
||||
#define BU21013_TH_OFF_4 0x10
|
||||
#define BU21013_TH_OFF_5 0x20
|
||||
#define BU21013_TH_OFF_6 0x40
|
||||
#define BU21013_TH_OFF_7 0x80
|
||||
#define BU21013_TH_OFF_MAX 0xFF
|
||||
|
||||
#define BU21013_X_EDGE_0 0x01
|
||||
#define BU21013_X_EDGE_1 0x02
|
||||
#define BU21013_X_EDGE_2 0x04
|
||||
#define BU21013_X_EDGE_3 0x08
|
||||
#define BU21013_Y_EDGE_0 0x10
|
||||
#define BU21013_Y_EDGE_1 0x20
|
||||
#define BU21013_Y_EDGE_2 0x40
|
||||
#define BU21013_Y_EDGE_3 0x80
|
||||
|
||||
#define BU21013_DONE 0x01
|
||||
#define BU21013_NUMBER_OF_X_SENSORS (6)
|
||||
#define BU21013_NUMBER_OF_Y_SENSORS (11)
|
||||
|
||||
#define DRIVER_TP "bu21013_tp"
|
||||
|
||||
/**
|
||||
* struct bu21013_ts_data - touch panel data structure
|
||||
* @client: pointer to the i2c client
|
||||
* @wait: variable to wait_queue_head_t structure
|
||||
* @touch_stopped: touch stop flag
|
||||
* @chip: pointer to the touch panel controller
|
||||
* @in_dev: pointer to the input device structure
|
||||
* @intr_pin: interrupt pin value
|
||||
*
|
||||
* Touch panel device data structure
|
||||
*/
|
||||
struct bu21013_ts_data {
|
||||
struct i2c_client *client;
|
||||
wait_queue_head_t wait;
|
||||
bool touch_stopped;
|
||||
const struct bu21013_platform_device *chip;
|
||||
struct input_dev *in_dev;
|
||||
unsigned int intr_pin;
|
||||
};
|
||||
|
||||
/**
|
||||
* bu21013_read_block_data(): read the touch co-ordinates
|
||||
* @data: bu21013_ts_data structure pointer
|
||||
* @buf: byte pointer
|
||||
*
|
||||
* Read the touch co-ordinates using i2c read block into buffer
|
||||
* and returns integer.
|
||||
*/
|
||||
static int bu21013_read_block_data(struct bu21013_ts_data *data, u8 *buf)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < I2C_RETRY_COUNT; i++) {
|
||||
ret = i2c_smbus_read_i2c_block_data
|
||||
(data->client, BU21013_SENSORS_BTN_0_7_REG,
|
||||
LENGTH_OF_BUFFER, buf);
|
||||
if (ret == LENGTH_OF_BUFFER)
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_do_touch_report(): Get the touch co-ordinates
|
||||
* @data: bu21013_ts_data structure pointer
|
||||
*
|
||||
* Get the touch co-ordinates from touch sensor registers and writes
|
||||
* into device structure and returns integer.
|
||||
*/
|
||||
static int bu21013_do_touch_report(struct bu21013_ts_data *data)
|
||||
{
|
||||
u8 buf[LENGTH_OF_BUFFER];
|
||||
unsigned int pos_x[2], pos_y[2];
|
||||
bool has_x_sensors, has_y_sensors;
|
||||
int finger_down_count = 0;
|
||||
int i;
|
||||
|
||||
if (data == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (bu21013_read_block_data(data, buf) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
has_x_sensors = hweight32(buf[0] & BU21013_SENSORS_EN_0_7);
|
||||
has_y_sensors = hweight32(((buf[1] & BU21013_SENSORS_EN_8_15) |
|
||||
((buf[2] & BU21013_SENSORS_EN_16_23) << SHIFT_8)) >> SHIFT_2);
|
||||
if (!has_x_sensors || !has_y_sensors)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < MAX_FINGERS; i++) {
|
||||
const u8 *p = &buf[4 * i + 3];
|
||||
unsigned int x = p[0] << SHIFT_2 | (p[1] & MASK_BITS);
|
||||
unsigned int y = p[2] << SHIFT_2 | (p[3] & MASK_BITS);
|
||||
if (x == 0 || y == 0)
|
||||
continue;
|
||||
pos_x[finger_down_count] = x;
|
||||
pos_y[finger_down_count] = y;
|
||||
finger_down_count++;
|
||||
}
|
||||
|
||||
if (finger_down_count) {
|
||||
if (finger_down_count == 2 &&
|
||||
(abs(pos_x[0] - pos_x[1]) < DELTA_MIN ||
|
||||
abs(pos_y[0] - pos_y[1]) < DELTA_MIN)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < finger_down_count; i++) {
|
||||
if (data->chip->x_flip)
|
||||
pos_x[i] = data->chip->touch_x_max - pos_x[i];
|
||||
if (data->chip->y_flip)
|
||||
pos_y[i] = data->chip->touch_y_max - pos_y[i];
|
||||
|
||||
input_report_abs(data->in_dev,
|
||||
ABS_MT_POSITION_X, pos_x[i]);
|
||||
input_report_abs(data->in_dev,
|
||||
ABS_MT_POSITION_Y, pos_y[i]);
|
||||
input_mt_sync(data->in_dev);
|
||||
}
|
||||
} else
|
||||
input_mt_sync(data->in_dev);
|
||||
|
||||
input_sync(data->in_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* bu21013_gpio_irq() - gpio thread function for touch interrupt
|
||||
* @irq: irq value
|
||||
* @device_data: void pointer
|
||||
*
|
||||
* This gpio thread function for touch interrupt
|
||||
* and returns irqreturn_t.
|
||||
*/
|
||||
static irqreturn_t bu21013_gpio_irq(int irq, void *device_data)
|
||||
{
|
||||
struct bu21013_ts_data *data = device_data;
|
||||
struct i2c_client *i2c = data->client;
|
||||
int retval;
|
||||
|
||||
do {
|
||||
retval = bu21013_do_touch_report(data);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "bu21013_do_touch_report failed\n");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
data->intr_pin = data->chip->irq_read_val();
|
||||
if (data->intr_pin == PEN_DOWN_INTR)
|
||||
wait_event_timeout(data->wait, data->touch_stopped,
|
||||
msecs_to_jiffies(2));
|
||||
} while (!data->intr_pin && !data->touch_stopped);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_init_chip() - power on sequence for the bu21013 controller
|
||||
* @data: device structure pointer
|
||||
*
|
||||
* This function is used to power on
|
||||
* the bu21013 controller and returns integer.
|
||||
*/
|
||||
static int bu21013_init_chip(struct bu21013_ts_data *data)
|
||||
{
|
||||
int retval;
|
||||
struct i2c_client *i2c = data->client;
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_RESET_REG,
|
||||
BU21013_RESET_ENABLE);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_RESET reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
msleep(RESET_DELAY);
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_0_7_REG,
|
||||
BU21013_SENSORS_EN_0_7);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_SENSOR_0_7 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_8_15_REG,
|
||||
BU21013_SENSORS_EN_8_15);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_SENSOR_8_15 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_SENSOR_16_23_REG,
|
||||
BU21013_SENSORS_EN_16_23);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_SENSOR_16_23 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE1_REG,
|
||||
(BU21013_POS_MODE1_0 | BU21013_POS_MODE1_1));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_POS_MODE1 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_POS_MODE2_REG,
|
||||
(BU21013_POS_MODE2_ZERO | BU21013_POS_MODE2_AVG1 |
|
||||
BU21013_POS_MODE2_AVG2 | BU21013_POS_MODE2_EN_RAW |
|
||||
BU21013_POS_MODE2_MULTI));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_POS_MODE2 reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (data->chip->ext_clk)
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
|
||||
(BU21013_CLK_MODE_EXT | BU21013_CLK_MODE_CALIB));
|
||||
else
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_CLK_MODE_REG,
|
||||
(BU21013_CLK_MODE_DIV | BU21013_CLK_MODE_CALIB));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_CLK_MODE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_IDLE_REG,
|
||||
(BU21013_IDLET_0 | BU21013_IDLE_INTERMIT_EN));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_IDLE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_INT_MODE_REG,
|
||||
BU21013_INT_MODE_LEVEL);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_INT_MODE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_FILTER_REG,
|
||||
(BU21013_DELTA_0_6 |
|
||||
BU21013_FILTER_EN));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_FILTER reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_ON_REG,
|
||||
BU21013_TH_ON_5);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_TH_ON reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_TH_OFF_REG,
|
||||
BU21013_TH_OFF_4 || BU21013_TH_OFF_3);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_TH_OFF reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_GAIN_REG,
|
||||
(BU21013_GAIN_0 | BU21013_GAIN_1));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_GAIN reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_OFFSET_MODE_REG,
|
||||
BU21013_OFFSET_MODE_DEFAULT);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_OFFSET_MODE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_XY_EDGE_REG,
|
||||
(BU21013_X_EDGE_0 | BU21013_X_EDGE_2 |
|
||||
BU21013_Y_EDGE_1 | BU21013_Y_EDGE_3));
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_XY_EDGE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
retval = i2c_smbus_write_byte_data(i2c, BU21013_DONE_REG,
|
||||
BU21013_DONE);
|
||||
if (retval < 0) {
|
||||
dev_err(&i2c->dev, "BU21013_REG_DONE reg write failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_free_irq() - frees IRQ registered for touchscreen
|
||||
* @bu21013_data: device structure pointer
|
||||
*
|
||||
* This function signals interrupt thread to stop processing and
|
||||
* frees interrupt.
|
||||
*/
|
||||
static void bu21013_free_irq(struct bu21013_ts_data *bu21013_data)
|
||||
{
|
||||
bu21013_data->touch_stopped = true;
|
||||
wake_up(&bu21013_data->wait);
|
||||
free_irq(bu21013_data->chip->irq, bu21013_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_probe() - initializes the i2c-client touchscreen driver
|
||||
* @client: i2c client structure pointer
|
||||
* @id: i2c device id pointer
|
||||
*
|
||||
* This function used to initializes the i2c-client touchscreen
|
||||
* driver and returns integer.
|
||||
*/
|
||||
static int __devinit bu21013_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data;
|
||||
struct input_dev *in_dev;
|
||||
const struct bu21013_platform_device *pdata =
|
||||
client->dev.platform_data;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
dev_err(&client->dev, "i2c smbus byte data not supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bu21013_data = kzalloc(sizeof(struct bu21013_ts_data), GFP_KERNEL);
|
||||
in_dev = input_allocate_device();
|
||||
if (!bu21013_data || !in_dev) {
|
||||
dev_err(&client->dev, "device memory alloc failed\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
bu21013_data->in_dev = in_dev;
|
||||
bu21013_data->chip = pdata;
|
||||
bu21013_data->client = client;
|
||||
bu21013_data->touch_stopped = false;
|
||||
init_waitqueue_head(&bu21013_data->wait);
|
||||
|
||||
/* configure the gpio pins */
|
||||
if (pdata->cs_en) {
|
||||
error = pdata->cs_en(pdata->cs_pin);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "chip init failed\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
}
|
||||
|
||||
/* configure the touch panel controller */
|
||||
error = bu21013_init_chip(bu21013_data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "error in bu21013 config\n");
|
||||
goto err_cs_disable;
|
||||
}
|
||||
|
||||
/* register the device to input subsystem */
|
||||
in_dev->name = DRIVER_TP;
|
||||
in_dev->id.bustype = BUS_I2C;
|
||||
in_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_SYN, in_dev->evbit);
|
||||
__set_bit(EV_KEY, in_dev->evbit);
|
||||
__set_bit(EV_ABS, in_dev->evbit);
|
||||
|
||||
input_set_abs_params(in_dev, ABS_MT_POSITION_X, 0,
|
||||
pdata->x_max_res, 0, 0);
|
||||
input_set_abs_params(in_dev, ABS_MT_POSITION_Y, 0,
|
||||
pdata->y_max_res, 0, 0);
|
||||
input_set_drvdata(in_dev, bu21013_data);
|
||||
|
||||
error = request_threaded_irq(pdata->irq, NULL, bu21013_gpio_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_SHARED,
|
||||
DRIVER_TP, bu21013_data);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "request irq %d failed\n", pdata->irq);
|
||||
goto err_cs_disable;
|
||||
}
|
||||
|
||||
error = input_register_device(in_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
device_init_wakeup(&client->dev, pdata->wakeup);
|
||||
i2c_set_clientdata(client, bu21013_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
bu21013_free_irq(bu21013_data);
|
||||
err_cs_disable:
|
||||
pdata->cs_dis(pdata->cs_pin);
|
||||
err_free_mem:
|
||||
input_free_device(bu21013_data->in_dev);
|
||||
kfree(bu21013_data);
|
||||
|
||||
return error;
|
||||
}
|
||||
/**
|
||||
* bu21013_remove() - removes the i2c-client touchscreen driver
|
||||
* @client: i2c client structure pointer
|
||||
*
|
||||
* This function uses to remove the i2c-client
|
||||
* touchscreen driver and returns integer.
|
||||
*/
|
||||
static int __devexit bu21013_remove(struct i2c_client *client)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data = i2c_get_clientdata(client);
|
||||
|
||||
bu21013_free_irq(bu21013_data);
|
||||
|
||||
bu21013_data->chip->cs_dis(bu21013_data->chip->cs_pin);
|
||||
|
||||
input_unregister_device(bu21013_data->in_dev);
|
||||
kfree(bu21013_data);
|
||||
|
||||
device_init_wakeup(&client->dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/**
|
||||
* bu21013_suspend() - suspend the touch screen controller
|
||||
* @dev: pointer to device structure
|
||||
*
|
||||
* This function is used to suspend the
|
||||
* touch panel controller and returns integer
|
||||
*/
|
||||
static int bu21013_suspend(struct device *dev)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = bu21013_data->client;
|
||||
|
||||
bu21013_data->touch_stopped = true;
|
||||
if (device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(bu21013_data->chip->irq);
|
||||
else
|
||||
disable_irq(bu21013_data->chip->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_resume() - resume the touch screen controller
|
||||
* @dev: pointer to device structure
|
||||
*
|
||||
* This function is used to resume the touch panel
|
||||
* controller and returns integer.
|
||||
*/
|
||||
static int bu21013_resume(struct device *dev)
|
||||
{
|
||||
struct bu21013_ts_data *bu21013_data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = bu21013_data->client;
|
||||
int retval;
|
||||
|
||||
retval = bu21013_init_chip(bu21013_data);
|
||||
if (retval < 0) {
|
||||
dev_err(&client->dev, "bu21013 controller config failed\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
bu21013_data->touch_stopped = false;
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(bu21013_data->chip->irq);
|
||||
else
|
||||
enable_irq(bu21013_data->chip->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops bu21013_dev_pm_ops = {
|
||||
.suspend = bu21013_suspend,
|
||||
.resume = bu21013_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id bu21013_id[] = {
|
||||
{ DRIVER_TP, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bu21013_id);
|
||||
|
||||
static struct i2c_driver bu21013_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_TP,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &bu21013_dev_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = bu21013_probe,
|
||||
.remove = __devexit_p(bu21013_remove),
|
||||
.id_table = bu21013_id,
|
||||
};
|
||||
|
||||
/**
|
||||
* bu21013_init() - initializes the bu21013 touchscreen driver
|
||||
*
|
||||
* This function used to initializes the bu21013
|
||||
* touchscreen driver and returns integer.
|
||||
*/
|
||||
static int __init bu21013_init(void)
|
||||
{
|
||||
return i2c_add_driver(&bu21013_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* bu21013_exit() - de-initializes the bu21013 touchscreen driver
|
||||
*
|
||||
* This function uses to de-initializes the bu21013
|
||||
* touchscreen driver and returns none.
|
||||
*/
|
||||
static void __exit bu21013_exit(void)
|
||||
{
|
||||
i2c_del_driver(&bu21013_driver);
|
||||
}
|
||||
|
||||
module_init(bu21013_init);
|
||||
module_exit(bu21013_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Naveen Kumar G <[email protected]>");
|
||||
MODULE_DESCRIPTION("bu21013 touch screen controller driver");
|
||||
|
|
@ -206,9 +206,9 @@ static int __devinit cy8ctmg110_probe(struct i2c_client *client,
|
|||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0);
|
||||
CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 4, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0);
|
||||
CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 4, 0);
|
||||
|
||||
if (ts->reset_pin) {
|
||||
err = gpio_request(ts->reset_pin, NULL);
|
||||
|
|
|
|||
|
|
@ -107,8 +107,7 @@ static int __init hp680_ts_init(void)
|
|||
return 0;
|
||||
|
||||
fail2: free_irq(HP680_TS_IRQ, NULL);
|
||||
cancel_delayed_work(&work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&work);
|
||||
fail1: input_free_device(hp680_ts_dev);
|
||||
return err;
|
||||
}
|
||||
|
|
@ -116,8 +115,7 @@ static int __init hp680_ts_init(void)
|
|||
static void __exit hp680_ts_exit(void)
|
||||
{
|
||||
free_irq(HP680_TS_IRQ, NULL);
|
||||
cancel_delayed_work(&work);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&work);
|
||||
input_unregister_device(hp680_ts_dev);
|
||||
}
|
||||
|
||||
|
|
|
|||
687
drivers/input/touchscreen/intel-mid-touch.c
Normal file
687
drivers/input/touchscreen/intel-mid-touch.c
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
/*
|
||||
* Intel MID Resistive Touch Screen Driver
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corp
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* Questions/Comments/Bug fixes to Sreedhara (sreedhara.ds@intel.com)
|
||||
* Ramesh Agarwal (ramesh.agarwal@intel.com)
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* TODO:
|
||||
* review conversion of r/m/w sequences
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* PMIC Interrupt registers */
|
||||
#define PMIC_REG_ID1 0x00 /* PMIC ID1 register */
|
||||
|
||||
/* PMIC Interrupt registers */
|
||||
#define PMIC_REG_INT 0x04 /* PMIC interrupt register */
|
||||
#define PMIC_REG_MINT 0x05 /* PMIC interrupt mask register */
|
||||
|
||||
/* ADC Interrupt registers */
|
||||
#define PMIC_REG_ADCINT 0x5F /* ADC interrupt register */
|
||||
#define PMIC_REG_MADCINT 0x60 /* ADC interrupt mask register */
|
||||
|
||||
/* ADC Control registers */
|
||||
#define PMIC_REG_ADCCNTL1 0x61 /* ADC control register */
|
||||
|
||||
/* ADC Channel Selection registers */
|
||||
#define PMICADDR0 0xA4
|
||||
#define END_OF_CHANNEL 0x1F
|
||||
|
||||
/* ADC Result register */
|
||||
#define PMIC_REG_ADCSNS0H 0x64
|
||||
|
||||
/* ADC channels for touch screen */
|
||||
#define MRST_TS_CHAN10 0xA /* Touch screen X+ connection */
|
||||
#define MRST_TS_CHAN11 0xB /* Touch screen X- connection */
|
||||
#define MRST_TS_CHAN12 0xC /* Touch screen Y+ connection */
|
||||
#define MRST_TS_CHAN13 0xD /* Touch screen Y- connection */
|
||||
|
||||
/* Touch screen channel BIAS constants */
|
||||
#define MRST_XBIAS 0x20
|
||||
#define MRST_YBIAS 0x40
|
||||
#define MRST_ZBIAS 0x80
|
||||
|
||||
/* Touch screen coordinates */
|
||||
#define MRST_X_MIN 10
|
||||
#define MRST_X_MAX 1024
|
||||
#define MRST_X_FUZZ 5
|
||||
#define MRST_Y_MIN 10
|
||||
#define MRST_Y_MAX 1024
|
||||
#define MRST_Y_FUZZ 5
|
||||
#define MRST_PRESSURE_MIN 0
|
||||
#define MRST_PRESSURE_NOMINAL 50
|
||||
#define MRST_PRESSURE_MAX 100
|
||||
|
||||
#define WAIT_ADC_COMPLETION 10 /* msec */
|
||||
|
||||
/* PMIC ADC round robin delays */
|
||||
#define ADC_LOOP_DELAY0 0x0 /* Continuous loop */
|
||||
#define ADC_LOOP_DELAY1 0x1 /* 4.5 ms approximate */
|
||||
|
||||
/* PMIC Vendor Identifiers */
|
||||
#define PMIC_VENDOR_FS 0 /* PMIC vendor FreeScale */
|
||||
#define PMIC_VENDOR_MAXIM 1 /* PMIC vendor MAXIM */
|
||||
#define PMIC_VENDOR_NEC 2 /* PMIC vendor NEC */
|
||||
#define MRSTOUCH_MAX_CHANNELS 32 /* Maximum ADC channels */
|
||||
|
||||
/* Touch screen device structure */
|
||||
struct mrstouch_dev {
|
||||
struct device *dev; /* device associated with touch screen */
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
u16 asr; /* Address selection register */
|
||||
int irq;
|
||||
unsigned int vendor; /* PMIC vendor */
|
||||
unsigned int rev; /* PMIC revision */
|
||||
|
||||
int (*read_prepare)(struct mrstouch_dev *tsdev);
|
||||
int (*read)(struct mrstouch_dev *tsdev, u16 *x, u16 *y, u16 *z);
|
||||
int (*read_finish)(struct mrstouch_dev *tsdev);
|
||||
};
|
||||
|
||||
|
||||
/*************************** NEC and Maxim Interface ************************/
|
||||
|
||||
static int mrstouch_nec_adc_read_prepare(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
return intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0, 0x20);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables PENDET interrupt.
|
||||
*/
|
||||
static int mrstouch_nec_adc_read_finish(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x20, 0x20);
|
||||
if (!err)
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, 0, 0x05);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads PMIC ADC touch screen result
|
||||
* Reads ADC storage registers for higher 7 and lower 3 bits and
|
||||
* converts the two readings into a single value and turns off gain bit
|
||||
*/
|
||||
static int mrstouch_ts_chan_read(u16 offset, u16 chan, u16 *vp, u16 *vm)
|
||||
{
|
||||
int err;
|
||||
u16 result;
|
||||
u32 res;
|
||||
|
||||
result = PMIC_REG_ADCSNS0H + offset;
|
||||
|
||||
if (chan == MRST_TS_CHAN12)
|
||||
result += 4;
|
||||
|
||||
err = intel_scu_ipc_ioread32(result, &res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Mash the bits up */
|
||||
|
||||
*vp = (res & 0xFF) << 3; /* Highest 7 bits */
|
||||
*vp |= (res >> 8) & 0x07; /* Lower 3 bits */
|
||||
*vp &= 0x3FF;
|
||||
|
||||
res >>= 16;
|
||||
|
||||
*vm = (res & 0xFF) << 3; /* Highest 7 bits */
|
||||
*vm |= (res >> 8) & 0x07; /* Lower 3 bits */
|
||||
*vm &= 0x3FF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables X, Y and Z bias values
|
||||
* Enables YPYM for X channels and XPXM for Y channels
|
||||
*/
|
||||
static int mrstouch_ts_bias_set(uint offset, uint bias)
|
||||
{
|
||||
int count;
|
||||
u16 chan, start;
|
||||
u16 reg[4];
|
||||
u8 data[4];
|
||||
|
||||
chan = PMICADDR0 + offset;
|
||||
start = MRST_TS_CHAN10;
|
||||
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = bias | (start + count);
|
||||
}
|
||||
|
||||
return intel_scu_ipc_writev(reg, data, 4);
|
||||
}
|
||||
|
||||
/* To read touch screen channel values */
|
||||
static int mrstouch_nec_adc_read(struct mrstouch_dev *tsdev,
|
||||
u16 *x, u16 *y, u16 *z)
|
||||
{
|
||||
int err;
|
||||
u16 xm, ym, zm;
|
||||
|
||||
/* configure Y bias for X channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, MRST_YBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read x+ and x- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, x, &xm);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* configure x bias for y channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, MRST_XBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read y+ and y- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN12, y, &ym);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* configure z bias for x and y channels */
|
||||
err = mrstouch_ts_bias_set(tsdev->asr, MRST_ZBIAS);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* read z+ and z- channels */
|
||||
err = mrstouch_ts_chan_read(tsdev->asr, MRST_TS_CHAN10, z, &zm);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during adc read\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/*************************** Freescale Interface ************************/
|
||||
|
||||
static int mrstouch_fs_adc_read_prepare(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, count;
|
||||
u16 chan;
|
||||
u16 reg[5];
|
||||
u8 data[5];
|
||||
|
||||
/* Stop the ADC */
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x00, 0x02);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
chan = PMICADDR0 + tsdev->asr;
|
||||
|
||||
/* Set X BIAS */
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0x2A;
|
||||
}
|
||||
reg[count] = chan++; /* Dummy */
|
||||
data[count] = 0;
|
||||
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* Set Y BIAS */
|
||||
for (count = 0; count <= 3; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0x4A;
|
||||
}
|
||||
reg[count] = chan++; /* Dummy */
|
||||
data[count] = 0;
|
||||
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
/* Set Z BIAS */
|
||||
err = intel_scu_ipc_iowrite32(chan + 2, 0x8A8A8A8A);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
msleep(WAIT_ADC_COMPLETION);
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during %s\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrstouch_fs_adc_read(struct mrstouch_dev *tsdev,
|
||||
u16 *x, u16 *y, u16 *z)
|
||||
{
|
||||
int err;
|
||||
u16 result;
|
||||
u16 reg[4];
|
||||
u8 data[4];
|
||||
|
||||
result = PMIC_REG_ADCSNS0H + tsdev->asr;
|
||||
|
||||
reg[0] = result + 4;
|
||||
reg[1] = result + 5;
|
||||
reg[2] = result + 16;
|
||||
reg[3] = result + 17;
|
||||
|
||||
err = intel_scu_ipc_readv(reg, data, 4);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
*x = data[0] << 3; /* Higher 7 bits */
|
||||
*x |= data[1] & 0x7; /* Lower 3 bits */
|
||||
*x &= 0x3FF;
|
||||
|
||||
*y = data[2] << 3; /* Higher 7 bits */
|
||||
*y |= data[3] & 0x7; /* Lower 3 bits */
|
||||
*y &= 0x3FF;
|
||||
|
||||
/* Read Z value */
|
||||
reg[0] = result + 28;
|
||||
reg[1] = result + 29;
|
||||
|
||||
err = intel_scu_ipc_readv(reg, data, 4);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
*z = data[0] << 3; /* Higher 7 bits */
|
||||
*z |= data[1] & 0x7; /* Lower 3 bits */
|
||||
*z &= 0x3FF;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during %s\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mrstouch_fs_adc_read_finish(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, count;
|
||||
u16 chan;
|
||||
u16 reg[5];
|
||||
u8 data[5];
|
||||
|
||||
/* Clear all TS channels */
|
||||
chan = PMICADDR0 + tsdev->asr;
|
||||
for (count = 0; count <= 4; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0;
|
||||
}
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
for (count = 0; count <= 4; count++) {
|
||||
reg[count] = chan++;
|
||||
data[count] = 0;
|
||||
}
|
||||
err = intel_scu_ipc_writev(reg, data, 5);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
err = intel_scu_ipc_iowrite32(chan + 2, 0x00000000);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
/* Start ADC */
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, 0x02, 0x02);
|
||||
if (err)
|
||||
goto ipc_error;
|
||||
|
||||
return 0;
|
||||
|
||||
ipc_error:
|
||||
dev_err(tsdev->dev, "ipc error during %s\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void mrstouch_report_event(struct input_dev *input,
|
||||
unsigned int x, unsigned int y, unsigned int z)
|
||||
{
|
||||
if (z > MRST_PRESSURE_NOMINAL) {
|
||||
/* Pen touched, report button touch and coordinates */
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
}
|
||||
|
||||
input_report_abs(input, ABS_PRESSURE, z);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
/* PENDET interrupt handler */
|
||||
static irqreturn_t mrstouch_pendet_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mrstouch_dev *tsdev = dev_id;
|
||||
u16 x, y, z;
|
||||
|
||||
/*
|
||||
* Should we lower thread priority? Probably not, since we are
|
||||
* not spinning but sleeping...
|
||||
*/
|
||||
|
||||
if (tsdev->read_prepare(tsdev))
|
||||
goto out;
|
||||
|
||||
do {
|
||||
if (tsdev->read(tsdev, &x, &y, &z))
|
||||
break;
|
||||
|
||||
mrstouch_report_event(tsdev->input, x, y, z);
|
||||
} while (z > MRST_PRESSURE_NOMINAL);
|
||||
|
||||
tsdev->read_finish(tsdev);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Utility to read PMIC ID */
|
||||
static int __devinit mrstouch_read_pmic_id(uint *vendor, uint *rev)
|
||||
{
|
||||
int err;
|
||||
u8 r;
|
||||
|
||||
err = intel_scu_ipc_ioread8(PMIC_REG_ID1, &r);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
*vendor = r & 0x7;
|
||||
*rev = (r >> 3) & 0x7;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse ADC channels to find end of the channel configured by other ADC user
|
||||
* NEC and MAXIM requires 4 channels and FreeScale needs 18 channels
|
||||
*/
|
||||
static int __devinit mrstouch_chan_parse(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, i, found;
|
||||
u8 r8;
|
||||
|
||||
found = -1;
|
||||
|
||||
for (i = 0; i < MRSTOUCH_MAX_CHANNELS; i++) {
|
||||
if (found >= 0)
|
||||
break;
|
||||
|
||||
err = intel_scu_ipc_ioread8(PMICADDR0 + i, &r8);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (r8 == END_OF_CHANNEL) {
|
||||
found = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found < 0)
|
||||
return 0;
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
if (found && found > (MRSTOUCH_MAX_CHANNELS - 18))
|
||||
return -ENOSPC;
|
||||
} else {
|
||||
if (found && found > (MRSTOUCH_MAX_CHANNELS - 4))
|
||||
return -ENOSPC;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Writes touch screen channels to ADC address selection registers
|
||||
*/
|
||||
static int __devinit mrstouch_ts_chan_set(uint offset)
|
||||
{
|
||||
u16 chan;
|
||||
|
||||
int ret, count;
|
||||
|
||||
chan = PMICADDR0 + offset;
|
||||
for (count = 0; count <= 3; count++) {
|
||||
ret = intel_scu_ipc_iowrite8(chan++, MRST_TS_CHAN10 + count);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return intel_scu_ipc_iowrite8(chan++, END_OF_CHANNEL);
|
||||
}
|
||||
|
||||
/* Initialize ADC */
|
||||
static int __devinit mrstouch_adc_init(struct mrstouch_dev *tsdev)
|
||||
{
|
||||
int err, start;
|
||||
u8 ra, rm;
|
||||
|
||||
err = mrstouch_read_pmic_id(&tsdev->vendor, &tsdev->rev);
|
||||
if (err) {
|
||||
dev_err(tsdev->dev, "Unable to read PMIC id\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (tsdev->vendor) {
|
||||
case PMIC_VENDOR_NEC:
|
||||
case PMIC_VENDOR_MAXIM:
|
||||
tsdev->read_prepare = mrstouch_nec_adc_read_prepare;
|
||||
tsdev->read = mrstouch_nec_adc_read;
|
||||
tsdev->read_finish = mrstouch_nec_adc_read_finish;
|
||||
break;
|
||||
|
||||
case PMIC_VENDOR_FS:
|
||||
tsdev->read_prepare = mrstouch_fs_adc_read_prepare;
|
||||
tsdev->read = mrstouch_fs_adc_read;
|
||||
tsdev->read_finish = mrstouch_fs_adc_read_finish;
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(tsdev->dev,
|
||||
"Unsupported touchscreen: %d\n", tsdev->vendor);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
start = mrstouch_chan_parse(tsdev);
|
||||
if (start < 0) {
|
||||
dev_err(tsdev->dev, "Unable to parse channels\n");
|
||||
return start;
|
||||
}
|
||||
|
||||
tsdev->asr = start;
|
||||
|
||||
/*
|
||||
* ADC power on, start, enable PENDET and set loop delay
|
||||
* ADC loop delay is set to 4.5 ms approximately
|
||||
* Loop delay more than this results in jitter in adc readings
|
||||
* Setting loop delay to 0 (continous loop) in MAXIM stops PENDET
|
||||
* interrupt generation sometimes.
|
||||
*/
|
||||
|
||||
if (tsdev->vendor == PMIC_VENDOR_FS) {
|
||||
ra = 0xE0 | ADC_LOOP_DELAY0;
|
||||
rm = 0x5;
|
||||
} else {
|
||||
/* NEC and MAXIm not consistent with loop delay 0 */
|
||||
ra = 0xE0 | ADC_LOOP_DELAY1;
|
||||
rm = 0x0;
|
||||
|
||||
/* configure touch screen channels */
|
||||
err = mrstouch_ts_chan_set(tsdev->asr);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_ADCCNTL1, ra, 0xE7);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = intel_scu_ipc_update_register(PMIC_REG_MADCINT, rm, 0x03);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Probe function for touch screen driver */
|
||||
static int __devinit mrstouch_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mrstouch_dev *tsdev;
|
||||
struct input_dev *input;
|
||||
int err;
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no interrupt assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdev = kzalloc(sizeof(struct mrstouch_dev), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!tsdev || !input) {
|
||||
dev_err(&pdev->dev, "unable to allocate memory\n");
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsdev->dev = &pdev->dev;
|
||||
tsdev->input = input;
|
||||
tsdev->irq = irq;
|
||||
|
||||
snprintf(tsdev->phys, sizeof(tsdev->phys),
|
||||
"%s/input0", dev_name(tsdev->dev));
|
||||
|
||||
err = mrstouch_adc_init(tsdev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "ADC initialization failed\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
input->name = "mrst_touchscreen";
|
||||
input->phys = tsdev->phys;
|
||||
input->dev.parent = tsdev->dev;
|
||||
|
||||
input->id.vendor = tsdev->vendor;
|
||||
input->id.version = tsdev->rev;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
input_set_abs_params(tsdev->input, ABS_X,
|
||||
MRST_X_MIN, MRST_X_MAX, MRST_X_FUZZ, 0);
|
||||
input_set_abs_params(tsdev->input, ABS_Y,
|
||||
MRST_Y_MIN, MRST_Y_MAX, MRST_Y_FUZZ, 0);
|
||||
input_set_abs_params(tsdev->input, ABS_PRESSURE,
|
||||
MRST_PRESSURE_MIN, MRST_PRESSURE_MAX, 0, 0);
|
||||
|
||||
err = request_threaded_irq(tsdev->irq, NULL, mrstouch_pendet_irq,
|
||||
0, "mrstouch", tsdev);
|
||||
if (err) {
|
||||
dev_err(tsdev->dev, "unable to allocate irq\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
err = input_register_device(tsdev->input);
|
||||
if (err) {
|
||||
dev_err(tsdev->dev, "unable to register input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tsdev);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(tsdev->irq, tsdev);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(tsdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit mrstouch_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mrstouch_dev *tsdev = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(tsdev->irq, tsdev);
|
||||
input_unregister_device(tsdev->input);
|
||||
kfree(tsdev);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mrstouch_driver = {
|
||||
.driver = {
|
||||
.name = "pmic_touch",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mrstouch_probe,
|
||||
.remove = __devexit_p(mrstouch_remove),
|
||||
};
|
||||
|
||||
static int __init mrstouch_init(void)
|
||||
{
|
||||
return platform_driver_register(&mrstouch_driver);
|
||||
}
|
||||
module_init(mrstouch_init);
|
||||
|
||||
static void __exit mrstouch_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mrstouch_driver);
|
||||
}
|
||||
module_exit(mrstouch_exit);
|
||||
|
||||
MODULE_AUTHOR("Sreedhara Murthy. D.S, [email protected]");
|
||||
MODULE_DESCRIPTION("Intel Moorestown Resistive Touch Screen Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
411
drivers/input/touchscreen/lpc32xx_ts.c
Normal file
411
drivers/input/touchscreen/lpc32xx_ts.c
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* LPC32xx built-in touchscreen driver
|
||||
*
|
||||
* Copyright (C) 2010 NXP Semiconductors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Touchscreen controller register offsets
|
||||
*/
|
||||
#define LPC32XX_TSC_STAT 0x00
|
||||
#define LPC32XX_TSC_SEL 0x04
|
||||
#define LPC32XX_TSC_CON 0x08
|
||||
#define LPC32XX_TSC_FIFO 0x0C
|
||||
#define LPC32XX_TSC_DTR 0x10
|
||||
#define LPC32XX_TSC_RTR 0x14
|
||||
#define LPC32XX_TSC_UTR 0x18
|
||||
#define LPC32XX_TSC_TTR 0x1C
|
||||
#define LPC32XX_TSC_DXP 0x20
|
||||
#define LPC32XX_TSC_MIN_X 0x24
|
||||
#define LPC32XX_TSC_MAX_X 0x28
|
||||
#define LPC32XX_TSC_MIN_Y 0x2C
|
||||
#define LPC32XX_TSC_MAX_Y 0x30
|
||||
#define LPC32XX_TSC_AUX_UTR 0x34
|
||||
#define LPC32XX_TSC_AUX_MIN 0x38
|
||||
#define LPC32XX_TSC_AUX_MAX 0x3C
|
||||
|
||||
#define LPC32XX_TSC_STAT_FIFO_OVRRN (1 << 8)
|
||||
#define LPC32XX_TSC_STAT_FIFO_EMPTY (1 << 7)
|
||||
|
||||
#define LPC32XX_TSC_SEL_DEFVAL 0x0284
|
||||
|
||||
#define LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 (0x1 << 11)
|
||||
#define LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(s) ((10 - (s)) << 7)
|
||||
#define LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(s) ((10 - (s)) << 4)
|
||||
#define LPC32XX_TSC_ADCCON_POWER_UP (1 << 2)
|
||||
#define LPC32XX_TSC_ADCCON_AUTO_EN (1 << 0)
|
||||
|
||||
#define LPC32XX_TSC_FIFO_TS_P_LEVEL (1 << 31)
|
||||
#define LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(x) (((x) & 0x03FF0000) >> 16)
|
||||
#define LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(y) ((y) & 0x000003FF)
|
||||
|
||||
#define LPC32XX_TSC_ADCDAT_VALUE_MASK 0x000003FF
|
||||
|
||||
#define LPC32XX_TSC_MIN_XY_VAL 0x0
|
||||
#define LPC32XX_TSC_MAX_XY_VAL 0x3FF
|
||||
|
||||
#define MOD_NAME "ts-lpc32xx"
|
||||
|
||||
#define tsc_readl(dev, reg) \
|
||||
__raw_readl((dev)->tsc_base + (reg))
|
||||
#define tsc_writel(dev, reg, val) \
|
||||
__raw_writel((val), (dev)->tsc_base + (reg))
|
||||
|
||||
struct lpc32xx_tsc {
|
||||
struct input_dev *dev;
|
||||
void __iomem *tsc_base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void lpc32xx_fifo_clear(struct lpc32xx_tsc *tsc)
|
||||
{
|
||||
while (!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
|
||||
LPC32XX_TSC_STAT_FIFO_EMPTY))
|
||||
tsc_readl(tsc, LPC32XX_TSC_FIFO);
|
||||
}
|
||||
|
||||
static irqreturn_t lpc32xx_ts_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
u32 tmp, rv[4], xs[4], ys[4];
|
||||
int idx;
|
||||
struct lpc32xx_tsc *tsc = dev_id;
|
||||
struct input_dev *input = tsc->dev;
|
||||
|
||||
tmp = tsc_readl(tsc, LPC32XX_TSC_STAT);
|
||||
|
||||
if (tmp & LPC32XX_TSC_STAT_FIFO_OVRRN) {
|
||||
/* FIFO overflow - throw away samples */
|
||||
lpc32xx_fifo_clear(tsc);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gather and normalize 4 samples. Pen-up events may have less
|
||||
* than 4 samples, but its ok to pop 4 and let the last sample
|
||||
* pen status check drop the samples.
|
||||
*/
|
||||
idx = 0;
|
||||
while (idx < 4 &&
|
||||
!(tsc_readl(tsc, LPC32XX_TSC_STAT) &
|
||||
LPC32XX_TSC_STAT_FIFO_EMPTY)) {
|
||||
tmp = tsc_readl(tsc, LPC32XX_TSC_FIFO);
|
||||
xs[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
|
||||
LPC32XX_TSC_FIFO_NORMALIZE_X_VAL(tmp);
|
||||
ys[idx] = LPC32XX_TSC_ADCDAT_VALUE_MASK -
|
||||
LPC32XX_TSC_FIFO_NORMALIZE_Y_VAL(tmp);
|
||||
rv[idx] = tmp;
|
||||
idx++;
|
||||
}
|
||||
|
||||
/* Data is only valid if pen is still down in last sample */
|
||||
if (!(rv[3] & LPC32XX_TSC_FIFO_TS_P_LEVEL) && idx == 4) {
|
||||
/* Use average of 2nd and 3rd sample for position */
|
||||
input_report_abs(input, ABS_X, (xs[1] + xs[2]) / 2);
|
||||
input_report_abs(input, ABS_Y, (ys[1] + ys[2]) / 2);
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void lpc32xx_stop_tsc(struct lpc32xx_tsc *tsc)
|
||||
{
|
||||
/* Disable auto mode */
|
||||
tsc_writel(tsc, LPC32XX_TSC_CON,
|
||||
tsc_readl(tsc, LPC32XX_TSC_CON) &
|
||||
~LPC32XX_TSC_ADCCON_AUTO_EN);
|
||||
|
||||
clk_disable(tsc->clk);
|
||||
}
|
||||
|
||||
static void lpc32xx_setup_tsc(struct lpc32xx_tsc *tsc)
|
||||
{
|
||||
u32 tmp;
|
||||
|
||||
clk_enable(tsc->clk);
|
||||
|
||||
tmp = tsc_readl(tsc, LPC32XX_TSC_CON) & ~LPC32XX_TSC_ADCCON_POWER_UP;
|
||||
|
||||
/* Set the TSC FIFO depth to 4 samples @ 10-bits per sample (max) */
|
||||
tmp = LPC32XX_TSC_ADCCON_IRQ_TO_FIFO_4 |
|
||||
LPC32XX_TSC_ADCCON_X_SAMPLE_SIZE(10) |
|
||||
LPC32XX_TSC_ADCCON_Y_SAMPLE_SIZE(10);
|
||||
tsc_writel(tsc, LPC32XX_TSC_CON, tmp);
|
||||
|
||||
/* These values are all preset */
|
||||
tsc_writel(tsc, LPC32XX_TSC_SEL, LPC32XX_TSC_SEL_DEFVAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MIN_X, LPC32XX_TSC_MIN_XY_VAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MAX_X, LPC32XX_TSC_MAX_XY_VAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MIN_Y, LPC32XX_TSC_MIN_XY_VAL);
|
||||
tsc_writel(tsc, LPC32XX_TSC_MAX_Y, LPC32XX_TSC_MAX_XY_VAL);
|
||||
|
||||
/* Aux support is not used */
|
||||
tsc_writel(tsc, LPC32XX_TSC_AUX_UTR, 0);
|
||||
tsc_writel(tsc, LPC32XX_TSC_AUX_MIN, 0);
|
||||
tsc_writel(tsc, LPC32XX_TSC_AUX_MAX, 0);
|
||||
|
||||
/*
|
||||
* Set sample rate to about 240Hz per X/Y pair. A single measurement
|
||||
* consists of 4 pairs which gives about a 60Hz sample rate based on
|
||||
* a stable 32768Hz clock source. Values are in clocks.
|
||||
* Rate is (32768 / (RTR + XCONV + RTR + YCONV + DXP + TTR + UTR) / 4
|
||||
*/
|
||||
tsc_writel(tsc, LPC32XX_TSC_RTR, 0x2);
|
||||
tsc_writel(tsc, LPC32XX_TSC_DTR, 0x2);
|
||||
tsc_writel(tsc, LPC32XX_TSC_TTR, 0x10);
|
||||
tsc_writel(tsc, LPC32XX_TSC_DXP, 0x4);
|
||||
tsc_writel(tsc, LPC32XX_TSC_UTR, 88);
|
||||
|
||||
lpc32xx_fifo_clear(tsc);
|
||||
|
||||
/* Enable automatic ts event capture */
|
||||
tsc_writel(tsc, LPC32XX_TSC_CON, tmp | LPC32XX_TSC_ADCCON_AUTO_EN);
|
||||
}
|
||||
|
||||
static int lpc32xx_ts_open(struct input_dev *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
|
||||
|
||||
lpc32xx_setup_tsc(tsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lpc32xx_ts_close(struct input_dev *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = input_get_drvdata(dev);
|
||||
|
||||
lpc32xx_stop_tsc(tsc);
|
||||
}
|
||||
|
||||
static int __devinit lpc32xx_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc;
|
||||
struct input_dev *input;
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Can't get memory resource\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Can't get interrupt resource\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!tsc || !input) {
|
||||
dev_err(&pdev->dev, "failed allocating memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsc->dev = input;
|
||||
tsc->irq = irq;
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
if (!request_mem_region(res->start, size, pdev->name)) {
|
||||
dev_err(&pdev->dev, "TSC registers are not free\n");
|
||||
error = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsc->tsc_base = ioremap(res->start, size);
|
||||
if (!tsc->tsc_base) {
|
||||
dev_err(&pdev->dev, "Can't map memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
tsc->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(tsc->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock\n");
|
||||
error = PTR_ERR(tsc->clk);
|
||||
goto err_unmap;
|
||||
}
|
||||
|
||||
input->name = MOD_NAME;
|
||||
input->phys = "lpc32xx/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0002;
|
||||
input->id.version = 0x0100;
|
||||
input->dev.parent = &pdev->dev;
|
||||
input->open = lpc32xx_ts_open;
|
||||
input->close = lpc32xx_ts_close;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
input_set_abs_params(input, ABS_X, LPC32XX_TSC_MIN_XY_VAL,
|
||||
LPC32XX_TSC_MAX_XY_VAL, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, LPC32XX_TSC_MIN_XY_VAL,
|
||||
LPC32XX_TSC_MAX_XY_VAL, 0, 0);
|
||||
|
||||
input_set_drvdata(input, tsc);
|
||||
|
||||
error = request_irq(tsc->irq, lpc32xx_ts_interrupt,
|
||||
IRQF_DISABLED, pdev->name, tsc);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed requesting interrupt\n");
|
||||
goto err_put_clock;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed registering input device\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tsc);
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(tsc->irq, tsc);
|
||||
err_put_clock:
|
||||
clk_put(tsc->clk);
|
||||
err_unmap:
|
||||
iounmap(tsc->tsc_base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, size);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(tsc);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit lpc32xx_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
free_irq(tsc->irq, tsc);
|
||||
|
||||
input_unregister_device(tsc->dev);
|
||||
|
||||
clk_put(tsc->clk);
|
||||
|
||||
iounmap(tsc->tsc_base);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(tsc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int lpc32xx_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
|
||||
struct input_dev *input = tsc->dev;
|
||||
|
||||
/*
|
||||
* Suspend and resume can be called when the device hasn't been
|
||||
* enabled. If there are no users that have the device open, then
|
||||
* avoid calling the TSC stop and start functions as the TSC
|
||||
* isn't yet clocked.
|
||||
*/
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users) {
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(tsc->irq);
|
||||
else
|
||||
lpc32xx_stop_tsc(tsc);
|
||||
}
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpc32xx_ts_resume(struct device *dev)
|
||||
{
|
||||
struct lpc32xx_tsc *tsc = dev_get_drvdata(dev);
|
||||
struct input_dev *input = tsc->dev;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users) {
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(tsc->irq);
|
||||
else
|
||||
lpc32xx_setup_tsc(tsc);
|
||||
}
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
|
||||
.suspend = lpc32xx_ts_suspend,
|
||||
.resume = lpc32xx_ts_resume,
|
||||
};
|
||||
#define LPC32XX_TS_PM_OPS (&lpc32xx_ts_pm_ops)
|
||||
#else
|
||||
#define LPC32XX_TS_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver lpc32xx_ts_driver = {
|
||||
.probe = lpc32xx_ts_probe,
|
||||
.remove = __devexit_p(lpc32xx_ts_remove),
|
||||
.driver = {
|
||||
.name = MOD_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = LPC32XX_TS_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init lpc32xx_ts_init(void)
|
||||
{
|
||||
return platform_driver_register(&lpc32xx_ts_driver);
|
||||
}
|
||||
module_init(lpc32xx_ts_init);
|
||||
|
||||
static void __exit lpc32xx_ts_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&lpc32xx_ts_driver);
|
||||
}
|
||||
module_exit(lpc32xx_ts_exit);
|
||||
|
||||
MODULE_AUTHOR("Kevin Wells <[email protected]");
|
||||
MODULE_DESCRIPTION("LPC32XX TSC Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:lpc32xx_ts");
|
||||
|
|
@ -350,7 +350,7 @@ static int __devinit s3c2410ts_probe(struct platform_device *pdev)
|
|||
err_tcirq:
|
||||
free_irq(ts.irq_tc, ts.input);
|
||||
err_inputdev:
|
||||
input_unregister_device(ts.input);
|
||||
input_free_device(ts.input);
|
||||
err_iomap:
|
||||
iounmap(ts.io);
|
||||
err_clk:
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)
|
|||
struct stmpe_touch *ts;
|
||||
struct input_dev *idev;
|
||||
struct stmpe_ts_platform_data *ts_pdata = NULL;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
int ts_irq;
|
||||
|
||||
ts_irq = platform_get_irq_byname(pdev, "FIFO_TH");
|
||||
|
|
@ -276,12 +276,16 @@ static int __devinit stmpe_input_probe(struct platform_device *pdev)
|
|||
return ts_irq;
|
||||
|
||||
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
if (!ts) {
|
||||
ret = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
idev = input_allocate_device();
|
||||
if (!idev)
|
||||
if (!idev) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free_ts;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
ts->stmpe = stmpe;
|
||||
|
|
@ -361,7 +365,6 @@ static int __devexit stmpe_ts_remove(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
input_unregister_device(ts->idev);
|
||||
input_free_device(ts->idev);
|
||||
|
||||
kfree(ts);
|
||||
|
||||
|
|
|
|||
|
|
@ -335,6 +335,7 @@ static int tps6507x_ts_probe(struct platform_device *pdev)
|
|||
dev_err(tsc->dev, "schedule failed");
|
||||
goto err2;
|
||||
}
|
||||
platform_set_drvdata(pdev, tps6507x_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
@ -358,7 +359,7 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
|
|||
cancel_delayed_work_sync(&tsc->work);
|
||||
destroy_workqueue(tsc->wq);
|
||||
|
||||
input_free_device(input_dev);
|
||||
input_unregister_device(input_dev);
|
||||
|
||||
tps6507x_dev->ts = NULL;
|
||||
kfree(tsc);
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ static int __devinit tsc2007_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct tsc2007 *ts;
|
||||
struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;
|
||||
struct tsc2007_platform_data *pdata = client->dev.platform_data;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
* Wacom W8001 penabled serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2008 Jaya Kumar
|
||||
* Copyright (c) 2010 Red Hat, Inc.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
|
|
@ -30,11 +31,24 @@ MODULE_LICENSE("GPL");
|
|||
#define W8001_LEAD_BYTE 0x80
|
||||
#define W8001_TAB_MASK 0x40
|
||||
#define W8001_TAB_BYTE 0x40
|
||||
/* set in first byte of touch data packets */
|
||||
#define W8001_TOUCH_MASK (0x10 | W8001_LEAD_MASK)
|
||||
#define W8001_TOUCH_BYTE (0x10 | W8001_LEAD_BYTE)
|
||||
|
||||
#define W8001_QUERY_PACKET 0x20
|
||||
|
||||
#define W8001_CMD_START '1'
|
||||
#define W8001_CMD_QUERY '*'
|
||||
#define W8001_CMD_TOUCHQUERY '%'
|
||||
|
||||
/* length of data packets in bytes, depends on device. */
|
||||
#define W8001_PKTLEN_TOUCH93 5
|
||||
#define W8001_PKTLEN_TOUCH9A 7
|
||||
#define W8001_PKTLEN_TPCPEN 9
|
||||
#define W8001_PKTLEN_TPCCTL 11 /* control packet */
|
||||
#define W8001_PKTLEN_TOUCH2FG 13
|
||||
|
||||
#define MAX_TRACKING_ID 0xFF /* arbitrarily chosen */
|
||||
|
||||
struct w8001_coord {
|
||||
u8 rdy;
|
||||
|
|
@ -48,6 +62,15 @@ struct w8001_coord {
|
|||
u8 tilt_y;
|
||||
};
|
||||
|
||||
/* touch query reply packet */
|
||||
struct w8001_touch_query {
|
||||
u8 panel_res;
|
||||
u8 capacity_res;
|
||||
u8 sensor_id;
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
|
|
@ -62,6 +85,9 @@ struct w8001 {
|
|||
unsigned char response[W8001_MAX_LENGTH];
|
||||
unsigned char data[W8001_MAX_LENGTH];
|
||||
char phys[32];
|
||||
int type;
|
||||
unsigned int pktlen;
|
||||
int trkid[2];
|
||||
};
|
||||
|
||||
static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
|
|
@ -88,11 +114,98 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
|
|||
coord->tilt_y = data[8] & 0x7F;
|
||||
}
|
||||
|
||||
static void parse_touch(struct w8001 *w8001)
|
||||
{
|
||||
static int trkid;
|
||||
struct input_dev *dev = w8001->dev;
|
||||
unsigned char *data = w8001->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
input_mt_slot(dev, i);
|
||||
|
||||
if (data[0] & (1 << i)) {
|
||||
int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
|
||||
int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
|
||||
/* data[5,6] and [11,12] is finger capacity */
|
||||
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER);
|
||||
if (w8001->trkid[i] < 0)
|
||||
w8001->trkid[i] = trkid++ & MAX_TRACKING_ID;
|
||||
} else {
|
||||
w8001->trkid[i] = -1;
|
||||
}
|
||||
input_report_abs(dev, ABS_MT_TRACKING_ID, w8001->trkid[i]);
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
|
||||
{
|
||||
memset(query, 0, sizeof(*query));
|
||||
|
||||
query->panel_res = data[1];
|
||||
query->sensor_id = data[2] & 0x7;
|
||||
query->capacity_res = data[7];
|
||||
|
||||
query->x = data[3] << 9;
|
||||
query->x |= data[4] << 2;
|
||||
query->x |= (data[2] >> 5) & 0x3;
|
||||
|
||||
query->y = data[5] << 9;
|
||||
query->y |= data[6] << 2;
|
||||
query->y |= (data[2] >> 3) & 0x3;
|
||||
}
|
||||
|
||||
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
|
||||
/*
|
||||
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
|
||||
* side2/eraser. If rdy && f2 are set, this can be either pen + side2,
|
||||
* or eraser. assume
|
||||
* - if dev is already in proximity and f2 is toggled → pen + side2
|
||||
* - if dev comes into proximity with f2 set → eraser
|
||||
* If f2 disappears after assuming eraser, fake proximity out for
|
||||
* eraser and in for pen.
|
||||
*/
|
||||
|
||||
if (!w8001->type) {
|
||||
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
} else if (w8001->type == BTN_TOOL_RUBBER) {
|
||||
if (!coord->f2) {
|
||||
input_report_abs(dev, ABS_PRESSURE, 0);
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
input_report_key(dev, BTN_STYLUS, 0);
|
||||
input_report_key(dev, BTN_STYLUS2, 0);
|
||||
input_report_key(dev, BTN_TOOL_RUBBER, 0);
|
||||
input_sync(dev);
|
||||
w8001->type = BTN_TOOL_PEN;
|
||||
}
|
||||
} else {
|
||||
input_report_key(dev, BTN_STYLUS2, coord->f2);
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_X, coord->x);
|
||||
input_report_abs(dev, ABS_Y, coord->y);
|
||||
input_report_abs(dev, ABS_PRESSURE, coord->pen_pressure);
|
||||
input_report_key(dev, BTN_TOUCH, coord->tsw);
|
||||
input_report_key(dev, BTN_STYLUS, coord->f1);
|
||||
input_report_key(dev, w8001->type, coord->rdy);
|
||||
input_sync(dev);
|
||||
|
||||
if (!coord->rdy)
|
||||
w8001->type = 0;
|
||||
}
|
||||
|
||||
static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct w8001 *w8001 = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = w8001->dev;
|
||||
struct w8001_coord coord;
|
||||
unsigned char tmp;
|
||||
|
||||
|
|
@ -105,26 +218,45 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
|
|||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
case W8001_PKTLEN_TOUCH93 - 1:
|
||||
case W8001_PKTLEN_TOUCH9A - 1:
|
||||
/* ignore one-finger touch packet. */
|
||||
if (w8001->pktlen == w8001->idx)
|
||||
w8001->idx = 0;
|
||||
break;
|
||||
|
||||
/* Pen coordinates packet */
|
||||
case W8001_PKTLEN_TPCPEN - 1:
|
||||
tmp = w8001->data[0] & W8001_TAB_MASK;
|
||||
if (unlikely(tmp == W8001_TAB_BYTE))
|
||||
break;
|
||||
|
||||
tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
|
||||
if (tmp == W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
w8001->idx = 0;
|
||||
parse_data(w8001->data, &coord);
|
||||
input_report_abs(dev, ABS_X, coord.x);
|
||||
input_report_abs(dev, ABS_Y, coord.y);
|
||||
input_report_abs(dev, ABS_PRESSURE, coord.pen_pressure);
|
||||
input_report_key(dev, BTN_TOUCH, coord.tsw);
|
||||
input_sync(dev);
|
||||
report_pen_events(w8001, &coord);
|
||||
break;
|
||||
|
||||
case 10:
|
||||
/* control packet */
|
||||
case W8001_PKTLEN_TPCCTL - 1:
|
||||
tmp = (w8001->data[0] & W8001_TOUCH_MASK);
|
||||
if (tmp == W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
w8001->idx = 0;
|
||||
memcpy(w8001->response, w8001->data, W8001_MAX_LENGTH);
|
||||
w8001->response_type = W8001_QUERY_PACKET;
|
||||
complete(&w8001->cmd_done);
|
||||
break;
|
||||
|
||||
/* 2 finger touch packet */
|
||||
case W8001_PKTLEN_TOUCH2FG - 1:
|
||||
w8001->idx = 0;
|
||||
parse_touch(w8001);
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
|
@ -167,6 +299,38 @@ static int w8001_setup(struct w8001 *w8001)
|
|||
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
|
||||
|
||||
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
|
||||
if (!error) {
|
||||
struct w8001_touch_query touch;
|
||||
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
|
||||
switch (touch.sensor_id) {
|
||||
case 0:
|
||||
case 2:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH93;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
|
||||
break;
|
||||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
input_mt_create_slots(dev, 2);
|
||||
input_set_abs_params(dev, ABS_MT_TRACKING_ID,
|
||||
0, MAX_TRACKING_ID, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
0, touch.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
|
||||
0, 0, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return w8001_command(w8001, W8001_CMD_START, false);
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +372,7 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
|||
w8001->serio = serio;
|
||||
w8001->id = serio->id.id;
|
||||
w8001->dev = input_dev;
|
||||
w8001->trkid[0] = w8001->trkid[1] = -1;
|
||||
init_completion(&w8001->cmd_done);
|
||||
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||
|
||||
|
|
@ -221,6 +386,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
|||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOOL_PEN)] |= BIT_MASK(BTN_TOOL_PEN);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOOL_RUBBER)] |= BIT_MASK(BTN_TOOL_RUBBER);
|
||||
input_dev->keybit[BIT_WORD(BTN_STYLUS)] |= BIT_MASK(BTN_STYLUS);
|
||||
input_dev->keybit[BIT_WORD(BTN_STYLUS2)] |= BIT_MASK(BTN_STYLUS2);
|
||||
|
||||
serio_set_drvdata(serio, w8001);
|
||||
err = serio_open(serio, drv);
|
||||
|
|
|
|||
|
|
@ -125,6 +125,8 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
|
|||
{
|
||||
int power_adc = 0, auxval;
|
||||
u16 power = 0;
|
||||
int rc = 0;
|
||||
int timeout = 0;
|
||||
|
||||
/* get codec */
|
||||
mutex_lock(&wm->codec_mutex);
|
||||
|
|
@ -143,7 +145,9 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
|
|||
|
||||
/* Turn polling mode on to read AUX ADC */
|
||||
wm->pen_probably_down = 1;
|
||||
wm->codec->poll_sample(wm, adcsel, &auxval);
|
||||
|
||||
while (rc != RC_VALID && timeout++ < 5)
|
||||
rc = wm->codec->poll_sample(wm, adcsel, &auxval);
|
||||
|
||||
if (power_adc)
|
||||
wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000);
|
||||
|
|
@ -152,8 +156,15 @@ int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel)
|
|||
|
||||
wm->pen_probably_down = 0;
|
||||
|
||||
if (timeout >= 5) {
|
||||
dev_err(wm->dev,
|
||||
"timeout reading auxadc %d, disabling digitiser\n",
|
||||
adcsel);
|
||||
wm->codec->dig_enable(wm, false);
|
||||
}
|
||||
|
||||
mutex_unlock(&wm->codec_mutex);
|
||||
return auxval & 0xfff;
|
||||
return (rc == RC_VALID ? auxval & 0xfff : -EBUSY);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc);
|
||||
|
||||
|
|
@ -684,8 +695,7 @@ static int wm97xx_probe(struct device *dev)
|
|||
touch_reg_err:
|
||||
platform_device_put(wm->touch_dev);
|
||||
touch_err:
|
||||
platform_device_unregister(wm->battery_dev);
|
||||
wm->battery_dev = NULL;
|
||||
platform_device_del(wm->battery_dev);
|
||||
batt_reg_err:
|
||||
platform_device_put(wm->battery_dev);
|
||||
batt_err:
|
||||
|
|
|
|||
Loading…
Reference in a new issue