mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-19 16:14:13 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input layer updates from Dmitry Torokhov: - a bunch of new drivers (DA9052/53 touchscreenn controller, Synaptics Navpoint, LM8333 keypads, Wacom I2C touhscreen); - updates to existing touchpad drivers (ALPS, Sntelic); - Wacom driver now supports Intuos5; - device-tree bindings in numerous drivers; - other cleanups and fixes. Fix annoying conflict in drivers/input/tablet/wacom_wac.c that I think implies that the input layer device naming is broken, but let's see. I brough it up with Dmitry. * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (57 commits) Input: matrix-keymap - fix building keymaps Input: spear-keyboard - document DT bindings Input: spear-keyboard - add device tree bindings Input: matrix-keymap - wire up device tree support Input: matrix-keymap - uninline and prepare for device tree support Input: adp5588 - add support for gpio names Input: omap-keypad - dynamically handle register offsets Input: synaptics - fix compile warning MAINTAINERS: adjust input-related patterns Input: ALPS - switch to using input_mt_report_finger_count Input: ALPS - add semi-MT support for v4 protocol Input: Add Synaptics NavPoint (PXA27x SSP/SPI) driver Input: atmel_mxt_ts - dump each message on just 1 line Input: atmel_mxt_ts - do not read extra (checksum) byte Input: atmel_mxt_ts - verify object size in mxt_write_object Input: atmel_mxt_ts - only allow root to update firmware Input: atmel_mxt_ts - use CONFIG_PM_SLEEP Input: sentelic - report device's production serial number Input: tl6040-vibra - Device Tree support Input: evdev - properly handle read/write with count 0 ...
This commit is contained in:
commit
2c01e7bc46
@ -23,9 +23,10 @@ Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Attribute group for control of the status LEDs and the OLEDs.
|
||||
This attribute group is only available for Intuos 4 M, L,
|
||||
and XL (with LEDs and OLEDs) and Cintiq 21UX2 and Cintiq 24HD
|
||||
(LEDs only). Therefore its presence implicitly signifies the
|
||||
presence of said LEDs and OLEDs on the tablet device.
|
||||
and XL (with LEDs and OLEDs), Intuos 5 (LEDs only), and Cintiq
|
||||
21UX2 and Cintiq 24HD (LEDs only). Therefore its presence
|
||||
implicitly signifies the presence of said LEDs and OLEDs on the
|
||||
tablet device.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status0_luminance
|
||||
Date: August 2011
|
||||
@ -48,10 +49,10 @@ What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led0
|
||||
Date: August 2011
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets which one of the four (for Intuos 4)
|
||||
or of the right four (for Cintiq 21UX2 and Cintiq 24HD) status
|
||||
LEDs is active (0..3). The other three LEDs on the same side are
|
||||
always inactive.
|
||||
Writing to this file sets which one of the four (for Intuos 4
|
||||
and Intuos 5) or of the right four (for Cintiq 21UX2 and Cintiq
|
||||
24HD) status LEDs is active (0..3). The other three LEDs on the
|
||||
same side are always inactive.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led1_select
|
||||
Date: September 2011
|
||||
|
20
Documentation/devicetree/bindings/input/spear-keyboard.txt
Normal file
20
Documentation/devicetree/bindings/input/spear-keyboard.txt
Normal file
@ -0,0 +1,20 @@
|
||||
* SPEAr keyboard controller
|
||||
|
||||
Required properties:
|
||||
- compatible: "st,spear300-kbd"
|
||||
|
||||
Optional properties, in addition to those specified by the shared
|
||||
matrix-keyboard bindings:
|
||||
- autorepeat: bool: enables key autorepeat
|
||||
- st,mode: keyboard mode: 0 - 9x9, 1 - 6x6, 2 - 2x2
|
||||
|
||||
Example:
|
||||
|
||||
kbd@fc400000 {
|
||||
compatible = "st,spear300-kbd";
|
||||
reg = <0xfc400000 0x100>;
|
||||
linux,keymap = < 0x00030012
|
||||
0x0102003a >;
|
||||
autorepeat;
|
||||
st,mode = <0>;
|
||||
};
|
@ -0,0 +1,16 @@
|
||||
* NXP LPC32xx SoC Touchscreen Controller (TSC)
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "nxp,lpc3220-tsc"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- interrupts: The TSC/ADC interrupt
|
||||
|
||||
Example:
|
||||
|
||||
tsc@40048000 {
|
||||
compatible = "nxp,lpc3220-tsc";
|
||||
reg = <0x40048000 0x1000>;
|
||||
interrupt-parent = <&mic>;
|
||||
interrupts = <39 0>;
|
||||
};
|
37
Documentation/devicetree/bindings/input/twl6040-vibra.txt
Normal file
37
Documentation/devicetree/bindings/input/twl6040-vibra.txt
Normal file
@ -0,0 +1,37 @@
|
||||
Vibra driver for the twl6040 family
|
||||
|
||||
The vibra driver is a child of the twl6040 MFD dirver.
|
||||
Documentation/devicetree/bindings/mfd/twl6040.txt
|
||||
|
||||
Required properties:
|
||||
- compatible : Must be "ti,twl6040-vibra";
|
||||
- interrupts: 4, Vibra overcurrent interrupt
|
||||
- vddvibl-supply: Regulator supplying the left vibra motor
|
||||
- vddvibr-supply: Regulator supplying the right vibra motor
|
||||
- vibldrv_res: Board specific left driver resistance
|
||||
- vibrdrv_res: Board specific right driver resistance
|
||||
- viblmotor_res: Board specific left motor resistance
|
||||
- vibrmotor_res: Board specific right motor resistance
|
||||
|
||||
Optional properties:
|
||||
- vddvibl_uV: If the vddvibl default voltage need to be changed
|
||||
- vddvibr_uV: If the vddvibr default voltage need to be changed
|
||||
|
||||
Example:
|
||||
/*
|
||||
* 8-channel high quality low-power audio codec
|
||||
* http://www.ti.com/lit/ds/symlink/twl6040.pdf
|
||||
*/
|
||||
twl6040: twl6040@4b {
|
||||
...
|
||||
twl6040_vibra: twl6040@1 {
|
||||
compatible = "ti,twl6040-vibra";
|
||||
interrupts = <4>;
|
||||
vddvibl-supply = <&vbat>;
|
||||
vddvibr-supply = <&vbat>;
|
||||
vibldrv_res = <8>;
|
||||
vibrdrv_res = <3>;
|
||||
viblmotor_res = <10>;
|
||||
vibrmotor_res = <10>;
|
||||
};
|
||||
};
|
@ -3465,6 +3465,8 @@ Q: http://patchwork.kernel.org/project/linux-input/list/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git
|
||||
S: Maintained
|
||||
F: drivers/input/
|
||||
F: include/linux/input.h
|
||||
F: include/linux/input/
|
||||
|
||||
INPUT MULTITOUCH (MT) PROTOCOL
|
||||
M: Henrik Rydberg <rydberg@euromail.se>
|
||||
|
@ -25,10 +25,6 @@ config INPUT
|
||||
|
||||
if INPUT
|
||||
|
||||
config INPUT_OF_MATRIX_KEYMAP
|
||||
depends on USE_OF
|
||||
bool
|
||||
|
||||
config INPUT_FF_MEMLESS
|
||||
tristate "Support for memoryless force-feedback devices"
|
||||
help
|
||||
@ -68,6 +64,19 @@ config INPUT_SPARSEKMAP
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sparse-keymap.
|
||||
|
||||
config INPUT_MATRIXKMAP
|
||||
tristate "Matrix keymap support library"
|
||||
help
|
||||
Say Y here if you are using a driver for an input
|
||||
device that uses matrix keymap. This option is only
|
||||
useful for out-of-tree drivers since in-tree drivers
|
||||
select it automatically.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called matrix-keymap.
|
||||
|
||||
comment "Userland interfaces"
|
||||
|
||||
config INPUT_MOUSEDEV
|
||||
|
@ -10,6 +10,7 @@ input-core-y := input.o input-compat.o input-mt.o ff-core.o
|
||||
obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
|
||||
obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
|
||||
obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
|
||||
obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o
|
||||
|
||||
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
|
||||
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
|
||||
@ -24,4 +25,3 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
|
||||
obj-$(CONFIG_INPUT_MISC) += misc/
|
||||
|
||||
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
|
||||
obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o
|
||||
|
@ -180,7 +180,10 @@ static int evdev_grab(struct evdev *evdev, struct evdev_client *client)
|
||||
|
||||
static int evdev_ungrab(struct evdev *evdev, struct evdev_client *client)
|
||||
{
|
||||
if (evdev->grab != client)
|
||||
struct evdev_client *grab = rcu_dereference_protected(evdev->grab,
|
||||
lockdep_is_held(&evdev->mutex));
|
||||
|
||||
if (grab != client)
|
||||
return -EINVAL;
|
||||
|
||||
rcu_assign_pointer(evdev->grab, NULL);
|
||||
@ -259,8 +262,7 @@ static int evdev_release(struct inode *inode, struct file *file)
|
||||
struct evdev *evdev = client->evdev;
|
||||
|
||||
mutex_lock(&evdev->mutex);
|
||||
if (evdev->grab == client)
|
||||
evdev_ungrab(evdev, client);
|
||||
evdev_ungrab(evdev, client);
|
||||
mutex_unlock(&evdev->mutex);
|
||||
|
||||
evdev_detach_client(evdev, client);
|
||||
@ -343,7 +345,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||
struct input_event event;
|
||||
int retval = 0;
|
||||
|
||||
if (count < input_event_size())
|
||||
if (count != 0 && count < input_event_size())
|
||||
return -EINVAL;
|
||||
|
||||
retval = mutex_lock_interruptible(&evdev->mutex);
|
||||
@ -355,7 +357,8 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||
goto out;
|
||||
}
|
||||
|
||||
do {
|
||||
while (retval + input_event_size() <= count) {
|
||||
|
||||
if (input_event_from_user(buffer + retval, &event)) {
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
@ -364,7 +367,7 @@ static ssize_t evdev_write(struct file *file, const char __user *buffer,
|
||||
|
||||
input_inject_event(&evdev->handle,
|
||||
event.type, event.code, event.value);
|
||||
} while (retval + input_event_size() <= count);
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&evdev->mutex);
|
||||
@ -395,35 +398,49 @@ static ssize_t evdev_read(struct file *file, char __user *buffer,
|
||||
struct evdev_client *client = file->private_data;
|
||||
struct evdev *evdev = client->evdev;
|
||||
struct input_event event;
|
||||
int retval = 0;
|
||||
size_t read = 0;
|
||||
int error;
|
||||
|
||||
if (count < input_event_size())
|
||||
if (count != 0 && count < input_event_size())
|
||||
return -EINVAL;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK)) {
|
||||
retval = wait_event_interruptible(evdev->wait,
|
||||
client->packet_head != client->tail ||
|
||||
!evdev->exist);
|
||||
if (retval)
|
||||
return retval;
|
||||
for (;;) {
|
||||
if (!evdev->exist)
|
||||
return -ENODEV;
|
||||
|
||||
if (client->packet_head == client->tail &&
|
||||
(file->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
/*
|
||||
* count == 0 is special - no IO is done but we check
|
||||
* for error conditions (see above).
|
||||
*/
|
||||
if (count == 0)
|
||||
break;
|
||||
|
||||
while (read + input_event_size() <= count &&
|
||||
evdev_fetch_next_event(client, &event)) {
|
||||
|
||||
if (input_event_to_user(buffer + read, &event))
|
||||
return -EFAULT;
|
||||
|
||||
read += input_event_size();
|
||||
}
|
||||
|
||||
if (read)
|
||||
break;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK)) {
|
||||
error = wait_event_interruptible(evdev->wait,
|
||||
client->packet_head != client->tail ||
|
||||
!evdev->exist);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
if (!evdev->exist)
|
||||
return -ENODEV;
|
||||
|
||||
while (retval + input_event_size() <= count &&
|
||||
evdev_fetch_next_event(client, &event)) {
|
||||
|
||||
if (input_event_to_user(buffer + retval, &event))
|
||||
return -EFAULT;
|
||||
|
||||
retval += input_event_size();
|
||||
}
|
||||
|
||||
if (retval == 0 && (file->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
return retval;
|
||||
return read;
|
||||
}
|
||||
|
||||
/* No kernel lock - fine */
|
||||
|
@ -125,15 +125,4 @@ static struct pci_driver emu_driver = {
|
||||
.remove = __devexit_p(emu_remove),
|
||||
};
|
||||
|
||||
static int __init emu_init(void)
|
||||
{
|
||||
return pci_register_driver(&emu_driver);
|
||||
}
|
||||
|
||||
static void __exit emu_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&emu_driver);
|
||||
}
|
||||
|
||||
module_init(emu_init);
|
||||
module_exit(emu_exit);
|
||||
module_pci_driver(emu_driver);
|
||||
|
@ -144,6 +144,7 @@ static const struct pci_device_id fm801_gp_id_table[] = {
|
||||
{ PCI_VENDOR_ID_FORTEMEDIA, PCI_DEVICE_ID_FM801_GP, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
||||
{ 0 }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
|
||||
|
||||
static struct pci_driver fm801_gp_driver = {
|
||||
.name = "FM801_gameport",
|
||||
@ -152,20 +153,7 @@ static struct pci_driver fm801_gp_driver = {
|
||||
.remove = __devexit_p(fm801_gp_remove),
|
||||
};
|
||||
|
||||
static int __init fm801_gp_init(void)
|
||||
{
|
||||
return pci_register_driver(&fm801_gp_driver);
|
||||
}
|
||||
|
||||
static void __exit fm801_gp_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&fm801_gp_driver);
|
||||
}
|
||||
|
||||
module_init(fm801_gp_init);
|
||||
module_exit(fm801_gp_exit);
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
|
||||
module_pci_driver(fm801_gp_driver);
|
||||
|
||||
MODULE_DESCRIPTION("FM801 gameport driver");
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
|
@ -413,15 +413,4 @@ static struct gameport_driver a3d_drv = {
|
||||
.disconnect = a3d_disconnect,
|
||||
};
|
||||
|
||||
static int __init a3d_init(void)
|
||||
{
|
||||
return gameport_register_driver(&a3d_drv);
|
||||
}
|
||||
|
||||
static void __exit a3d_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&a3d_drv);
|
||||
}
|
||||
|
||||
module_init(a3d_init);
|
||||
module_exit(a3d_exit);
|
||||
module_gameport_driver(a3d_drv);
|
||||
|
@ -557,10 +557,6 @@ static void adi_disconnect(struct gameport *gameport)
|
||||
kfree(port);
|
||||
}
|
||||
|
||||
/*
|
||||
* The gameport device structure.
|
||||
*/
|
||||
|
||||
static struct gameport_driver adi_drv = {
|
||||
.driver = {
|
||||
.name = "adi",
|
||||
@ -570,15 +566,4 @@ static struct gameport_driver adi_drv = {
|
||||
.disconnect = adi_disconnect,
|
||||
};
|
||||
|
||||
static int __init adi_init(void)
|
||||
{
|
||||
return gameport_register_driver(&adi_drv);
|
||||
}
|
||||
|
||||
static void __exit adi_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&adi_drv);
|
||||
}
|
||||
|
||||
module_init(adi_init);
|
||||
module_exit(adi_exit);
|
||||
module_gameport_driver(adi_drv);
|
||||
|
@ -261,15 +261,4 @@ static struct gameport_driver cobra_drv = {
|
||||
.disconnect = cobra_disconnect,
|
||||
};
|
||||
|
||||
static int __init cobra_init(void)
|
||||
{
|
||||
return gameport_register_driver(&cobra_drv);
|
||||
}
|
||||
|
||||
static void __exit cobra_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&cobra_drv);
|
||||
}
|
||||
|
||||
module_init(cobra_init);
|
||||
module_exit(cobra_exit);
|
||||
module_gameport_driver(cobra_drv);
|
||||
|
@ -373,15 +373,4 @@ static struct gameport_driver gf2k_drv = {
|
||||
.disconnect = gf2k_disconnect,
|
||||
};
|
||||
|
||||
static int __init gf2k_init(void)
|
||||
{
|
||||
return gameport_register_driver(&gf2k_drv);
|
||||
}
|
||||
|
||||
static void __exit gf2k_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&gf2k_drv);
|
||||
}
|
||||
|
||||
module_init(gf2k_init);
|
||||
module_exit(gf2k_exit);
|
||||
module_gameport_driver(gf2k_drv);
|
||||
|
@ -424,15 +424,4 @@ static struct gameport_driver grip_drv = {
|
||||
.disconnect = grip_disconnect,
|
||||
};
|
||||
|
||||
static int __init grip_init(void)
|
||||
{
|
||||
return gameport_register_driver(&grip_drv);
|
||||
}
|
||||
|
||||
static void __exit grip_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&grip_drv);
|
||||
}
|
||||
|
||||
module_init(grip_init);
|
||||
module_exit(grip_exit);
|
||||
module_gameport_driver(grip_drv);
|
||||
|
@ -687,15 +687,4 @@ static struct gameport_driver grip_drv = {
|
||||
.disconnect = grip_disconnect,
|
||||
};
|
||||
|
||||
static int __init grip_init(void)
|
||||
{
|
||||
return gameport_register_driver(&grip_drv);
|
||||
}
|
||||
|
||||
static void __exit grip_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&grip_drv);
|
||||
}
|
||||
|
||||
module_init(grip_init);
|
||||
module_exit(grip_exit);
|
||||
module_gameport_driver(grip_drv);
|
||||
|
@ -281,15 +281,4 @@ static struct gameport_driver guillemot_drv = {
|
||||
.disconnect = guillemot_disconnect,
|
||||
};
|
||||
|
||||
static int __init guillemot_init(void)
|
||||
{
|
||||
return gameport_register_driver(&guillemot_drv);
|
||||
}
|
||||
|
||||
static void __exit guillemot_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&guillemot_drv);
|
||||
}
|
||||
|
||||
module_init(guillemot_init);
|
||||
module_exit(guillemot_exit);
|
||||
module_gameport_driver(guillemot_drv);
|
||||
|
@ -311,15 +311,4 @@ static struct gameport_driver interact_drv = {
|
||||
.disconnect = interact_disconnect,
|
||||
};
|
||||
|
||||
static int __init interact_init(void)
|
||||
{
|
||||
return gameport_register_driver(&interact_drv);
|
||||
}
|
||||
|
||||
static void __exit interact_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&interact_drv);
|
||||
}
|
||||
|
||||
module_init(interact_init);
|
||||
module_exit(interact_exit);
|
||||
module_gameport_driver(interact_drv);
|
||||
|
@ -159,15 +159,4 @@ static struct gameport_driver joydump_drv = {
|
||||
.disconnect = joydump_disconnect,
|
||||
};
|
||||
|
||||
static int __init joydump_init(void)
|
||||
{
|
||||
return gameport_register_driver(&joydump_drv);
|
||||
}
|
||||
|
||||
static void __exit joydump_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&joydump_drv);
|
||||
}
|
||||
|
||||
module_init(joydump_init);
|
||||
module_exit(joydump_exit);
|
||||
module_gameport_driver(joydump_drv);
|
||||
|
@ -222,19 +222,4 @@ static struct serio_driver magellan_drv = {
|
||||
.disconnect = magellan_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init magellan_init(void)
|
||||
{
|
||||
return serio_register_driver(&magellan_drv);
|
||||
}
|
||||
|
||||
static void __exit magellan_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&magellan_drv);
|
||||
}
|
||||
|
||||
module_init(magellan_init);
|
||||
module_exit(magellan_exit);
|
||||
module_serio_driver(magellan_drv);
|
||||
|
@ -820,15 +820,4 @@ static struct gameport_driver sw_drv = {
|
||||
.disconnect = sw_disconnect,
|
||||
};
|
||||
|
||||
static int __init sw_init(void)
|
||||
{
|
||||
return gameport_register_driver(&sw_drv);
|
||||
}
|
||||
|
||||
static void __exit sw_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&sw_drv);
|
||||
}
|
||||
|
||||
module_init(sw_init);
|
||||
module_exit(sw_exit);
|
||||
module_gameport_driver(sw_drv);
|
||||
|
@ -296,19 +296,4 @@ static struct serio_driver spaceball_drv = {
|
||||
.disconnect = spaceball_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init spaceball_init(void)
|
||||
{
|
||||
return serio_register_driver(&spaceball_drv);
|
||||
}
|
||||
|
||||
static void __exit spaceball_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&spaceball_drv);
|
||||
}
|
||||
|
||||
module_init(spaceball_init);
|
||||
module_exit(spaceball_exit);
|
||||
module_serio_driver(spaceball_drv);
|
||||
|
@ -237,19 +237,4 @@ static struct serio_driver spaceorb_drv = {
|
||||
.disconnect = spaceorb_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init spaceorb_init(void)
|
||||
{
|
||||
return serio_register_driver(&spaceorb_drv);
|
||||
}
|
||||
|
||||
static void __exit spaceorb_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&spaceorb_drv);
|
||||
}
|
||||
|
||||
module_init(spaceorb_init);
|
||||
module_exit(spaceorb_exit);
|
||||
module_serio_driver(spaceorb_drv);
|
||||
|
@ -208,19 +208,4 @@ static struct serio_driver stinger_drv = {
|
||||
.disconnect = stinger_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init stinger_init(void)
|
||||
{
|
||||
return serio_register_driver(&stinger_drv);
|
||||
}
|
||||
|
||||
static void __exit stinger_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&stinger_drv);
|
||||
}
|
||||
|
||||
module_init(stinger_init);
|
||||
module_exit(stinger_exit);
|
||||
module_serio_driver(stinger_drv);
|
||||
|
@ -436,15 +436,4 @@ static struct gameport_driver tmdc_drv = {
|
||||
.disconnect = tmdc_disconnect,
|
||||
};
|
||||
|
||||
static int __init tmdc_init(void)
|
||||
{
|
||||
return gameport_register_driver(&tmdc_drv);
|
||||
}
|
||||
|
||||
static void __exit tmdc_exit(void)
|
||||
{
|
||||
gameport_unregister_driver(&tmdc_drv);
|
||||
}
|
||||
|
||||
module_init(tmdc_init);
|
||||
module_exit(tmdc_exit);
|
||||
module_gameport_driver(tmdc_drv);
|
||||
|
@ -257,19 +257,4 @@ static struct serio_driver twidjoy_drv = {
|
||||
.disconnect = twidjoy_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init twidjoy_init(void)
|
||||
{
|
||||
return serio_register_driver(&twidjoy_drv);
|
||||
}
|
||||
|
||||
static void __exit twidjoy_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&twidjoy_drv);
|
||||
}
|
||||
|
||||
module_init(twidjoy_init);
|
||||
module_exit(twidjoy_exit);
|
||||
module_serio_driver(twidjoy_drv);
|
||||
|
@ -217,19 +217,4 @@ static struct serio_driver warrior_drv = {
|
||||
.disconnect = warrior_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init warrior_init(void)
|
||||
{
|
||||
return serio_register_driver(&warrior_drv);
|
||||
}
|
||||
|
||||
static void __exit warrior_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&warrior_drv);
|
||||
}
|
||||
|
||||
module_init(warrior_init);
|
||||
module_exit(warrior_exit);
|
||||
module_serio_driver(warrior_drv);
|
||||
|
@ -225,19 +225,4 @@ static struct serio_driver zhenhua_drv = {
|
||||
.disconnect = zhenhua_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init zhenhua_init(void)
|
||||
{
|
||||
return serio_register_driver(&zhenhua_drv);
|
||||
}
|
||||
|
||||
static void __exit zhenhua_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&zhenhua_drv);
|
||||
}
|
||||
|
||||
module_init(zhenhua_init);
|
||||
module_exit(zhenhua_exit);
|
||||
module_serio_driver(zhenhua_drv);
|
||||
|
@ -166,6 +166,7 @@ config KEYBOARD_LKKBD
|
||||
config KEYBOARD_EP93XX
|
||||
tristate "EP93xx Matrix Keypad support"
|
||||
depends on ARCH_EP93XX
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here to enable the matrix keypad on the Cirrus EP93XX.
|
||||
|
||||
@ -224,6 +225,7 @@ config KEYBOARD_TCA6416
|
||||
config KEYBOARD_TCA8418
|
||||
tristate "TCA8418 Keypad Support"
|
||||
depends on I2C
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
This driver implements basic keypad functionality
|
||||
for keys connected through TCA8418 keypad decoder.
|
||||
@ -240,6 +242,7 @@ config KEYBOARD_TCA8418
|
||||
config KEYBOARD_MATRIX
|
||||
tristate "GPIO driven matrix keypad support"
|
||||
depends on GENERIC_GPIO
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Enable support for GPIO driven matrix keypad.
|
||||
|
||||
@ -309,6 +312,17 @@ config KEYBOARD_LM8323
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lm8323.
|
||||
|
||||
config KEYBOARD_LM8333
|
||||
tristate "LM8333 keypad chip"
|
||||
depends on I2C
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
If you say yes here you get support for the National Semiconductor
|
||||
LM8333 keypad controller.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lm8333.
|
||||
|
||||
config KEYBOARD_LOCOMO
|
||||
tristate "LoCoMo Keyboard Support"
|
||||
depends on SHARP_LOCOMO
|
||||
@ -366,6 +380,7 @@ config KEYBOARD_MPR121
|
||||
config KEYBOARD_IMX
|
||||
tristate "IMX keypad support"
|
||||
depends on ARCH_MXC
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Enable support for IMX keypad port.
|
||||
|
||||
@ -384,6 +399,7 @@ config KEYBOARD_NEWTON
|
||||
config KEYBOARD_NOMADIK
|
||||
tristate "ST-Ericsson Nomadik SKE keyboard"
|
||||
depends on PLAT_NOMADIK
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use a keypad provided on the SKE controller
|
||||
used on the Ux500 and Nomadik platforms
|
||||
@ -394,7 +410,7 @@ config KEYBOARD_NOMADIK
|
||||
config KEYBOARD_TEGRA
|
||||
tristate "NVIDIA Tegra internal matrix keyboard controller support"
|
||||
depends on ARCH_TEGRA
|
||||
select INPUT_OF_MATRIX_KEYMAP if USE_OF
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use a matrix keyboard connected directly
|
||||
to the internal keyboard controller on Tegra SoCs.
|
||||
@ -432,6 +448,7 @@ config KEYBOARD_PXA930_ROTARY
|
||||
config KEYBOARD_PMIC8XXX
|
||||
tristate "Qualcomm PMIC8XXX keypad support"
|
||||
depends on MFD_PM8XXX
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to enable the driver for the PMIC8XXX
|
||||
keypad provided as a reference design from Qualcomm. This is intended
|
||||
@ -443,6 +460,7 @@ config KEYBOARD_PMIC8XXX
|
||||
config KEYBOARD_SAMSUNG
|
||||
tristate "Samsung keypad support"
|
||||
depends on HAVE_CLK
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the keypad on your Samsung mobile
|
||||
device.
|
||||
@ -485,6 +503,7 @@ config KEYBOARD_SH_KEYSC
|
||||
config KEYBOARD_STMPE
|
||||
tristate "STMPE keypad support"
|
||||
depends on MFD_STMPE
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the keypad controller on STMPE I/O
|
||||
expanders.
|
||||
@ -505,6 +524,7 @@ config KEYBOARD_DAVINCI
|
||||
config KEYBOARD_OMAP
|
||||
tristate "TI OMAP keypad support"
|
||||
depends on (ARCH_OMAP1 || ARCH_OMAP2)
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the OMAP keypad.
|
||||
|
||||
@ -512,9 +532,10 @@ config KEYBOARD_OMAP
|
||||
module will be called omap-keypad.
|
||||
|
||||
config KEYBOARD_OMAP4
|
||||
tristate "TI OMAP4 keypad support"
|
||||
tristate "TI OMAP4+ keypad support"
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the OMAP4 keypad.
|
||||
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.
|
||||
@ -522,6 +543,7 @@ config KEYBOARD_OMAP4
|
||||
config KEYBOARD_SPEAR
|
||||
tristate "ST SPEAR keyboard support"
|
||||
depends on PLAT_SPEAR
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the SPEAR keyboard.
|
||||
|
||||
@ -531,6 +553,7 @@ config KEYBOARD_SPEAR
|
||||
config KEYBOARD_TC3589X
|
||||
tristate "TC3589X Keypad support"
|
||||
depends on MFD_TC3589X
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the keypad controller on
|
||||
TC35892/3 I/O expander.
|
||||
@ -541,6 +564,7 @@ config KEYBOARD_TC3589X
|
||||
config KEYBOARD_TNETV107X
|
||||
tristate "TI TNETV107X keypad support"
|
||||
depends on ARCH_DAVINCI_TNETV107X
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if you want to use the TNETV107X keypad.
|
||||
|
||||
@ -550,6 +574,7 @@ config KEYBOARD_TNETV107X
|
||||
config KEYBOARD_TWL4030
|
||||
tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
|
||||
depends on TWL4030_CORE
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here if your board use the keypad controller on
|
||||
TWL4030 family chips. It's safe to say enable this
|
||||
@ -573,6 +598,7 @@ config KEYBOARD_XTKBD
|
||||
config KEYBOARD_W90P910
|
||||
tristate "W90P910 Matrix Keypad support"
|
||||
depends on ARCH_W90X900
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Say Y here to enable the matrix keypad on evaluation board
|
||||
based on W90P910.
|
||||
|
@ -24,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
|
||||
obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o
|
||||
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
|
||||
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
||||
|
@ -197,6 +197,7 @@ static int __devinit adp5588_gpio_add(struct adp5588_kpad *kpad)
|
||||
kpad->gc.base = gpio_data->gpio_start;
|
||||
kpad->gc.label = kpad->client->name;
|
||||
kpad->gc.owner = THIS_MODULE;
|
||||
kpad->gc.names = gpio_data->names;
|
||||
|
||||
mutex_init(&kpad->gpio_lock);
|
||||
|
||||
|
@ -433,7 +433,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
|
||||
if (printk_ratelimit())
|
||||
dev_warn(&serio->dev,
|
||||
"Spurious %s on %s. "
|
||||
"Some program might be trying access hardware directly.\n",
|
||||
"Some program might be trying to access hardware directly.\n",
|
||||
data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
|
||||
goto out;
|
||||
case ATKBD_RET_ERR:
|
||||
|
@ -182,16 +182,10 @@ static void ep93xx_keypad_close(struct input_dev *pdev)
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* NOTE: I don't know if this is correct, or will work on the ep93xx.
|
||||
*
|
||||
* None of the existing ep93xx drivers have power management support.
|
||||
* But, this is basically what the pxa27x_keypad driver does.
|
||||
*/
|
||||
static int ep93xx_keypad_suspend(struct platform_device *pdev,
|
||||
pm_message_t state)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ep93xx_keypad_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
|
||||
struct input_dev *input_dev = keypad->input_dev;
|
||||
|
||||
@ -210,8 +204,9 @@ static int ep93xx_keypad_suspend(struct platform_device *pdev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_keypad_resume(struct platform_device *pdev)
|
||||
static int ep93xx_keypad_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
|
||||
struct input_dev *input_dev = keypad->input_dev;
|
||||
|
||||
@ -232,10 +227,10 @@ static int ep93xx_keypad_resume(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else /* !CONFIG_PM */
|
||||
#define ep93xx_keypad_suspend NULL
|
||||
#define ep93xx_keypad_resume NULL
|
||||
#endif /* !CONFIG_PM */
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops,
|
||||
ep93xx_keypad_suspend, ep93xx_keypad_resume);
|
||||
|
||||
static int __devinit ep93xx_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
@ -308,19 +303,16 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev)
|
||||
input_dev->open = ep93xx_keypad_open;
|
||||
input_dev->close = ep93xx_keypad_close;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->keycode = keypad->keycodes;
|
||||
input_dev->keycodesize = sizeof(keypad->keycodes[0]);
|
||||
input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
|
||||
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
err = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS,
|
||||
keypad->keycodes, input_dev);
|
||||
if (err)
|
||||
goto failed_free_dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT)
|
||||
input_dev->evbit[0] |= BIT_MASK(EV_REP);
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, 3,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
err = request_irq(keypad->irq, ep93xx_keypad_irq_handler,
|
||||
0, pdev->name, keypad);
|
||||
@ -331,6 +323,7 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto failed_free_irq;
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
return 0;
|
||||
@ -384,11 +377,10 @@ static struct platform_driver ep93xx_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "ep93xx-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ep93xx_keypad_pm_ops,
|
||||
},
|
||||
.probe = ep93xx_keypad_probe,
|
||||
.remove = __devexit_p(ep93xx_keypad_remove),
|
||||
.suspend = ep93xx_keypad_suspend,
|
||||
.resume = ep93xx_keypad_resume,
|
||||
};
|
||||
module_platform_driver(ep93xx_keypad_driver);
|
||||
|
||||
|
@ -583,15 +583,4 @@ static struct serio_driver hil_serio_drv = {
|
||||
.interrupt = hil_dev_interrupt
|
||||
};
|
||||
|
||||
static int __init hil_dev_init(void)
|
||||
{
|
||||
return serio_register_driver(&hil_serio_drv);
|
||||
}
|
||||
|
||||
static void __exit hil_dev_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&hil_serio_drv);
|
||||
}
|
||||
|
||||
module_init(hil_dev_init);
|
||||
module_exit(hil_dev_exit);
|
||||
module_serio_driver(hil_serio_drv);
|
||||
|
@ -481,7 +481,7 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) ||
|
||||
keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {
|
||||
keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) {
|
||||
dev_err(&pdev->dev,
|
||||
"invalid key data (too many rows or colums)\n");
|
||||
error = -EINVAL;
|
||||
@ -496,14 +496,17 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->open = imx_keypad_open;
|
||||
input_dev->close = imx_keypad_close;
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
input_dev->keycode = keypad->keycodes;
|
||||
input_dev->keycodesize = sizeof(keypad->keycodes[0]);
|
||||
input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT,
|
||||
keypad->keycodes, input_dev->keybit);
|
||||
error = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
MAX_MATRIX_KEY_ROWS,
|
||||
MAX_MATRIX_KEY_COLS,
|
||||
keypad->keycodes, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto failed_clock_put;
|
||||
}
|
||||
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
|
@ -731,19 +731,4 @@ static struct serio_driver lkkbd_drv = {
|
||||
.interrupt = lkkbd_interrupt,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for insering/removing us as a module.
|
||||
*/
|
||||
static int __init lkkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&lkkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit lkkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&lkkbd_drv);
|
||||
}
|
||||
|
||||
module_init(lkkbd_init);
|
||||
module_exit(lkkbd_exit);
|
||||
|
||||
module_serio_driver(lkkbd_drv);
|
||||
|
235
drivers/input/keyboard/lm8333.c
Normal file
235
drivers/input/keyboard/lm8333.c
Normal file
@ -0,0 +1,235 @@
|
||||
/*
|
||||
* LM8333 keypad driver
|
||||
* Copyright (C) 2012 Wolfram Sang, Pengutronix <w.sang@pengutronix.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/input/lm8333.h>
|
||||
|
||||
#define LM8333_FIFO_READ 0x20
|
||||
#define LM8333_DEBOUNCE 0x22
|
||||
#define LM8333_READ_INT 0xD0
|
||||
#define LM8333_ACTIVE 0xE4
|
||||
#define LM8333_READ_ERROR 0xF0
|
||||
|
||||
#define LM8333_KEYPAD_IRQ (1 << 0)
|
||||
#define LM8333_ERROR_IRQ (1 << 3)
|
||||
|
||||
#define LM8333_ERROR_KEYOVR 0x04
|
||||
#define LM8333_ERROR_FIFOOVR 0x40
|
||||
|
||||
#define LM8333_FIFO_TRANSFER_SIZE 16
|
||||
|
||||
#define LM8333_NUM_ROWS 8
|
||||
#define LM8333_NUM_COLS 16
|
||||
#define LM8333_ROW_SHIFT 4
|
||||
|
||||
struct lm8333 {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT];
|
||||
};
|
||||
|
||||
/* The accessors try twice because the first access may be needed for wakeup */
|
||||
#define LM8333_READ_RETRIES 2
|
||||
|
||||
int lm8333_read8(struct lm8333 *lm8333, u8 cmd)
|
||||
{
|
||||
int retries = 0, ret;
|
||||
|
||||
do {
|
||||
ret = i2c_smbus_read_byte_data(lm8333->client, cmd);
|
||||
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val)
|
||||
{
|
||||
int retries = 0, ret;
|
||||
|
||||
do {
|
||||
ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val);
|
||||
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf)
|
||||
{
|
||||
int retries = 0, ret;
|
||||
|
||||
do {
|
||||
ret = i2c_smbus_read_i2c_block_data(lm8333->client,
|
||||
cmd, len, buf);
|
||||
} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void lm8333_key_handler(struct lm8333 *lm8333)
|
||||
{
|
||||
struct input_dev *input = lm8333->input;
|
||||
u8 keys[LM8333_FIFO_TRANSFER_SIZE];
|
||||
u8 code, pressed;
|
||||
int i, ret;
|
||||
|
||||
ret = lm8333_read_block(lm8333, LM8333_FIFO_READ,
|
||||
LM8333_FIFO_TRANSFER_SIZE, keys);
|
||||
if (ret != LM8333_FIFO_TRANSFER_SIZE) {
|
||||
dev_err(&lm8333->client->dev,
|
||||
"Error %d while reading FIFO\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; keys[i] && i < LM8333_FIFO_TRANSFER_SIZE; i++) {
|
||||
pressed = keys[i] & 0x80;
|
||||
code = keys[i] & 0x7f;
|
||||
|
||||
input_event(input, EV_MSC, MSC_SCAN, code);
|
||||
input_report_key(input, lm8333->keycodes[code], pressed);
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static irqreturn_t lm8333_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct lm8333 *lm8333 = data;
|
||||
u8 status = lm8333_read8(lm8333, LM8333_READ_INT);
|
||||
|
||||
if (!status)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (status & LM8333_ERROR_IRQ) {
|
||||
u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR);
|
||||
|
||||
if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) {
|
||||
u8 dummy[LM8333_FIFO_TRANSFER_SIZE];
|
||||
|
||||
lm8333_read_block(lm8333, LM8333_FIFO_READ,
|
||||
LM8333_FIFO_TRANSFER_SIZE, dummy);
|
||||
}
|
||||
dev_err(&lm8333->client->dev, "Got error %02x\n", err);
|
||||
}
|
||||
|
||||
if (status & LM8333_KEYPAD_IRQ)
|
||||
lm8333_key_handler(lm8333);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit lm8333_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct lm8333_platform_data *pdata = client->dev.platform_data;
|
||||
struct lm8333 *lm8333;
|
||||
struct input_dev *input;
|
||||
int err, active_time;
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
active_time = pdata->active_time ?: 500;
|
||||
if (active_time / 3 <= pdata->debounce_time / 3) {
|
||||
dev_err(&client->dev, "Active time not big enough!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!lm8333 || !input) {
|
||||
err = -ENOMEM;
|
||||
goto free_mem;
|
||||
}
|
||||
|
||||
lm8333->client = client;
|
||||
lm8333->input = input;
|
||||
|
||||
input->name = client->name;
|
||||
input->dev.parent = &client->dev;
|
||||
input->id.bustype = BUS_I2C;
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
|
||||
err = matrix_keypad_build_keymap(pdata->matrix_data, NULL,
|
||||
LM8333_NUM_ROWS, LM8333_NUM_COLS,
|
||||
lm8333->keycodes, input);
|
||||
if (err)
|
||||
goto free_mem;
|
||||
|
||||
if (pdata->debounce_time) {
|
||||
err = lm8333_write8(lm8333, LM8333_DEBOUNCE,
|
||||
pdata->debounce_time / 3);
|
||||
if (err)
|
||||
dev_warn(&client->dev, "Unable to set debounce time\n");
|
||||
}
|
||||
|
||||
if (pdata->active_time) {
|
||||
err = lm8333_write8(lm8333, LM8333_ACTIVE,
|
||||
pdata->active_time / 3);
|
||||
if (err)
|
||||
dev_warn(&client->dev, "Unable to set active time\n");
|
||||
}
|
||||
|
||||
err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
"lm8333", lm8333);
|
||||
if (err)
|
||||
goto free_mem;
|
||||
|
||||
err = input_register_device(input);
|
||||
if (err)
|
||||
goto free_irq;
|
||||
|
||||
i2c_set_clientdata(client, lm8333);
|
||||
return 0;
|
||||
|
||||
free_irq:
|
||||
free_irq(client->irq, lm8333);
|
||||
free_mem:
|
||||
input_free_device(input);
|
||||
kfree(lm8333);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit lm8333_remove(struct i2c_client *client)
|
||||
{
|
||||
struct lm8333 *lm8333 = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, lm8333);
|
||||
input_unregister_device(lm8333->input);
|
||||
kfree(lm8333);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm8333_id[] = {
|
||||
{ "lm8333", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm8333_id);
|
||||
|
||||
static struct i2c_driver lm8333_driver = {
|
||||
.driver = {
|
||||
.name = "lm8333",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = lm8333_probe,
|
||||
.remove = __devexit_p(lm8333_remove),
|
||||
.id_table = lm8333_id,
|
||||
};
|
||||
module_i2c_driver(lm8333_driver);
|
||||
|
||||
MODULE_AUTHOR("Wolfram Sang <w.sang@pengutronix.de>");
|
||||
MODULE_DESCRIPTION("LM8333 keyboard driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -27,7 +27,6 @@
|
||||
struct matrix_keypad {
|
||||
const struct matrix_keypad_platform_data *pdata;
|
||||
struct input_dev *input_dev;
|
||||
unsigned short *keycodes;
|
||||
unsigned int row_shift;
|
||||
|
||||
DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
|
||||
@ -38,6 +37,8 @@ struct matrix_keypad {
|
||||
bool scan_pending;
|
||||
bool stopped;
|
||||
bool gpio_all_disabled;
|
||||
|
||||
unsigned short keycodes[];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -224,7 +225,7 @@ static void matrix_keypad_stop(struct input_dev *dev)
|
||||
disable_row_irqs(keypad);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
@ -293,16 +294,16 @@ static int matrix_keypad_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
|
||||
matrix_keypad_suspend, matrix_keypad_resume);
|
||||
#endif
|
||||
|
||||
static int __devinit init_matrix_gpio(struct platform_device *pdev,
|
||||
struct matrix_keypad *keypad)
|
||||
static SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops,
|
||||
matrix_keypad_suspend, matrix_keypad_resume);
|
||||
|
||||
static int __devinit matrix_keypad_init_gpio(struct platform_device *pdev,
|
||||
struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i, err = -EINVAL;
|
||||
int i, err;
|
||||
|
||||
/* initialized strobe lines as outputs, activated */
|
||||
for (i = 0; i < pdata->num_col_gpios; i++) {
|
||||
@ -348,8 +349,7 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,
|
||||
"matrix-keypad", keypad);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to acquire interrupt "
|
||||
"for GPIO line %i\n",
|
||||
"Unable to acquire interrupt for GPIO line %i\n",
|
||||
pdata->row_gpios[i]);
|
||||
goto err_free_irqs;
|
||||
}
|
||||
@ -375,14 +375,33 @@ err_free_cols:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i;
|
||||
|
||||
if (pdata->clustered_irq > 0) {
|
||||
free_irq(pdata->clustered_irq, keypad);
|
||||
} else {
|
||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||
gpio_free(pdata->row_gpios[i]);
|
||||
|
||||
for (i = 0; i < pdata->num_col_gpios; i++)
|
||||
gpio_free(pdata->col_gpios[i]);
|
||||
}
|
||||
|
||||
static int __devinit matrix_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata;
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
struct matrix_keypad *keypad;
|
||||
struct input_dev *input_dev;
|
||||
unsigned short *keycodes;
|
||||
unsigned int row_shift;
|
||||
size_t keymap_size;
|
||||
int err;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
@ -398,20 +417,18 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
row_shift = get_count_order(pdata->num_col_gpios);
|
||||
|
||||
keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
|
||||
keycodes = kzalloc((pdata->num_row_gpios << row_shift) *
|
||||
sizeof(*keycodes),
|
||||
GFP_KERNEL);
|
||||
keymap_size = (pdata->num_row_gpios << row_shift) *
|
||||
sizeof(keypad->keycodes[0]);
|
||||
keypad = kzalloc(sizeof(struct matrix_keypad) + keymap_size,
|
||||
GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!keypad || !keycodes || !input_dev) {
|
||||
if (!keypad || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
keypad->input_dev = input_dev;
|
||||
keypad->pdata = pdata;
|
||||
keypad->keycodes = keycodes;
|
||||
keypad->row_shift = row_shift;
|
||||
keypad->stopped = true;
|
||||
INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan);
|
||||
@ -420,38 +437,38 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev)
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
if (!pdata->no_autorepeat)
|
||||
input_dev->evbit[0] |= BIT_MASK(EV_REP);
|
||||
input_dev->open = matrix_keypad_start;
|
||||
input_dev->close = matrix_keypad_stop;
|
||||
|
||||
input_dev->keycode = keycodes;
|
||||
input_dev->keycodesize = sizeof(*keycodes);
|
||||
input_dev->keycodemax = pdata->num_row_gpios << row_shift;
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, row_shift,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
err = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
pdata->num_row_gpios,
|
||||
pdata->num_col_gpios,
|
||||
keypad->keycodes, input_dev);
|
||||
if (err)
|
||||
goto err_free_mem;
|
||||
|
||||
if (!pdata->no_autorepeat)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
err = init_matrix_gpio(pdev, keypad);
|
||||
err = matrix_keypad_init_gpio(pdev, keypad);
|
||||
if (err)
|
||||
goto err_free_mem;
|
||||
|
||||
err = input_register_device(keypad->input_dev);
|
||||
if (err)
|
||||
goto err_free_mem;
|
||||
goto err_free_gpio;
|
||||
|
||||
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_gpio:
|
||||
matrix_keypad_free_gpio(keypad);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(keycodes);
|
||||
kfree(keypad);
|
||||
return err;
|
||||
}
|
||||
@ -459,29 +476,15 @@ err_free_mem:
|
||||
static int __devexit matrix_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
int i;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
|
||||
if (pdata->clustered_irq > 0) {
|
||||
free_irq(pdata->clustered_irq, keypad);
|
||||
} else {
|
||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
|
||||
}
|
||||
|
||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||
gpio_free(pdata->row_gpios[i]);
|
||||
|
||||
for (i = 0; i < pdata->num_col_gpios; i++)
|
||||
gpio_free(pdata->col_gpios[i]);
|
||||
|
||||
matrix_keypad_free_gpio(keypad);
|
||||
input_unregister_device(keypad->input_dev);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(keypad->keycodes);
|
||||
kfree(keypad);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -491,9 +494,7 @@ static struct platform_driver matrix_keypad_driver = {
|
||||
.driver = {
|
||||
.name = "matrix-keypad",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &matrix_keypad_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
module_platform_driver(matrix_keypad_driver);
|
||||
|
@ -166,15 +166,4 @@ static struct serio_driver nkbd_drv = {
|
||||
.disconnect = nkbd_disconnect,
|
||||
};
|
||||
|
||||
static int __init nkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&nkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit nkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&nkbd_drv);
|
||||
}
|
||||
|
||||
module_init(nkbd_init);
|
||||
module_exit(nkbd_exit);
|
||||
module_serio_driver(nkbd_drv);
|
||||
|
@ -39,7 +39,8 @@
|
||||
#define SKE_KPRISA (0x1 << 2)
|
||||
|
||||
#define SKE_KEYPAD_ROW_SHIFT 3
|
||||
#define SKE_KPD_KEYMAP_SIZE (8 * 8)
|
||||
#define SKE_KPD_NUM_ROWS 8
|
||||
#define SKE_KPD_NUM_COLS 8
|
||||
|
||||
/* keypad auto scan registers */
|
||||
#define SKE_ASR0 0x20
|
||||
@ -63,7 +64,7 @@ struct ske_keypad {
|
||||
void __iomem *reg_base;
|
||||
struct input_dev *input;
|
||||
const struct ske_keypad_platform_data *board;
|
||||
unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
|
||||
unsigned short keymap[SKE_KPD_NUM_ROWS * SKE_KPD_NUM_COLS];
|
||||
struct clk *clk;
|
||||
spinlock_t ske_keypad_lock;
|
||||
};
|
||||
@ -261,19 +262,18 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
|
||||
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);
|
||||
error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
|
||||
SKE_KPD_NUM_ROWS, SKE_KPD_NUM_COLS,
|
||||
keypad->keymap, input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Failed to build keymap\n");
|
||||
goto err_iounmap;
|
||||
}
|
||||
|
||||
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 */
|
||||
|
@ -61,6 +61,7 @@ struct omap_kp {
|
||||
unsigned int cols;
|
||||
unsigned long delay;
|
||||
unsigned int debounce;
|
||||
unsigned short keymap[];
|
||||
};
|
||||
|
||||
static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
|
||||
@ -316,13 +317,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
|
||||
if (!cpu_is_omap24xx())
|
||||
omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
|
||||
input_dev->keycode = &omap_kp[1];
|
||||
input_dev->keycodesize = sizeof(unsigned short);
|
||||
input_dev->keycodemax = keycodemax;
|
||||
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
if (pdata->delay)
|
||||
omap_kp->delay = pdata->delay;
|
||||
|
||||
@ -371,9 +365,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
|
||||
goto err2;
|
||||
|
||||
/* setup input device */
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
input_dev->name = "omap-keypad";
|
||||
input_dev->phys = "omap-keypad/input0";
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
@ -383,6 +374,15 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
ret = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
|
||||
pdata->rows, pdata->cols,
|
||||
omap_kp->keymap, input_dev);
|
||||
if (ret < 0)
|
||||
goto err3;
|
||||
|
||||
ret = input_register_device(omap_kp->input);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Unable to register omap-keypad input device\n");
|
||||
|
@ -68,19 +68,52 @@
|
||||
|
||||
#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF
|
||||
|
||||
enum {
|
||||
KBD_REVISION_OMAP4 = 0,
|
||||
KBD_REVISION_OMAP5,
|
||||
};
|
||||
|
||||
struct omap4_keypad {
|
||||
struct input_dev *input;
|
||||
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
unsigned int irq;
|
||||
|
||||
unsigned int rows;
|
||||
unsigned int cols;
|
||||
u32 reg_offset;
|
||||
u32 irqreg_offset;
|
||||
unsigned int row_shift;
|
||||
unsigned char key_state[8];
|
||||
unsigned short keymap[];
|
||||
};
|
||||
|
||||
static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
|
||||
{
|
||||
return __raw_readl(keypad_data->base +
|
||||
keypad_data->reg_offset + offset);
|
||||
}
|
||||
|
||||
static void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
|
||||
{
|
||||
__raw_writel(value,
|
||||
keypad_data->base + keypad_data->reg_offset + offset);
|
||||
}
|
||||
|
||||
static int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
|
||||
{
|
||||
return __raw_readl(keypad_data->base +
|
||||
keypad_data->irqreg_offset + offset);
|
||||
}
|
||||
|
||||
static void kbd_write_irqreg(struct omap4_keypad *keypad_data,
|
||||
u32 offset, u32 value)
|
||||
{
|
||||
__raw_writel(value,
|
||||
keypad_data->base + keypad_data->irqreg_offset + offset);
|
||||
}
|
||||
|
||||
|
||||
/* Interrupt handler */
|
||||
static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
@ -91,12 +124,11 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
|
||||
u32 *new_state = (u32 *) key_state;
|
||||
|
||||
/* Disable interrupts */
|
||||
__raw_writel(OMAP4_VAL_IRQDISABLE,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
|
||||
OMAP4_VAL_IRQDISABLE);
|
||||
|
||||
*new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = __raw_readl(keypad_data->base
|
||||
+ OMAP4_KBD_FULLCODE63_32);
|
||||
*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
|
||||
*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
|
||||
|
||||
for (row = 0; row < keypad_data->rows; row++) {
|
||||
changed = key_state[row] ^ keypad_data->key_state[row];
|
||||
@ -121,12 +153,13 @@ static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
|
||||
sizeof(keypad_data->key_state));
|
||||
|
||||
/* clear pending interrupts */
|
||||
__raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
|
||||
keypad_data->base + OMAP4_KBD_IRQSTATUS);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
|
||||
|
||||
/* enable interrupts */
|
||||
__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
|
||||
OMAP4_DEF_IRQENABLE_EVENTEN |
|
||||
OMAP4_DEF_IRQENABLE_LONGKEY);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -139,16 +172,17 @@ static int omap4_keypad_open(struct input_dev *input)
|
||||
|
||||
disable_irq(keypad_data->irq);
|
||||
|
||||
__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);
|
||||
kbd_writel(keypad_data, OMAP4_KBD_CTRL,
|
||||
OMAP4_VAL_FUNCTIONALCFG);
|
||||
kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
|
||||
OMAP4_VAL_DEBOUNCINGTIME);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
OMAP4_VAL_IRQDISABLE);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
|
||||
OMAP4_DEF_IRQENABLE_EVENTEN |
|
||||
OMAP4_DEF_IRQENABLE_LONGKEY);
|
||||
kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
|
||||
OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
|
||||
|
||||
enable_irq(keypad_data->irq);
|
||||
|
||||
@ -162,12 +196,12 @@ static void omap4_keypad_close(struct input_dev *input)
|
||||
disable_irq(keypad_data->irq);
|
||||
|
||||
/* Disable interrupts */
|
||||
__raw_writel(OMAP4_VAL_IRQDISABLE,
|
||||
keypad_data->base + OMAP4_KBD_IRQENABLE);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
|
||||
OMAP4_VAL_IRQDISABLE);
|
||||
|
||||
/* clear pending interrupts */
|
||||
__raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
|
||||
keypad_data->base + OMAP4_KBD_IRQSTATUS);
|
||||
kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
|
||||
kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
|
||||
|
||||
enable_irq(keypad_data->irq);
|
||||
|
||||
@ -182,6 +216,7 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
||||
struct resource *res;
|
||||
resource_size_t size;
|
||||
unsigned int row_shift, max_keys;
|
||||
int rev;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
@ -241,11 +276,40 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
||||
keypad_data->rows = pdata->rows;
|
||||
keypad_data->cols = pdata->cols;
|
||||
|
||||
/*
|
||||
* Enable clocks for the keypad module so that we can read
|
||||
* revision register.
|
||||
*/
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
error = pm_runtime_get_sync(&pdev->dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
|
||||
goto err_unmap;
|
||||
}
|
||||
rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
|
||||
rev &= 0x03 << 30;
|
||||
rev >>= 30;
|
||||
switch (rev) {
|
||||
case KBD_REVISION_OMAP4:
|
||||
keypad_data->reg_offset = 0x00;
|
||||
keypad_data->irqreg_offset = 0x00;
|
||||
break;
|
||||
case KBD_REVISION_OMAP5:
|
||||
keypad_data->reg_offset = 0x10;
|
||||
keypad_data->irqreg_offset = 0x0c;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev,
|
||||
"Keypad reports unsupported revision %d", rev);
|
||||
error = -EINVAL;
|
||||
goto err_pm_put_sync;
|
||||
}
|
||||
|
||||
/* input device allocation */
|
||||
keypad_data->input = input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_unmap;
|
||||
goto err_pm_put_sync;
|
||||
}
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
@ -258,20 +322,19 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
||||
input_dev->open = omap4_keypad_open;
|
||||
input_dev->close = omap4_keypad_close;
|
||||
|
||||
input_dev->keycode = keypad_data->keymap;
|
||||
input_dev->keycodesize = sizeof(keypad_data->keymap[0]);
|
||||
input_dev->keycodemax = max_keys;
|
||||
error = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
|
||||
pdata->rows, pdata->cols,
|
||||
keypad_data->keymap, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
__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);
|
||||
|
||||
error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"omap4-keypad", keypad_data);
|
||||
@ -280,7 +343,7 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
||||
error = input_register_device(keypad_data->input);
|
||||
if (error < 0) {
|
||||
@ -296,6 +359,8 @@ err_pm_disable:
|
||||
free_irq(keypad_data->irq, keypad_data);
|
||||
err_free_input:
|
||||
input_free_device(input_dev);
|
||||
err_pm_put_sync:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
err_unmap:
|
||||
iounmap(keypad_data->base);
|
||||
err_release_mem:
|
||||
|
@ -626,21 +626,21 @@ static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev)
|
||||
kp->input->id.product = 0x0001;
|
||||
kp->input->id.vendor = 0x0001;
|
||||
|
||||
kp->input->evbit[0] = BIT_MASK(EV_KEY);
|
||||
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, kp->input->evbit);
|
||||
|
||||
kp->input->keycode = kp->keycodes;
|
||||
kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE;
|
||||
kp->input->keycodesize = sizeof(kp->keycodes);
|
||||
kp->input->open = pmic8xxx_kp_open;
|
||||
kp->input->close = pmic8xxx_kp_close;
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT,
|
||||
kp->input->keycode, kp->input->keybit);
|
||||
rc = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS,
|
||||
kp->keycodes, kp->input);
|
||||
if (rc) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto err_get_irq;
|
||||
}
|
||||
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, kp->input->evbit);
|
||||
input_set_capability(kp->input, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_set_drvdata(kp->input, kp);
|
||||
|
||||
/* initialize keypad state */
|
||||
|
@ -454,23 +454,23 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev)
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
input_dev->open = samsung_keypad_open;
|
||||
input_dev->close = samsung_keypad_close;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
if (!pdata->no_autorepeat)
|
||||
input_dev->evbit[0] |= BIT_MASK(EV_REP);
|
||||
error = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
pdata->rows, pdata->cols,
|
||||
keypad->keycodes, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
if (!pdata->no_autorepeat)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
input_dev->keycode = keypad->keycodes;
|
||||
input_dev->keycodesize = sizeof(keypad->keycodes[0]);
|
||||
input_dev->keycodemax = pdata->rows << row_shift;
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, row_shift,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
keypad->irq = platform_get_irq(pdev, 0);
|
||||
if (keypad->irq < 0) {
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeup.h>
|
||||
#include <linux/slab.h>
|
||||
@ -49,7 +50,9 @@
|
||||
#define KEY_VALUE 0x00FFFFFF
|
||||
#define ROW_MASK 0xF0
|
||||
#define COLUMN_MASK 0x0F
|
||||
#define ROW_SHIFT 4
|
||||
#define NUM_ROWS 16
|
||||
#define NUM_COLS 16
|
||||
|
||||
#define KEY_MATRIX_SHIFT 6
|
||||
|
||||
struct spear_kbd {
|
||||
@ -60,7 +63,8 @@ struct spear_kbd {
|
||||
unsigned int irq;
|
||||
unsigned int mode;
|
||||
unsigned short last_key;
|
||||
unsigned short keycodes[256];
|
||||
unsigned short keycodes[NUM_ROWS * NUM_COLS];
|
||||
bool rep;
|
||||
};
|
||||
|
||||
static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
|
||||
@ -136,27 +140,49 @@ static void spear_kbd_close(struct input_dev *dev)
|
||||
kbd->last_key = KEY_RESERVED;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int __devinit spear_kbd_parse_dt(struct platform_device *pdev,
|
||||
struct spear_kbd *kbd)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int error;
|
||||
u32 val;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "Missing DT data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(np, "autorepeat"))
|
||||
kbd->rep = true;
|
||||
|
||||
error = of_property_read_u32(np, "st,mode", &val);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "DT: Invalid or missing mode\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
kbd->mode = val;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int spear_kbd_parse_dt(struct platform_device *pdev,
|
||||
struct spear_kbd *kbd)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit spear_kbd_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct kbd_platform_data *pdata = pdev->dev.platform_data;
|
||||
const struct matrix_keymap_data *keymap;
|
||||
struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL;
|
||||
struct spear_kbd *kbd;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "Invalid platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
keymap = pdata->keymap;
|
||||
if (!keymap) {
|
||||
dev_err(&pdev->dev, "no keymap defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no keyboard resource defined\n");
|
||||
@ -179,7 +205,15 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev)
|
||||
|
||||
kbd->input = input_dev;
|
||||
kbd->irq = irq;
|
||||
kbd->mode = pdata->mode;
|
||||
|
||||
if (!pdata) {
|
||||
error = spear_kbd_parse_dt(pdev, kbd);
|
||||
if (error)
|
||||
goto err_free_mem;
|
||||
} else {
|
||||
kbd->mode = pdata->mode;
|
||||
kbd->rep = pdata->rep;
|
||||
}
|
||||
|
||||
kbd->res = request_mem_region(res->start, resource_size(res),
|
||||
pdev->name);
|
||||
@ -212,18 +246,17 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev)
|
||||
input_dev->open = spear_kbd_open;
|
||||
input_dev->close = spear_kbd_close;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
if (pdata->rep)
|
||||
error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS,
|
||||
kbd->keycodes, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Failed to build keymap\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
if (kbd->rep)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_dev->keycode = kbd->keycodes;
|
||||
input_dev->keycodesize = sizeof(kbd->keycodes[0]);
|
||||
input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes);
|
||||
|
||||
matrix_keypad_build_keymap(keymap, ROW_SHIFT,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
|
||||
input_set_drvdata(input_dev, kbd);
|
||||
|
||||
error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd);
|
||||
@ -317,6 +350,14 @@ static int spear_kbd_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id spear_kbd_id_table[] = {
|
||||
{ .compatible = "st,spear300-kbd" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spear_kbd_id_table);
|
||||
#endif
|
||||
|
||||
static struct platform_driver spear_kbd_driver = {
|
||||
.probe = spear_kbd_probe,
|
||||
.remove = __devexit_p(spear_kbd_remove),
|
||||
@ -324,6 +365,7 @@ static struct platform_driver spear_kbd_driver = {
|
||||
.name = "keyboard",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &spear_kbd_pm_ops,
|
||||
.of_match_table = of_match_ptr(spear_kbd_id_table),
|
||||
},
|
||||
};
|
||||
module_platform_driver(spear_kbd_driver);
|
||||
|
@ -289,19 +289,17 @@ static int __devinit stmpe_keypad_probe(struct platform_device *pdev)
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
ret = matrix_keypad_build_keymap(plat->keymap_data, NULL,
|
||||
STMPE_KEYPAD_MAX_ROWS,
|
||||
STMPE_KEYPAD_MAX_COLS,
|
||||
keypad->keymap, input);
|
||||
if (ret)
|
||||
goto out_freeinput;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
if (!plat->no_autorepeat)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
input->keycode = keypad->keymap;
|
||||
input->keycodesize = sizeof(keypad->keymap[0]);
|
||||
input->keycodemax = ARRAY_SIZE(keypad->keymap);
|
||||
|
||||
matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT,
|
||||
input->keycode, input->keybit);
|
||||
|
||||
for (i = 0; i < plat->keymap_data->keymap_size; i++) {
|
||||
unsigned int key = plat->keymap_data->keymap[i];
|
||||
|
||||
|
@ -170,15 +170,4 @@ static struct serio_driver skbd_drv = {
|
||||
.disconnect = skbd_disconnect,
|
||||
};
|
||||
|
||||
static int __init skbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&skbd_drv);
|
||||
}
|
||||
|
||||
static void __exit skbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&skbd_drv);
|
||||
}
|
||||
|
||||
module_init(skbd_init);
|
||||
module_exit(skbd_exit);
|
||||
module_serio_driver(skbd_drv);
|
||||
|
@ -369,19 +369,4 @@ static struct serio_driver sunkbd_drv = {
|
||||
.disconnect = sunkbd_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for insering/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init sunkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&sunkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit sunkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&sunkbd_drv);
|
||||
}
|
||||
|
||||
module_init(sunkbd_init);
|
||||
module_exit(sunkbd_exit);
|
||||
module_serio_driver(sunkbd_drv);
|
||||
|
@ -78,7 +78,7 @@
|
||||
* @input: pointer to input device object
|
||||
* @board: keypad platform device
|
||||
* @krow: number of rows
|
||||
* @kcol: number of coloumns
|
||||
* @kcol: number of columns
|
||||
* @keymap: matrix scan code table for keycodes
|
||||
* @keypad_stopped: holds keypad status
|
||||
*/
|
||||
@ -96,21 +96,15 @@ static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
|
||||
{
|
||||
int ret;
|
||||
struct tc3589x *tc3589x = keypad->tc3589x;
|
||||
u8 settle_time = keypad->board->settle_time;
|
||||
u8 dbounce_period = keypad->board->debounce_period;
|
||||
u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */
|
||||
u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */
|
||||
const struct tc3589x_keypad_platform_data *board = keypad->board;
|
||||
|
||||
/* validate platform configurations */
|
||||
if (keypad->board->kcol > TC3589x_MAX_KPCOL ||
|
||||
keypad->board->krow > TC3589x_MAX_KPROW ||
|
||||
keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE ||
|
||||
keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE)
|
||||
/* validate platform configuration */
|
||||
if (board->kcol > TC3589x_MAX_KPCOL || board->krow > TC3589x_MAX_KPROW)
|
||||
return -EINVAL;
|
||||
|
||||
/* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,
|
||||
(rows << KP_ROW_SHIFT) | column);
|
||||
(board->krow << KP_ROW_SHIFT) | board->kcol);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -124,12 +118,14 @@ static int tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
|
||||
return ret;
|
||||
|
||||
/* Configure settle time */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time);
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG,
|
||||
board->settle_time);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure debounce time */
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period);
|
||||
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE,
|
||||
board->debounce_period);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@ -337,23 +333,22 @@ static int __devinit tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
input->name = pdev->name;
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input->keycode = keypad->keymap;
|
||||
input->keycodesize = sizeof(keypad->keymap[0]);
|
||||
input->keycodemax = ARRAY_SIZE(keypad->keymap);
|
||||
|
||||
input->open = tc3589x_keypad_open;
|
||||
input->close = tc3589x_keypad_close;
|
||||
|
||||
input_set_drvdata(input, keypad);
|
||||
error = matrix_keypad_build_keymap(plat->keymap_data, NULL,
|
||||
TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL,
|
||||
keypad->keymap, input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Failed to build keymap\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
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, 0x3,
|
||||
input->keycode, input->keybit);
|
||||
input_set_drvdata(input, keypad);
|
||||
|
||||
error = request_threaded_irq(irq, NULL,
|
||||
tc3589x_keypad_irq, plat->irqtype,
|
||||
|
@ -342,21 +342,20 @@ static int __devinit tca8418_keypad_probe(struct i2c_client *client,
|
||||
input->id.product = 0x001;
|
||||
input->id.version = 0x0001;
|
||||
|
||||
input->keycode = keypad_data->keymap;
|
||||
input->keycodesize = sizeof(keypad_data->keymap[0]);
|
||||
input->keycodemax = max_keys;
|
||||
error = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
|
||||
pdata->rows, pdata->cols,
|
||||
keypad_data->keymap, input);
|
||||
if (error) {
|
||||
dev_dbg(&client->dev, "Failed to build keymap\n");
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_set_drvdata(input, keypad_data);
|
||||
|
||||
matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
|
||||
input->keycode, input->keybit);
|
||||
|
||||
if (pdata->irq_is_gpio)
|
||||
client->irq = gpio_to_irq(client->irq);
|
||||
|
||||
|
@ -619,8 +619,8 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct tegra_kbc_platform_data * __devinit
|
||||
tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
|
||||
static struct tegra_kbc_platform_data * __devinit tegra_kbc_dt_parse_pdata(
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_kbc_platform_data *pdata;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
@ -660,10 +660,6 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
|
||||
pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL;
|
||||
}
|
||||
|
||||
pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap");
|
||||
|
||||
/* FIXME: Add handling of linux,fn-keymap here */
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
@ -674,10 +670,36 @@ static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata(
|
||||
}
|
||||
#endif
|
||||
|
||||
static int __devinit tegra_kbd_setup_keymap(struct tegra_kbc *kbc)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = kbc->pdata;
|
||||
const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
|
||||
unsigned int keymap_rows = KBC_MAX_KEY;
|
||||
int retval;
|
||||
|
||||
if (keymap_data && pdata->use_fn_map)
|
||||
keymap_rows *= 2;
|
||||
|
||||
retval = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
keymap_rows, KBC_MAX_COL,
|
||||
kbc->keycode, kbc->idev);
|
||||
if (retval == -ENOSYS || retval == -ENOENT) {
|
||||
/*
|
||||
* If there is no OF support in kernel or keymap
|
||||
* property is missing, use default keymap.
|
||||
*/
|
||||
retval = matrix_keypad_build_keymap(
|
||||
&tegra_kbc_default_keymap_data, NULL,
|
||||
keymap_rows, KBC_MAX_COL,
|
||||
kbc->keycode, kbc->idev);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int __devinit tegra_kbc_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data;
|
||||
const struct matrix_keymap_data *keymap_data;
|
||||
struct tegra_kbc *kbc;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
@ -757,29 +779,26 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
|
||||
kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt;
|
||||
kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS);
|
||||
|
||||
kbc->wakeup_key = pdata->wakeup_key;
|
||||
kbc->use_fn_map = pdata->use_fn_map;
|
||||
kbc->use_ghost_filter = pdata->use_ghost_filter;
|
||||
|
||||
input_dev->name = pdev->name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->open = tegra_kbc_open;
|
||||
input_dev->close = tegra_kbc_close;
|
||||
|
||||
input_set_drvdata(input_dev, kbc);
|
||||
err = tegra_kbd_setup_keymap(kbc);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to setup keymap\n");
|
||||
goto err_put_clk;
|
||||
}
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_dev->keycode = kbc->keycode;
|
||||
input_dev->keycodesize = sizeof(kbc->keycode[0]);
|
||||
input_dev->keycodemax = KBC_MAX_KEY;
|
||||
if (pdata->use_fn_map)
|
||||
input_dev->keycodemax *= 2;
|
||||
|
||||
kbc->use_fn_map = pdata->use_fn_map;
|
||||
kbc->use_ghost_filter = pdata->use_ghost_filter;
|
||||
keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data;
|
||||
matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
kbc->wakeup_key = pdata->wakeup_key;
|
||||
input_set_drvdata(input_dev, kbc);
|
||||
|
||||
err = request_irq(kbc->irq, tegra_kbc_isr,
|
||||
IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc);
|
||||
@ -799,9 +818,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, kbc);
|
||||
device_init_wakeup(&pdev->dev, pdata->wakeup);
|
||||
|
||||
if (!pdev->dev.platform_data)
|
||||
matrix_keyboard_of_free_keymap(pdata->keymap_data);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
@ -816,10 +832,8 @@ err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(kbc);
|
||||
err_free_pdata:
|
||||
if (!pdev->dev.platform_data) {
|
||||
matrix_keyboard_of_free_keymap(pdata->keymap_data);
|
||||
if (!pdev->dev.platform_data)
|
||||
kfree(pdata);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -247,15 +247,11 @@ static int __devinit keypad_probe(struct platform_device *pdev)
|
||||
error = -ENOMEM;
|
||||
goto error_input;
|
||||
}
|
||||
input_set_drvdata(kp->input_dev, kp);
|
||||
|
||||
kp->input_dev->name = pdev->name;
|
||||
kp->input_dev->dev.parent = &pdev->dev;
|
||||
kp->input_dev->open = keypad_start;
|
||||
kp->input_dev->close = keypad_stop;
|
||||
kp->input_dev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
if (!pdata->no_autorepeat)
|
||||
kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
|
||||
|
||||
clk_enable(kp->clk);
|
||||
rev = keypad_read(kp, rev);
|
||||
@ -264,15 +260,20 @@ static int __devinit keypad_probe(struct platform_device *pdev)
|
||||
kp->input_dev->id.version = ((rev >> 16) & 0xfff);
|
||||
clk_disable(kp->clk);
|
||||
|
||||
kp->input_dev->keycode = kp->keycodes;
|
||||
kp->input_dev->keycodesize = sizeof(kp->keycodes[0]);
|
||||
kp->input_dev->keycodemax = kp->rows << kp->row_shift;
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes,
|
||||
kp->input_dev->keybit);
|
||||
error = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
kp->rows, kp->cols,
|
||||
kp->keycodes, kp->input_dev);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to build keymap\n");
|
||||
goto error_reg;
|
||||
}
|
||||
|
||||
if (!pdata->no_autorepeat)
|
||||
kp->input_dev->evbit[0] |= BIT_MASK(EV_REP);
|
||||
input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
input_set_drvdata(kp->input_dev, kp);
|
||||
|
||||
error = input_register_device(kp->input_dev);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "Could not register input device\n");
|
||||
|
@ -361,14 +361,6 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
|
||||
kp->irq = platform_get_irq(pdev, 0);
|
||||
|
||||
/* setup input device */
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
|
||||
/* Enable auto repeat feature of Linux input subsystem */
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
|
||||
input->name = "TWL4030 Keypad";
|
||||
input->phys = "twl4030_keypad/input0";
|
||||
input->dev.parent = &pdev->dev;
|
||||
@ -378,12 +370,19 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev)
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = 0x0003;
|
||||
|
||||
input->keycode = kp->keymap;
|
||||
input->keycodesize = sizeof(kp->keymap[0]);
|
||||
input->keycodemax = ARRAY_SIZE(kp->keymap);
|
||||
error = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
TWL4030_MAX_ROWS,
|
||||
1 << TWL4030_ROW_SHIFT,
|
||||
kp->keymap, input);
|
||||
if (error) {
|
||||
dev_err(kp->dbg_dev, "Failed to build keymap\n");
|
||||
goto err1;
|
||||
}
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT,
|
||||
input->keycode, input->keybit);
|
||||
input_set_capability(input, EV_MSC, MSC_SCAN);
|
||||
/* Enable auto repeat feature of Linux input subsystem */
|
||||
if (pdata->rep)
|
||||
__set_bit(EV_REP, input->evbit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
|
@ -42,7 +42,8 @@
|
||||
#define KGET_RAW(n) (((n) & KEY0R) >> 3)
|
||||
#define KGET_COLUMN(n) ((n) & KEY0C)
|
||||
|
||||
#define W90P910_MAX_KEY_NUM (8 * 8)
|
||||
#define W90P910_NUM_ROWS 8
|
||||
#define W90P910_NUM_COLS 8
|
||||
#define W90P910_ROW_SHIFT 3
|
||||
|
||||
struct w90p910_keypad {
|
||||
@ -51,7 +52,7 @@ struct w90p910_keypad {
|
||||
struct input_dev *input_dev;
|
||||
void __iomem *mmio_base;
|
||||
int irq;
|
||||
unsigned short keymap[W90P910_MAX_KEY_NUM];
|
||||
unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS];
|
||||
};
|
||||
|
||||
static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad,
|
||||
@ -190,17 +191,13 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev)
|
||||
input_dev->close = w90p910_keypad_close;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
|
||||
input_dev->keycode = keypad->keymap;
|
||||
input_dev->keycodesize = sizeof(keypad->keymap[0]);
|
||||
input_dev->keycodemax = ARRAY_SIZE(keypad->keymap);
|
||||
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
|
||||
matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT,
|
||||
input_dev->keycode, input_dev->keybit);
|
||||
error = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
W90P910_NUM_ROWS, W90P910_NUM_COLS,
|
||||
keypad->keymap, input_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "failed to build keymap\n");
|
||||
goto failed_put_clk;
|
||||
}
|
||||
|
||||
error = request_irq(keypad->irq, w90p910_keypad_irq_handler,
|
||||
0, pdev->name, keypad);
|
||||
@ -209,6 +206,10 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev)
|
||||
goto failed_put_clk;
|
||||
}
|
||||
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
/* Register the input device */
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
|
@ -169,15 +169,4 @@ static struct serio_driver xtkbd_drv = {
|
||||
.disconnect = xtkbd_disconnect,
|
||||
};
|
||||
|
||||
static int __init xtkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&xtkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit xtkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&xtkbd_drv);
|
||||
}
|
||||
|
||||
module_init(xtkbd_init);
|
||||
module_exit(xtkbd_exit);
|
||||
module_serio_driver(xtkbd_drv);
|
||||
|
163
drivers/input/matrix-keymap.c
Normal file
163
drivers/input/matrix-keymap.c
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Helpers for matrix keyboard bindings
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc
|
||||
*
|
||||
* Author:
|
||||
* Olof Johansson <olof@lixom.net>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
|
||||
static bool matrix_keypad_map_key(struct input_dev *input_dev,
|
||||
unsigned int rows, unsigned int cols,
|
||||
unsigned int row_shift, unsigned int key)
|
||||
{
|
||||
unsigned short *keymap = input_dev->keycode;
|
||||
unsigned int row = KEY_ROW(key);
|
||||
unsigned int col = KEY_COL(key);
|
||||
unsigned short code = KEY_VAL(key);
|
||||
|
||||
if (row >= rows || col >= cols) {
|
||||
dev_err(input_dev->dev.parent,
|
||||
"%s: invalid keymap entry 0x%x (row: %d, col: %d, rows: %d, cols: %d)\n",
|
||||
__func__, key, row, col, rows, cols);
|
||||
return false;
|
||||
}
|
||||
|
||||
keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
|
||||
__set_bit(code, input_dev->keybit);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static int matrix_keypad_parse_of_keymap(const char *propname,
|
||||
unsigned int rows, unsigned int cols,
|
||||
struct input_dev *input_dev)
|
||||
{
|
||||
struct device *dev = input_dev->dev.parent;
|
||||
struct device_node *np = dev->of_node;
|
||||
unsigned int row_shift = get_count_order(cols);
|
||||
unsigned int max_keys = rows << row_shift;
|
||||
unsigned int proplen, i, size;
|
||||
const __be32 *prop;
|
||||
|
||||
if (!np)
|
||||
return -ENOENT;
|
||||
|
||||
if (!propname)
|
||||
propname = "linux,keymap";
|
||||
|
||||
prop = of_get_property(np, propname, &proplen);
|
||||
if (!prop) {
|
||||
dev_err(dev, "OF: %s property not defined in %s\n",
|
||||
propname, np->full_name);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (proplen % sizeof(u32)) {
|
||||
dev_err(dev, "OF: Malformed keycode property %s in %s\n",
|
||||
propname, np->full_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = proplen / sizeof(u32);
|
||||
if (size > max_keys) {
|
||||
dev_err(dev, "OF: %s size overflow\n", propname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
unsigned int key = be32_to_cpup(prop + i);
|
||||
|
||||
if (!matrix_keypad_map_key(input_dev, rows, cols,
|
||||
row_shift, key))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int matrix_keypad_parse_of_keymap(const char *propname,
|
||||
unsigned int rows, unsigned int cols,
|
||||
struct input_dev *input_dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* matrix_keypad_build_keymap - convert platform keymap into matrix keymap
|
||||
* @keymap_data: keymap supplied by the platform code
|
||||
* @keymap_name: name of device tree property containing keymap (if device
|
||||
* tree support is enabled).
|
||||
* @rows: number of rows in target keymap array
|
||||
* @cols: number of cols in target keymap array
|
||||
* @keymap: expanded version of keymap that is suitable for use by
|
||||
* matrix keyboard driver
|
||||
* @input_dev: input devices for which we are setting up the keymap
|
||||
*
|
||||
* This function converts platform keymap (encoded with KEY() macro) into
|
||||
* an array of keycodes that is suitable for using in a standard matrix
|
||||
* keyboard driver that uses row and col as indices.
|
||||
*
|
||||
* If @keymap_data is not supplied and device tree support is enabled
|
||||
* it will attempt load the keymap from property specified by @keymap_name
|
||||
* argument (or "linux,keymap" if @keymap_name is %NULL).
|
||||
*
|
||||
* Callers are expected to set up input_dev->dev.parent before calling this
|
||||
* function.
|
||||
*/
|
||||
int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
|
||||
const char *keymap_name,
|
||||
unsigned int rows, unsigned int cols,
|
||||
unsigned short *keymap,
|
||||
struct input_dev *input_dev)
|
||||
{
|
||||
unsigned int row_shift = get_count_order(cols);
|
||||
int i;
|
||||
int error;
|
||||
|
||||
input_dev->keycode = keymap;
|
||||
input_dev->keycodesize = sizeof(*keymap);
|
||||
input_dev->keycodemax = rows << row_shift;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
|
||||
if (keymap_data) {
|
||||
for (i = 0; i < keymap_data->keymap_size; i++) {
|
||||
unsigned int key = keymap_data->keymap[i];
|
||||
|
||||
if (!matrix_keypad_map_key(input_dev, rows, cols,
|
||||
row_shift, key))
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols,
|
||||
input_dev);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
__clear_bit(KEY_RESERVED, input_dev->keybit);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(matrix_keypad_build_keymap);
|
@ -318,7 +318,7 @@ struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
|
||||
mutex_init(&data->mutex);
|
||||
|
||||
data->mode = pdata->mode;
|
||||
if (data->mode < CMAMODE_DEFAULT || data->mode > CMAMODE_POFF) {
|
||||
if (data->mode > CMAMODE_POFF) {
|
||||
data->mode = CMAMODE_MOTDET;
|
||||
dev_warn(dev,
|
||||
"Invalid mode specified, assuming Motion Detect\n");
|
||||
|
@ -367,7 +367,7 @@ static int __devinit mpu3050_probe(struct i2c_client *client,
|
||||
|
||||
error = request_threaded_irq(client->irq,
|
||||
NULL, mpu3050_interrupt_thread,
|
||||
IRQF_TRIGGER_RISING,
|
||||
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
"mpu3050", sensor);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
|
@ -27,6 +27,7 @@
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/mfd/twl6040.h>
|
||||
@ -258,10 +259,13 @@ static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
|
||||
static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl6040_vibra_data *pdata = pdev->dev.platform_data;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct vibra_info *info;
|
||||
int vddvibl_uV = 0;
|
||||
int vddvibr_uV = 0;
|
||||
int ret;
|
||||
|
||||
if (!pdata) {
|
||||
if (!pdata && !node) {
|
||||
dev_err(&pdev->dev, "platform_data not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -273,11 +277,26 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
info->twl6040 = dev_get_drvdata(pdev->dev.parent);
|
||||
info->vibldrv_res = pdata->vibldrv_res;
|
||||
info->vibrdrv_res = pdata->vibrdrv_res;
|
||||
info->viblmotor_res = pdata->viblmotor_res;
|
||||
info->vibrmotor_res = pdata->vibrmotor_res;
|
||||
if (pdata) {
|
||||
info->vibldrv_res = pdata->vibldrv_res;
|
||||
info->vibrdrv_res = pdata->vibrdrv_res;
|
||||
info->viblmotor_res = pdata->viblmotor_res;
|
||||
info->vibrmotor_res = pdata->vibrmotor_res;
|
||||
vddvibl_uV = pdata->vddvibl_uV;
|
||||
vddvibr_uV = pdata->vddvibr_uV;
|
||||
} else {
|
||||
of_property_read_u32(node, "vibldrv_res", &info->vibldrv_res);
|
||||
of_property_read_u32(node, "vibrdrv_res", &info->vibrdrv_res);
|
||||
of_property_read_u32(node, "viblmotor_res",
|
||||
&info->viblmotor_res);
|
||||
of_property_read_u32(node, "vibrmotor_res",
|
||||
&info->vibrmotor_res);
|
||||
of_property_read_u32(node, "vddvibl_uV", &vddvibl_uV);
|
||||
of_property_read_u32(node, "vddvibr_uV", &vddvibr_uV);
|
||||
}
|
||||
|
||||
if ((!info->vibldrv_res && !info->viblmotor_res) ||
|
||||
(!info->vibrdrv_res && !info->vibrmotor_res)) {
|
||||
dev_err(info->dev, "invalid vibra driver/motor resistance\n");
|
||||
@ -339,10 +358,9 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
|
||||
goto err_regulator;
|
||||
}
|
||||
|
||||
if (pdata->vddvibl_uV) {
|
||||
if (vddvibl_uV) {
|
||||
ret = regulator_set_voltage(info->supplies[0].consumer,
|
||||
pdata->vddvibl_uV,
|
||||
pdata->vddvibl_uV);
|
||||
vddvibl_uV, vddvibl_uV);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
|
||||
ret);
|
||||
@ -350,10 +368,9 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->vddvibr_uV) {
|
||||
if (vddvibr_uV) {
|
||||
ret = regulator_set_voltage(info->supplies[1].consumer,
|
||||
pdata->vddvibr_uV,
|
||||
pdata->vddvibr_uV);
|
||||
vddvibr_uV, vddvibr_uV);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
|
||||
ret);
|
||||
@ -401,6 +418,12 @@ static int __devexit twl6040_vibra_remove(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id twl6040_vibra_of_match[] = {
|
||||
{.compatible = "ti,twl6040-vibra", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, twl6040_vibra_of_match);
|
||||
|
||||
static struct platform_driver twl6040_vibra_driver = {
|
||||
.probe = twl6040_vibra_probe,
|
||||
.remove = __devexit_p(twl6040_vibra_remove),
|
||||
@ -408,6 +431,7 @@ static struct platform_driver twl6040_vibra_driver = {
|
||||
.name = "twl6040-vibra",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &twl6040_vibra_pm_ops,
|
||||
.of_match_table = twl6040_vibra_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(twl6040_vibra_driver);
|
||||
|
@ -339,4 +339,16 @@ config MOUSE_SYNAPTICS_USB
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called synaptics_usb.
|
||||
|
||||
config MOUSE_NAVPOINT_PXA27x
|
||||
tristate "Synaptics NavPoint (PXA27x SSP/SPI)"
|
||||
depends on PXA27x && PXA_SSP
|
||||
help
|
||||
This driver adds support for the Synaptics NavPoint touchpad connected
|
||||
to a PXA27x SSP port in SPI slave mode. The device emulates a mouse;
|
||||
a tap or tap-and-a-half drag gesture emulates the left mouse button.
|
||||
For example, use the xf86-input-evdev driver for an X pointing device.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called navpoint.
|
||||
|
||||
endif
|
||||
|
@ -12,6 +12,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
|
||||
obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o
|
||||
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
||||
obj-$(CONFIG_MOUSE_PS2) += psmouse.o
|
||||
obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
|
||||
|
@ -553,10 +553,7 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse)
|
||||
|
||||
alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
|
||||
|
||||
input_report_key(dev, BTN_TOOL_FINGER, fingers == 1);
|
||||
input_report_key(dev, BTN_TOOL_DOUBLETAP, fingers == 2);
|
||||
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
|
||||
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
@ -604,10 +601,54 @@ static void alps_process_packet_v3(struct psmouse *psmouse)
|
||||
|
||||
static void alps_process_packet_v4(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
int offset;
|
||||
int x, y, z;
|
||||
int left, right;
|
||||
int x1, y1, x2, y2;
|
||||
int fingers = 0;
|
||||
unsigned int x_bitmap, y_bitmap;
|
||||
|
||||
/*
|
||||
* v4 has a 6-byte encoding for bitmap data, but this data is
|
||||
* broken up between 3 normal packets. Use priv->multi_packet to
|
||||
* track our position in the bitmap packet.
|
||||
*/
|
||||
if (packet[6] & 0x40) {
|
||||
/* sync, reset position */
|
||||
priv->multi_packet = 0;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(priv->multi_packet > 2))
|
||||
return;
|
||||
|
||||
offset = 2 * priv->multi_packet;
|
||||
priv->multi_data[offset] = packet[6];
|
||||
priv->multi_data[offset + 1] = packet[7];
|
||||
|
||||
if (++priv->multi_packet > 2) {
|
||||
priv->multi_packet = 0;
|
||||
|
||||
x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) |
|
||||
((priv->multi_data[3] & 0x60) << 3) |
|
||||
((priv->multi_data[0] & 0x3f) << 2) |
|
||||
((priv->multi_data[1] & 0x60) >> 5);
|
||||
y_bitmap = ((priv->multi_data[5] & 0x01) << 10) |
|
||||
((priv->multi_data[3] & 0x1f) << 5) |
|
||||
(priv->multi_data[1] & 0x1f);
|
||||
|
||||
fingers = alps_process_bitmap(x_bitmap, y_bitmap,
|
||||
&x1, &y1, &x2, &y2);
|
||||
|
||||
/* Store MT data.*/
|
||||
priv->fingers = fingers;
|
||||
priv->x1 = x1;
|
||||
priv->x2 = x2;
|
||||
priv->y1 = y1;
|
||||
priv->y2 = y2;
|
||||
}
|
||||
|
||||
left = packet[4] & 0x01;
|
||||
right = packet[4] & 0x02;
|
||||
@ -617,21 +658,41 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
|
||||
y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
|
||||
z = packet[5] & 0x7f;
|
||||
|
||||
/*
|
||||
* If there were no contacts in the bitmap, use ST
|
||||
* points in MT reports.
|
||||
* If there were two contacts or more, report MT data.
|
||||
*/
|
||||
if (priv->fingers < 2) {
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
fingers = z > 0 ? 1 : 0;
|
||||
} else {
|
||||
fingers = priv->fingers;
|
||||
x1 = priv->x1;
|
||||
x2 = priv->x2;
|
||||
y1 = priv->y1;
|
||||
y2 = priv->y2;
|
||||
}
|
||||
|
||||
if (z >= 64)
|
||||
input_report_key(dev, BTN_TOUCH, 1);
|
||||
else
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
|
||||
alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
|
||||
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
|
||||
if (z > 0) {
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
}
|
||||
input_report_abs(dev, ABS_PRESSURE, z);
|
||||
|
||||
input_report_key(dev, BTN_TOOL_FINGER, z > 0);
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
@ -1557,6 +1618,7 @@ int alps_init(struct psmouse *psmouse)
|
||||
input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0);
|
||||
break;
|
||||
case ALPS_PROTO_V3:
|
||||
case ALPS_PROTO_V4:
|
||||
set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
|
||||
input_mt_init_slots(dev1, 2);
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
|
||||
@ -1565,8 +1627,7 @@ int alps_init(struct psmouse *psmouse)
|
||||
set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
|
||||
set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
|
||||
set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
|
||||
/* fall through */
|
||||
case ALPS_PROTO_V4:
|
||||
|
||||
input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0);
|
||||
break;
|
||||
|
@ -39,6 +39,8 @@ struct alps_data {
|
||||
int prev_fin; /* Finger bit from previous packet */
|
||||
int multi_packet; /* Multi-packet data in progress */
|
||||
unsigned char multi_data[6]; /* Saved multi-packet data */
|
||||
int x1, x2, y1, y2; /* Coordinates from last MT report */
|
||||
int fingers; /* Number of fingers from MT report */
|
||||
u8 quirks;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
369
drivers/input/mouse/navpoint.c
Normal file
369
drivers/input/mouse/navpoint.c
Normal file
@ -0,0 +1,369 @@
|
||||
/*
|
||||
* Synaptics NavPoint (PXA27x SSP/SPI) driver.
|
||||
*
|
||||
* Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com>
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/navpoint.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pxa2xx_ssp.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Synaptics Modular Embedded Protocol: Module Packet Format.
|
||||
* Module header byte 2:0 = Length (# bytes that follow)
|
||||
* Module header byte 4:3 = Control
|
||||
* Module header byte 7:5 = Module Address
|
||||
*/
|
||||
#define HEADER_LENGTH(byte) ((byte) & 0x07)
|
||||
#define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03)
|
||||
#define HEADER_ADDRESS(byte) ((byte) >> 5)
|
||||
|
||||
struct navpoint {
|
||||
struct ssp_device *ssp;
|
||||
struct input_dev *input;
|
||||
struct device *dev;
|
||||
int gpio;
|
||||
int index;
|
||||
u8 data[1 + HEADER_LENGTH(0xff)];
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialization values for SSCR0_x, SSCR1_x, SSSR_x.
|
||||
*/
|
||||
static const u32 sscr0 = 0
|
||||
| SSCR0_TUM /* TIM = 1; No TUR interrupts */
|
||||
| SSCR0_RIM /* RIM = 1; No ROR interrupts */
|
||||
| SSCR0_SSE /* SSE = 1; SSP enabled */
|
||||
| SSCR0_Motorola /* FRF = 0; Motorola SPI */
|
||||
| SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */
|
||||
;
|
||||
static const u32 sscr1 = 0
|
||||
| SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */
|
||||
| SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */
|
||||
| SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */
|
||||
| SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */
|
||||
| SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */
|
||||
| SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */
|
||||
| SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */
|
||||
;
|
||||
static const u32 sssr = 0
|
||||
| SSSR_BCE /* BCE = 1; Clear BCE */
|
||||
| SSSR_TUR /* TUR = 1; Clear TUR */
|
||||
| SSSR_EOC /* EOC = 1; Clear EOC */
|
||||
| SSSR_TINT /* TINT = 1; Clear TINT */
|
||||
| SSSR_PINT /* PINT = 1; Clear PINT */
|
||||
| SSSR_ROR /* ROR = 1; Clear ROR */
|
||||
;
|
||||
|
||||
/*
|
||||
* MEP Query $22: Touchpad Coordinate Range Query is not supported by
|
||||
* the NavPoint module, so sampled values provide the default limits.
|
||||
*/
|
||||
#define NAVPOINT_X_MIN 1278
|
||||
#define NAVPOINT_X_MAX 5340
|
||||
#define NAVPOINT_Y_MIN 1572
|
||||
#define NAVPOINT_Y_MAX 4396
|
||||
#define NAVPOINT_PRESSURE_MIN 0
|
||||
#define NAVPOINT_PRESSURE_MAX 255
|
||||
|
||||
static void navpoint_packet(struct navpoint *navpoint)
|
||||
{
|
||||
int finger;
|
||||
int gesture;
|
||||
int x, y, z;
|
||||
|
||||
switch (navpoint->data[0]) {
|
||||
case 0xff: /* Garbage (packet?) between reset and Hello packet */
|
||||
case 0x00: /* Module 0, NULL packet */
|
||||
break;
|
||||
|
||||
case 0x0e: /* Module 0, Absolute packet */
|
||||
finger = (navpoint->data[1] & 0x01);
|
||||
gesture = (navpoint->data[1] & 0x02);
|
||||
x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3];
|
||||
y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5];
|
||||
z = navpoint->data[6];
|
||||
input_report_key(navpoint->input, BTN_TOUCH, finger);
|
||||
input_report_abs(navpoint->input, ABS_X, x);
|
||||
input_report_abs(navpoint->input, ABS_Y, y);
|
||||
input_report_abs(navpoint->input, ABS_PRESSURE, z);
|
||||
input_report_key(navpoint->input, BTN_TOOL_FINGER, finger);
|
||||
input_report_key(navpoint->input, BTN_LEFT, gesture);
|
||||
input_sync(navpoint->input);
|
||||
break;
|
||||
|
||||
case 0x19: /* Module 0, Hello packet */
|
||||
if ((navpoint->data[1] & 0xf0) == 0x10)
|
||||
break;
|
||||
/* FALLTHROUGH */
|
||||
default:
|
||||
dev_warn(navpoint->dev,
|
||||
"spurious packet: data=0x%02x,0x%02x,...\n",
|
||||
navpoint->data[0], navpoint->data[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t navpoint_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct navpoint *navpoint = dev_id;
|
||||
struct ssp_device *ssp = navpoint->ssp;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
u32 status;
|
||||
|
||||
status = pxa_ssp_read_reg(ssp, SSSR);
|
||||
if (status & sssr) {
|
||||
dev_warn(navpoint->dev,
|
||||
"unexpected interrupt: status=0x%08x\n", status);
|
||||
pxa_ssp_write_reg(ssp, SSSR, (status & sssr));
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
while (status & SSSR_RNE) {
|
||||
u32 data;
|
||||
|
||||
data = pxa_ssp_read_reg(ssp, SSDR);
|
||||
navpoint->data[navpoint->index + 0] = (data >> 8);
|
||||
navpoint->data[navpoint->index + 1] = data;
|
||||
navpoint->index += 2;
|
||||
if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) {
|
||||
navpoint_packet(navpoint);
|
||||
navpoint->index = 0;
|
||||
}
|
||||
status = pxa_ssp_read_reg(ssp, SSSR);
|
||||
ret = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void navpoint_up(struct navpoint *navpoint)
|
||||
{
|
||||
struct ssp_device *ssp = navpoint->ssp;
|
||||
int timeout;
|
||||
|
||||
clk_prepare_enable(ssp->clk);
|
||||
|
||||
pxa_ssp_write_reg(ssp, SSCR1, sscr1);
|
||||
pxa_ssp_write_reg(ssp, SSSR, sssr);
|
||||
pxa_ssp_write_reg(ssp, SSTO, 0);
|
||||
pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */
|
||||
|
||||
/* Wait until SSP port is ready for slave clock operations */
|
||||
for (timeout = 100; timeout != 0; --timeout) {
|
||||
if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS))
|
||||
break;
|
||||
msleep(1);
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
dev_err(navpoint->dev,
|
||||
"timeout waiting for SSSR[CSS] to clear\n");
|
||||
|
||||
if (gpio_is_valid(navpoint->gpio))
|
||||
gpio_set_value(navpoint->gpio, 1);
|
||||
}
|
||||
|
||||
static void navpoint_down(struct navpoint *navpoint)
|
||||
{
|
||||
struct ssp_device *ssp = navpoint->ssp;
|
||||
|
||||
if (gpio_is_valid(navpoint->gpio))
|
||||
gpio_set_value(navpoint->gpio, 0);
|
||||
|
||||
pxa_ssp_write_reg(ssp, SSCR0, 0);
|
||||
|
||||
clk_disable_unprepare(ssp->clk);
|
||||
}
|
||||
|
||||
static int navpoint_open(struct input_dev *input)
|
||||
{
|
||||
struct navpoint *navpoint = input_get_drvdata(input);
|
||||
|
||||
navpoint_up(navpoint);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void navpoint_close(struct input_dev *input)
|
||||
{
|
||||
struct navpoint *navpoint = input_get_drvdata(input);
|
||||
|
||||
navpoint_down(navpoint);
|
||||
}
|
||||
|
||||
static int __devinit navpoint_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct navpoint_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct ssp_device *ssp;
|
||||
struct input_dev *input;
|
||||
struct navpoint *navpoint;
|
||||
int error;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "no platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_is_valid(pdata->gpio)) {
|
||||
error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW,
|
||||
"SYNAPTICS_ON");
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
ssp = pxa_ssp_request(pdata->port, pdev->name);
|
||||
if (!ssp) {
|
||||
error = -ENODEV;
|
||||
goto err_free_gpio;
|
||||
}
|
||||
|
||||
/* HaRET does not disable devices before jumping into Linux */
|
||||
if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) {
|
||||
pxa_ssp_write_reg(ssp, SSCR0, 0);
|
||||
dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port);
|
||||
}
|
||||
|
||||
navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!navpoint || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
navpoint->ssp = ssp;
|
||||
navpoint->input = input;
|
||||
navpoint->dev = &pdev->dev;
|
||||
navpoint->gpio = pdata->gpio;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
|
||||
input_set_abs_params(input, ABS_X,
|
||||
NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y,
|
||||
NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE,
|
||||
NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX,
|
||||
0, 0);
|
||||
|
||||
input->open = navpoint_open;
|
||||
input->close = navpoint_close;
|
||||
|
||||
input_set_drvdata(input, navpoint);
|
||||
|
||||
error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint);
|
||||
if (error)
|
||||
goto err_free_mem;
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err_free_irq;
|
||||
|
||||
platform_set_drvdata(pdev, navpoint);
|
||||
dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(ssp->irq, &pdev->dev);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(navpoint);
|
||||
pxa_ssp_free(ssp);
|
||||
err_free_gpio:
|
||||
if (gpio_is_valid(pdata->gpio))
|
||||
gpio_free(pdata->gpio);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit navpoint_remove(struct platform_device *pdev)
|
||||
{
|
||||
const struct navpoint_platform_data *pdata =
|
||||
dev_get_platdata(&pdev->dev);
|
||||
struct navpoint *navpoint = platform_get_drvdata(pdev);
|
||||
struct ssp_device *ssp = navpoint->ssp;
|
||||
|
||||
free_irq(ssp->irq, navpoint);
|
||||
|
||||
input_unregister_device(navpoint->input);
|
||||
kfree(navpoint);
|
||||
|
||||
pxa_ssp_free(ssp);
|
||||
|
||||
if (gpio_is_valid(pdata->gpio))
|
||||
gpio_free(pdata->gpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int navpoint_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct navpoint *navpoint = platform_get_drvdata(pdev);
|
||||
struct input_dev *input = navpoint->input;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
if (input->users)
|
||||
navpoint_down(navpoint);
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int navpoint_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct navpoint *navpoint = platform_get_drvdata(pdev);
|
||||
struct input_dev *input = navpoint->input;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
if (input->users)
|
||||
navpoint_up(navpoint);
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume);
|
||||
|
||||
static struct platform_driver navpoint_driver = {
|
||||
.probe = navpoint_probe,
|
||||
.remove = __devexit_p(navpoint_remove),
|
||||
.driver = {
|
||||
.name = "navpoint",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &navpoint_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(navpoint_driver);
|
||||
|
||||
MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>");
|
||||
MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:navpoint");
|
@ -41,7 +41,7 @@
|
||||
#define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03))
|
||||
|
||||
/** Driver version. */
|
||||
static const char fsp_drv_ver[] = "1.0.0-K";
|
||||
static const char fsp_drv_ver[] = "1.1.0-K";
|
||||
|
||||
/*
|
||||
* Make sure that the value being sent to FSP will not conflict with
|
||||
@ -303,6 +303,27 @@ static int fsp_get_revision(struct psmouse *psmouse, int *rev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsp_get_sn(struct psmouse *psmouse, int *sn)
|
||||
{
|
||||
int v0, v1, v2;
|
||||
int rc = -EIO;
|
||||
|
||||
/* production number since Cx is available at: 0x0b40 ~ 0x0b42 */
|
||||
if (fsp_page_reg_write(psmouse, FSP_PAGE_0B))
|
||||
goto out;
|
||||
if (fsp_reg_read(psmouse, FSP_REG_SN0, &v0))
|
||||
goto out;
|
||||
if (fsp_reg_read(psmouse, FSP_REG_SN1, &v1))
|
||||
goto out;
|
||||
if (fsp_reg_read(psmouse, FSP_REG_SN2, &v2))
|
||||
goto out;
|
||||
*sn = (v0 << 16) | (v1 << 8) | v2;
|
||||
rc = 0;
|
||||
out:
|
||||
fsp_page_reg_write(psmouse, FSP_PAGE_DEFAULT);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fsp_get_buttons(struct psmouse *psmouse, int *btn)
|
||||
{
|
||||
static const int buttons[] = {
|
||||
@ -1000,16 +1021,21 @@ static int fsp_reconnect(struct psmouse *psmouse)
|
||||
int fsp_init(struct psmouse *psmouse)
|
||||
{
|
||||
struct fsp_data *priv;
|
||||
int ver, rev;
|
||||
int ver, rev, sn = 0;
|
||||
int error;
|
||||
|
||||
if (fsp_get_version(psmouse, &ver) ||
|
||||
fsp_get_revision(psmouse, &rev)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
if (ver >= FSP_VER_STL3888_C0) {
|
||||
/* firmware information is only available since C0 */
|
||||
fsp_get_sn(psmouse, &sn);
|
||||
}
|
||||
|
||||
psmouse_info(psmouse, "Finger Sensing Pad, hw: %d.%d.%d, sw: %s\n",
|
||||
ver >> 4, ver & 0x0F, rev, fsp_drv_ver);
|
||||
psmouse_info(psmouse,
|
||||
"Finger Sensing Pad, hw: %d.%d.%d, sn: %x, sw: %s\n",
|
||||
ver >> 4, ver & 0x0F, rev, sn, fsp_drv_ver);
|
||||
|
||||
psmouse->private = priv = kzalloc(sizeof(struct fsp_data), GFP_KERNEL);
|
||||
if (!priv)
|
||||
|
@ -65,6 +65,14 @@
|
||||
#define FSP_BIT_SWC1_GST_GRP1 BIT(6)
|
||||
#define FSP_BIT_SWC1_BX_COMPAT BIT(7)
|
||||
|
||||
#define FSP_PAGE_0B (0x0b)
|
||||
#define FSP_PAGE_82 (0x82)
|
||||
#define FSP_PAGE_DEFAULT FSP_PAGE_82
|
||||
|
||||
#define FSP_REG_SN0 (0x40)
|
||||
#define FSP_REG_SN1 (0x41)
|
||||
#define FSP_REG_SN2 (0x42)
|
||||
|
||||
/* Finger-sensing Pad packet formating related definitions */
|
||||
|
||||
/* absolute packet type */
|
||||
|
@ -355,15 +355,4 @@ static struct serio_driver sermouse_drv = {
|
||||
.disconnect = sermouse_disconnect,
|
||||
};
|
||||
|
||||
static int __init sermouse_init(void)
|
||||
{
|
||||
return serio_register_driver(&sermouse_drv);
|
||||
}
|
||||
|
||||
static void __exit sermouse_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&sermouse_drv);
|
||||
}
|
||||
|
||||
module_init(sermouse_init);
|
||||
module_exit(sermouse_exit);
|
||||
module_serio_driver(sermouse_drv);
|
||||
|
@ -45,16 +45,6 @@
|
||||
#define YMIN_NOMINAL 1408
|
||||
#define YMAX_NOMINAL 4448
|
||||
|
||||
/*
|
||||
* Synaptics touchpads report the y coordinate from bottom to top, which is
|
||||
* opposite from what userspace expects.
|
||||
* This function is used to invert y before reporting.
|
||||
*/
|
||||
static int synaptics_invert_y(int y)
|
||||
{
|
||||
return YMAX_NOMINAL + YMIN_NOMINAL - y;
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Stuff we need even when we do not want native Synaptics support
|
||||
@ -111,6 +101,16 @@ void synaptics_reset(struct psmouse *psmouse)
|
||||
* Synaptics communications functions
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* Synaptics touchpads report the y coordinate from bottom to top, which is
|
||||
* opposite from what userspace expects.
|
||||
* This function is used to invert y before reporting.
|
||||
*/
|
||||
static int synaptics_invert_y(int y)
|
||||
{
|
||||
return YMAX_NOMINAL + YMIN_NOMINAL - y;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a command to the synpatics touchpad by special commands
|
||||
*/
|
||||
|
@ -548,16 +548,4 @@ static struct serio_driver vsxxxaa_drv = {
|
||||
.disconnect = vsxxxaa_disconnect,
|
||||
};
|
||||
|
||||
static int __init vsxxxaa_init(void)
|
||||
{
|
||||
return serio_register_driver(&vsxxxaa_drv);
|
||||
}
|
||||
|
||||
static void __exit vsxxxaa_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&vsxxxaa_drv);
|
||||
}
|
||||
|
||||
module_init(vsxxxaa_init);
|
||||
module_exit(vsxxxaa_exit);
|
||||
|
||||
module_serio_driver(vsxxxaa_drv);
|
||||
|
@ -1,87 +0,0 @@
|
||||
/*
|
||||
* Helpers for open firmware matrix keyboard bindings
|
||||
*
|
||||
* Copyright (C) 2012 Google, Inc
|
||||
*
|
||||
* Author:
|
||||
* Olof Johansson <olof@lixom.net>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/input/matrix_keypad.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct matrix_keymap_data *
|
||||
matrix_keyboard_of_fill_keymap(struct device_node *np,
|
||||
const char *propname)
|
||||
{
|
||||
struct matrix_keymap_data *kd;
|
||||
u32 *keymap;
|
||||
int proplen, i;
|
||||
const __be32 *prop;
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
if (!propname)
|
||||
propname = "linux,keymap";
|
||||
|
||||
prop = of_get_property(np, propname, &proplen);
|
||||
if (!prop)
|
||||
return NULL;
|
||||
|
||||
if (proplen % sizeof(u32)) {
|
||||
pr_warn("Malformed keymap property %s in %s\n",
|
||||
propname, np->full_name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kd = kzalloc(sizeof(*kd), GFP_KERNEL);
|
||||
if (!kd)
|
||||
return NULL;
|
||||
|
||||
kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL);
|
||||
if (!kd->keymap) {
|
||||
kfree(kd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kd->keymap_size = proplen / sizeof(u32);
|
||||
|
||||
for (i = 0; i < kd->keymap_size; i++) {
|
||||
u32 tmp = be32_to_cpup(prop + i);
|
||||
int key_code, row, col;
|
||||
|
||||
row = (tmp >> 24) & 0xff;
|
||||
col = (tmp >> 16) & 0xff;
|
||||
key_code = tmp & 0xffff;
|
||||
keymap[i] = KEY(row, col, key_code);
|
||||
}
|
||||
|
||||
return kd;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap);
|
||||
|
||||
void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd)
|
||||
{
|
||||
if (kd) {
|
||||
kfree(kd->keymap);
|
||||
kfree(kd);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap);
|
@ -206,6 +206,7 @@ static const struct pci_device_id pcips2_ids[] = {
|
||||
},
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pcips2_ids);
|
||||
|
||||
static struct pci_driver pcips2_driver = {
|
||||
.name = "pcips2",
|
||||
@ -214,20 +215,8 @@ static struct pci_driver pcips2_driver = {
|
||||
.remove = __devexit_p(pcips2_remove),
|
||||
};
|
||||
|
||||
static int __init pcips2_init(void)
|
||||
{
|
||||
return pci_register_driver(&pcips2_driver);
|
||||
}
|
||||
|
||||
static void __exit pcips2_exit(void)
|
||||
{
|
||||
pci_unregister_driver(&pcips2_driver);
|
||||
}
|
||||
|
||||
module_init(pcips2_init);
|
||||
module_exit(pcips2_exit);
|
||||
module_pci_driver(pcips2_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
|
||||
MODULE_DESCRIPTION("PCI PS/2 keyboard/mouse driver");
|
||||
MODULE_DEVICE_TABLE(pci, pcips2_ids);
|
||||
|
@ -304,15 +304,4 @@ static struct serio_driver ps2mult_drv = {
|
||||
.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);
|
||||
module_serio_driver(ps2mult_drv);
|
||||
|
@ -165,31 +165,38 @@ static ssize_t serio_raw_read(struct file *file, char __user *buffer,
|
||||
struct serio_raw *serio_raw = client->serio_raw;
|
||||
char uninitialized_var(c);
|
||||
ssize_t read = 0;
|
||||
int retval;
|
||||
int error;
|
||||
|
||||
if (serio_raw->dead)
|
||||
return -ENODEV;
|
||||
for (;;) {
|
||||
if (serio_raw->dead)
|
||||
return -ENODEV;
|
||||
|
||||
if (serio_raw->head == serio_raw->tail && (file->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
if (serio_raw->head == serio_raw->tail &&
|
||||
(file->f_flags & O_NONBLOCK))
|
||||
return -EAGAIN;
|
||||
|
||||
retval = wait_event_interruptible(serio_raw->wait,
|
||||
serio_raw->head != serio_raw->tail || serio_raw->dead);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
if (serio_raw->dead)
|
||||
return -ENODEV;
|
||||
|
||||
while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
|
||||
if (put_user(c, buffer++)) {
|
||||
retval = -EFAULT;
|
||||
if (count == 0)
|
||||
break;
|
||||
|
||||
while (read < count && serio_raw_fetch_byte(serio_raw, &c)) {
|
||||
if (put_user(c, buffer++))
|
||||
return -EFAULT;
|
||||
read++;
|
||||
}
|
||||
|
||||
if (read)
|
||||
break;
|
||||
|
||||
if (!(file->f_flags & O_NONBLOCK)) {
|
||||
error = wait_event_interruptible(serio_raw->wait,
|
||||
serio_raw->head != serio_raw->tail ||
|
||||
serio_raw->dead);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
read++;
|
||||
}
|
||||
|
||||
return read ?: retval;
|
||||
return read;
|
||||
}
|
||||
|
||||
static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
|
||||
@ -197,8 +204,7 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
|
||||
{
|
||||
struct serio_raw_client *client = file->private_data;
|
||||
struct serio_raw *serio_raw = client->serio_raw;
|
||||
ssize_t written = 0;
|
||||
int retval;
|
||||
int retval = 0;
|
||||
unsigned char c;
|
||||
|
||||
retval = mutex_lock_interruptible(&serio_raw_mutex);
|
||||
@ -218,16 +224,20 @@ static ssize_t serio_raw_write(struct file *file, const char __user *buffer,
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (serio_write(serio_raw->serio, c)) {
|
||||
retval = -EIO;
|
||||
/* Either signal error or partial write */
|
||||
if (retval == 0)
|
||||
retval = -EIO;
|
||||
goto out;
|
||||
}
|
||||
written++;
|
||||
|
||||
retval++;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&serio_raw_mutex);
|
||||
return written ?: retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static unsigned int serio_raw_poll(struct file *file, poll_table *wait)
|
||||
@ -432,15 +442,4 @@ static struct serio_driver serio_raw_drv = {
|
||||
.manual_bind = true,
|
||||
};
|
||||
|
||||
static int __init serio_raw_init(void)
|
||||
{
|
||||
return serio_register_driver(&serio_raw_drv);
|
||||
}
|
||||
|
||||
static void __exit serio_raw_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&serio_raw_drv);
|
||||
}
|
||||
|
||||
module_init(serio_raw_init);
|
||||
module_exit(serio_raw_exit);
|
||||
module_serio_driver(serio_raw_drv);
|
||||
|
@ -73,7 +73,8 @@ struct xps2data {
|
||||
spinlock_t lock;
|
||||
void __iomem *base_address; /* virt. address of control registers */
|
||||
unsigned int flags;
|
||||
struct serio serio; /* serio */
|
||||
struct serio *serio; /* serio */
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/************************************/
|
||||
@ -119,7 +120,7 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
|
||||
|
||||
/* Check which interrupt is active */
|
||||
if (intr_sr & XPS2_IPIXR_RX_OVF)
|
||||
dev_warn(drvdata->serio.dev.parent, "receive overrun error\n");
|
||||
dev_warn(drvdata->dev, "receive overrun error\n");
|
||||
|
||||
if (intr_sr & XPS2_IPIXR_RX_ERR)
|
||||
drvdata->flags |= SERIO_PARITY;
|
||||
@ -132,10 +133,10 @@ static irqreturn_t xps2_interrupt(int irq, void *dev_id)
|
||||
|
||||
/* Error, if a byte is not received */
|
||||
if (status) {
|
||||
dev_err(drvdata->serio.dev.parent,
|
||||
dev_err(drvdata->dev,
|
||||
"wrong rcvd byte count (%d)\n", status);
|
||||
} else {
|
||||
serio_interrupt(&drvdata->serio, c, drvdata->flags);
|
||||
serio_interrupt(drvdata->serio, c, drvdata->flags);
|
||||
drvdata->flags = 0;
|
||||
}
|
||||
}
|
||||
@ -193,7 +194,7 @@ static int sxps2_open(struct serio *pserio)
|
||||
error = request_irq(drvdata->irq, &xps2_interrupt, 0,
|
||||
DRIVER_NAME, drvdata);
|
||||
if (error) {
|
||||
dev_err(drvdata->serio.dev.parent,
|
||||
dev_err(drvdata->dev,
|
||||
"Couldn't allocate interrupt %d\n", drvdata->irq);
|
||||
return error;
|
||||
}
|
||||
@ -259,15 +260,16 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
|
||||
}
|
||||
|
||||
drvdata = kzalloc(sizeof(struct xps2data), GFP_KERNEL);
|
||||
if (!drvdata) {
|
||||
dev_err(dev, "Couldn't allocate device private record\n");
|
||||
return -ENOMEM;
|
||||
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
|
||||
if (!drvdata || !serio) {
|
||||
error = -ENOMEM;
|
||||
goto failed1;
|
||||
}
|
||||
|
||||
dev_set_drvdata(dev, drvdata);
|
||||
|
||||
spin_lock_init(&drvdata->lock);
|
||||
drvdata->irq = r_irq.start;
|
||||
drvdata->serio = serio;
|
||||
drvdata->dev = dev;
|
||||
|
||||
phys_addr = r_mem.start;
|
||||
remap_size = resource_size(&r_mem);
|
||||
@ -298,7 +300,6 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
|
||||
(unsigned long long)phys_addr, drvdata->base_address,
|
||||
drvdata->irq);
|
||||
|
||||
serio = &drvdata->serio;
|
||||
serio->id.type = SERIO_8042;
|
||||
serio->write = sxps2_write;
|
||||
serio->open = sxps2_open;
|
||||
@ -312,13 +313,14 @@ static int __devinit xps2_of_probe(struct platform_device *ofdev)
|
||||
|
||||
serio_register_port(serio);
|
||||
|
||||
platform_set_drvdata(ofdev, drvdata);
|
||||
return 0; /* success */
|
||||
|
||||
failed2:
|
||||
release_mem_region(phys_addr, remap_size);
|
||||
failed1:
|
||||
kfree(serio);
|
||||
kfree(drvdata);
|
||||
dev_set_drvdata(dev, NULL);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -333,22 +335,21 @@ failed1:
|
||||
*/
|
||||
static int __devexit xps2_of_remove(struct platform_device *of_dev)
|
||||
{
|
||||
struct device *dev = &of_dev->dev;
|
||||
struct xps2data *drvdata = dev_get_drvdata(dev);
|
||||
struct xps2data *drvdata = platform_get_drvdata(of_dev);
|
||||
struct resource r_mem; /* IO mem resources */
|
||||
|
||||
serio_unregister_port(&drvdata->serio);
|
||||
serio_unregister_port(drvdata->serio);
|
||||
iounmap(drvdata->base_address);
|
||||
|
||||
/* Get iospace of the device */
|
||||
if (of_address_to_resource(of_dev->dev.of_node, 0, &r_mem))
|
||||
dev_err(dev, "invalid address\n");
|
||||
dev_err(drvdata->dev, "invalid address\n");
|
||||
else
|
||||
release_mem_region(r_mem.start, resource_size(&r_mem));
|
||||
|
||||
kfree(drvdata);
|
||||
|
||||
dev_set_drvdata(dev, NULL);
|
||||
platform_set_drvdata(of_dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1862,7 +1862,7 @@ aiptek_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
||||
if (i == ARRAY_SIZE(speeds)) {
|
||||
dev_info(&intf->dev,
|
||||
"Aiptek tried all speeds, no sane response\n");
|
||||
goto fail2;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
/* Associate this driver's struct with the usb interface.
|
||||
|
@ -135,6 +135,6 @@ 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);
|
||||
int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
#endif
|
||||
|
@ -28,6 +28,7 @@
|
||||
#define HID_USAGE_Y_TILT 0x3e
|
||||
#define HID_USAGE_FINGER 0x22
|
||||
#define HID_USAGE_STYLUS 0x20
|
||||
#define HID_USAGE_CONTACTMAX 0x55
|
||||
#define HID_COLLECTION 0xa1
|
||||
#define HID_COLLECTION_LOGICAL 0x02
|
||||
#define HID_COLLECTION_END 0xc0
|
||||
@ -204,6 +205,27 @@ static int wacom_parse_logical_collection(unsigned char *report,
|
||||
return length;
|
||||
}
|
||||
|
||||
static void wacom_retrieve_report_data(struct usb_interface *intf,
|
||||
struct wacom_features *features)
|
||||
{
|
||||
int result = 0;
|
||||
unsigned char *rep_data;
|
||||
|
||||
rep_data = kmalloc(2, GFP_KERNEL);
|
||||
if (rep_data) {
|
||||
|
||||
rep_data[0] = 12;
|
||||
result = wacom_get_report(intf, WAC_HID_FEATURE_REPORT,
|
||||
rep_data[0], &rep_data, 2,
|
||||
WAC_MSG_RETRIES);
|
||||
|
||||
if (result >= 0 && rep_data[1] > 2)
|
||||
features->touch_max = rep_data[1];
|
||||
|
||||
kfree(rep_data);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interface Descriptor of wacom devices can be incomplete and
|
||||
* inconsistent so wacom_features table is used to store stylus
|
||||
@ -236,6 +258,9 @@ static int wacom_parse_logical_collection(unsigned char *report,
|
||||
* 3rd gen Bamboo Touch no longer define a Digitizer-Finger Pysical
|
||||
* Collection. Instead they define a Logical Collection with a single
|
||||
* Logical Maximum for both X and Y.
|
||||
*
|
||||
* Intuos5 touch interface does not contain useful data. We deal with
|
||||
* this after returning from this function.
|
||||
*/
|
||||
static int wacom_parse_hid(struct usb_interface *intf,
|
||||
struct hid_descriptor *hid_desc,
|
||||
@ -295,6 +320,10 @@ static int wacom_parse_hid(struct usb_interface *intf,
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_TPC2FG;
|
||||
}
|
||||
|
||||
if (features->type == MTSCREEN)
|
||||
features->pktlen = WACOM_PKGLEN_MTOUCH;
|
||||
|
||||
if (features->type == BAMBOO_PT) {
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH;
|
||||
@ -327,18 +356,15 @@ static int wacom_parse_hid(struct usb_interface *intf,
|
||||
case HID_USAGE_Y:
|
||||
if (usage == WCM_DESKTOP) {
|
||||
if (finger) {
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
if (features->type == TABLETPC2FG) {
|
||||
/* need to reset back */
|
||||
features->pktlen = WACOM_PKGLEN_TPC2FG;
|
||||
int type = features->type;
|
||||
|
||||
if (type == TABLETPC2FG || type == MTSCREEN) {
|
||||
features->y_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
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;
|
||||
} else if (type == BAMBOO_PT) {
|
||||
features->y_phy =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
features->y_max =
|
||||
@ -352,10 +378,6 @@ static int wacom_parse_hid(struct usb_interface *intf,
|
||||
i += 4;
|
||||
}
|
||||
} else if (pen) {
|
||||
/* penabled only accepts exact bytes of data */
|
||||
if (features->type == TABLETPC2FG)
|
||||
features->pktlen = WACOM_PKGLEN_GRAPHIRE;
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
features->y_max =
|
||||
get_unaligned_le16(&report[i + 3]);
|
||||
i += 4;
|
||||
@ -377,6 +399,11 @@ static int wacom_parse_hid(struct usb_interface *intf,
|
||||
pen = 1;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case HID_USAGE_CONTACTMAX:
|
||||
wacom_retrieve_report_data(intf, features);
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -413,22 +440,29 @@ 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 MT Tablet PC or
|
||||
* not a Tablet PC */
|
||||
if (features->type == TABLETPC2FG) {
|
||||
do {
|
||||
rep_data[0] = 3;
|
||||
rep_data[1] = 4;
|
||||
rep_data[2] = 0;
|
||||
rep_data[3] = 0;
|
||||
report_id = 3;
|
||||
error = wacom_set_report(intf, WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 4, 1);
|
||||
if (error >= 0)
|
||||
error = wacom_get_report(intf,
|
||||
WAC_HID_FEATURE_REPORT,
|
||||
report_id, rep_data, 4, 1);
|
||||
} while ((error < 0 || rep_data[1] != 4) && limit++ < WAC_MSG_RETRIES);
|
||||
/* ask to report Wacom data */
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
/* if it is an MT Tablet PC touch */
|
||||
if (features->type == TABLETPC2FG ||
|
||||
features->type == MTSCREEN) {
|
||||
do {
|
||||
rep_data[0] = 3;
|
||||
rep_data[1] = 4;
|
||||
rep_data[2] = 0;
|
||||
rep_data[3] = 0;
|
||||
report_id = 3;
|
||||
error = wacom_set_report(intf,
|
||||
WAC_HID_FEATURE_REPORT,
|
||||
report_id,
|
||||
rep_data, 4, 1);
|
||||
if (error >= 0)
|
||||
error = wacom_get_report(intf,
|
||||
WAC_HID_FEATURE_REPORT,
|
||||
report_id,
|
||||
rep_data, 4, 1);
|
||||
} while ((error < 0 || rep_data[1] != 4) &&
|
||||
limit++ < WAC_MSG_RETRIES);
|
||||
}
|
||||
} else if (features->type != TABLETPC &&
|
||||
features->type != WIRELESS &&
|
||||
features->device_type == BTN_TOOL_PEN) {
|
||||
@ -450,7 +484,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
|
||||
}
|
||||
|
||||
static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
|
||||
struct wacom_features *features)
|
||||
struct wacom_features *features)
|
||||
{
|
||||
int error = 0;
|
||||
struct usb_host_interface *interface = intf->cur_altsetting;
|
||||
@ -478,16 +512,21 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
|
||||
}
|
||||
}
|
||||
|
||||
/* only Tablet PCs and Bamboo P&T need to retrieve the info */
|
||||
if ((features->type != TABLETPC) && (features->type != TABLETPC2FG) &&
|
||||
(features->type != BAMBOO_PT))
|
||||
/* only devices that support touch need to retrieve the info */
|
||||
if (features->type != TABLETPC &&
|
||||
features->type != TABLETPC2FG &&
|
||||
features->type != BAMBOO_PT &&
|
||||
features->type != MTSCREEN) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc)) {
|
||||
if (usb_get_extra_descriptor(&interface->endpoint[0],
|
||||
HID_DEVICET_REPORT, &hid_desc)) {
|
||||
printk("wacom: can not retrieve extra class descriptor\n");
|
||||
error = 1;
|
||||
error = usb_get_extra_descriptor(interface, HID_DEVICET_HID, &hid_desc);
|
||||
if (error) {
|
||||
error = usb_get_extra_descriptor(&interface->endpoint[0],
|
||||
HID_DEVICET_REPORT, &hid_desc);
|
||||
if (error) {
|
||||
dev_err(&intf->dev,
|
||||
"can not retrieve extra class descriptor\n");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@ -577,23 +616,39 @@ static void wacom_remove_shared_data(struct wacom_wac *wacom)
|
||||
static int wacom_led_control(struct wacom *wacom)
|
||||
{
|
||||
unsigned char *buf;
|
||||
int retval, led = 0;
|
||||
int retval;
|
||||
|
||||
buf = kzalloc(9, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
|
||||
wacom->wacom_wac.features.type == WACOM_24HD)
|
||||
led = (wacom->led.select[1] << 4) | 0x40;
|
||||
if (wacom->wacom_wac.features.type >= INTUOS5S &&
|
||||
wacom->wacom_wac.features.type <= INTUOS5L) {
|
||||
/*
|
||||
* Touch Ring and crop mark LED luminance may take on
|
||||
* one of four values:
|
||||
* 0 = Low; 1 = Medium; 2 = High; 3 = Off
|
||||
*/
|
||||
int ring_led = wacom->led.select[0] & 0x03;
|
||||
int ring_lum = (((wacom->led.llv & 0x60) >> 5) - 1) & 0x03;
|
||||
int crop_lum = 0;
|
||||
|
||||
led |= wacom->led.select[0] | 0x4;
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = (crop_lum << 4) | (ring_lum << 2) | (ring_led);
|
||||
}
|
||||
else {
|
||||
int led = wacom->led.select[0] | 0x4;
|
||||
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = led;
|
||||
buf[2] = wacom->led.llv;
|
||||
buf[3] = wacom->led.hlv;
|
||||
buf[4] = wacom->led.img_lum;
|
||||
if (wacom->wacom_wac.features.type == WACOM_21UX2 ||
|
||||
wacom->wacom_wac.features.type == WACOM_24HD)
|
||||
led |= (wacom->led.select[1] << 4) | 0x40;
|
||||
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = led;
|
||||
buf[2] = wacom->led.llv;
|
||||
buf[3] = wacom->led.hlv;
|
||||
buf[4] = wacom->led.img_lum;
|
||||
}
|
||||
|
||||
retval = wacom_set_report(wacom->intf, 0x03, WAC_CMD_LED_CONTROL,
|
||||
buf, 9, WAC_CMD_RETRIES);
|
||||
@ -786,6 +841,17 @@ static struct attribute_group intuos4_led_attr_group = {
|
||||
.attrs = intuos4_led_attrs,
|
||||
};
|
||||
|
||||
static struct attribute *intuos5_led_attrs[] = {
|
||||
&dev_attr_status0_luminance.attr,
|
||||
&dev_attr_status_led0_select.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group intuos5_led_attr_group = {
|
||||
.name = "wacom_led",
|
||||
.attrs = intuos5_led_attrs,
|
||||
};
|
||||
|
||||
static int wacom_initialize_leds(struct wacom *wacom)
|
||||
{
|
||||
int error;
|
||||
@ -815,6 +881,19 @@ static int wacom_initialize_leds(struct wacom *wacom)
|
||||
&cintiq_led_attr_group);
|
||||
break;
|
||||
|
||||
case INTUOS5S:
|
||||
case INTUOS5:
|
||||
case INTUOS5L:
|
||||
wacom->led.select[0] = 0;
|
||||
wacom->led.select[1] = 0;
|
||||
wacom->led.llv = 32;
|
||||
wacom->led.hlv = 0;
|
||||
wacom->led.img_lum = 0;
|
||||
|
||||
error = sysfs_create_group(&wacom->intf->dev.kobj,
|
||||
&intuos5_led_attr_group);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@ -843,6 +922,13 @@ static void wacom_destroy_leds(struct wacom *wacom)
|
||||
sysfs_remove_group(&wacom->intf->dev.kobj,
|
||||
&cintiq_led_attr_group);
|
||||
break;
|
||||
|
||||
case INTUOS5S:
|
||||
case INTUOS5:
|
||||
case INTUOS5L:
|
||||
sysfs_remove_group(&wacom->intf->dev.kobj,
|
||||
&intuos5_led_attr_group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -904,8 +990,10 @@ static int wacom_register_input(struct wacom *wacom)
|
||||
int error;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
if (!input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
input_dev->name = wacom_wac->name;
|
||||
input_dev->dev.parent = &intf->dev;
|
||||
@ -915,14 +1003,20 @@ static int wacom_register_input(struct wacom *wacom)
|
||||
input_set_drvdata(input_dev, wacom);
|
||||
|
||||
wacom_wac->input = input_dev;
|
||||
wacom_setup_input_capabilities(input_dev, wacom_wac);
|
||||
error = wacom_setup_input_capabilities(input_dev, wacom_wac);
|
||||
if (error)
|
||||
goto fail1;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
input_free_device(input_dev);
|
||||
wacom_wac->input = NULL;
|
||||
}
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
input_free_device(input_dev);
|
||||
wacom_wac->input = NULL;
|
||||
fail1:
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -941,22 +1035,22 @@ static void wacom_wireless_work(struct work_struct *work)
|
||||
wacom = usb_get_intfdata(usbdev->config->interface[1]);
|
||||
if (wacom->wacom_wac.input)
|
||||
input_unregister_device(wacom->wacom_wac.input);
|
||||
wacom->wacom_wac.input = 0;
|
||||
wacom->wacom_wac.input = NULL;
|
||||
|
||||
/* Touch interface */
|
||||
wacom = usb_get_intfdata(usbdev->config->interface[2]);
|
||||
if (wacom->wacom_wac.input)
|
||||
input_unregister_device(wacom->wacom_wac.input);
|
||||
wacom->wacom_wac.input = 0;
|
||||
wacom->wacom_wac.input = NULL;
|
||||
|
||||
if (wacom_wac->pid == 0) {
|
||||
printk(KERN_INFO "wacom: wireless tablet disconnected\n");
|
||||
dev_info(&wacom->intf->dev, "wireless tablet disconnected\n");
|
||||
} else {
|
||||
const struct usb_device_id *id = wacom_ids;
|
||||
|
||||
printk(KERN_INFO
|
||||
"wacom: wireless tablet connected with PID %x\n",
|
||||
wacom_wac->pid);
|
||||
dev_info(&wacom->intf->dev,
|
||||
"wireless tablet connected with PID %x\n",
|
||||
wacom_wac->pid);
|
||||
|
||||
while (id->match_flags) {
|
||||
if (id->idVendor == USB_VENDOR_ID_WACOM &&
|
||||
@ -966,8 +1060,8 @@ static void wacom_wireless_work(struct work_struct *work)
|
||||
}
|
||||
|
||||
if (!id->match_flags) {
|
||||
printk(KERN_INFO
|
||||
"wacom: ignorning unknown PID.\n");
|
||||
dev_info(&wacom->intf->dev,
|
||||
"ignoring unknown PID.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1038,11 +1132,33 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
|
||||
|
||||
endpoint = &intf->cur_altsetting->endpoint[0].desc;
|
||||
|
||||
/* Retrieve the physical and logical size for OEM devices */
|
||||
/* Retrieve the physical and logical size for touch devices */
|
||||
error = wacom_retrieve_hid_descriptor(intf, features);
|
||||
if (error)
|
||||
goto fail3;
|
||||
|
||||
/*
|
||||
* Intuos5 has no useful data about its touch interface in its
|
||||
* HID descriptor. If this is the touch interface (wMaxPacketSize
|
||||
* of WACOM_PKGLEN_BBTOUCH3), override the table values.
|
||||
*/
|
||||
if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
|
||||
if (endpoint->wMaxPacketSize == WACOM_PKGLEN_BBTOUCH3) {
|
||||
features->device_type = BTN_TOOL_FINGER;
|
||||
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
|
||||
|
||||
features->x_phy =
|
||||
(features->x_max * 100) / features->x_resolution;
|
||||
features->y_phy =
|
||||
(features->y_max * 100) / features->y_resolution;
|
||||
|
||||
features->x_max = 4096;
|
||||
features->y_max = 4096;
|
||||
} else {
|
||||
features->device_type = BTN_TOOL_PEN;
|
||||
}
|
||||
}
|
||||
|
||||
wacom_setup_device_quirks(features);
|
||||
|
||||
strlcpy(wacom_wac->name, features->name, sizeof(wacom_wac->name));
|
||||
|
@ -61,7 +61,8 @@ static int wacom_penpartner_irq(struct wacom_wac *wacom)
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
|
||||
dev_dbg(input->dev.parent,
|
||||
"%s: received unknown report #%d\n", __func__, data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -76,8 +77,8 @@ static int wacom_pl_irq(struct wacom_wac *wacom)
|
||||
int prox, pressure;
|
||||
|
||||
if (data[0] != WACOM_REPORT_PENABLED) {
|
||||
dev_dbg(&input->dev,
|
||||
"wacom_pl_irq: received unknown report #%d\n", data[0]);
|
||||
dev_dbg(input->dev.parent,
|
||||
"%s: received unknown report #%d\n", __func__, data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -147,7 +148,8 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
|
||||
struct input_dev *input = wacom->input;
|
||||
|
||||
if (data[0] != WACOM_REPORT_PENABLED) {
|
||||
printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
|
||||
dev_dbg(input->dev.parent,
|
||||
"%s: received unknown report #%d\n", __func__, data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -176,7 +178,8 @@ static int wacom_dtu_irq(struct wacom_wac *wacom)
|
||||
struct input_dev *input = wacom->input;
|
||||
int prox = data[1] & 0x20, pressure;
|
||||
|
||||
dev_dbg(&input->dev, "wacom_dtu_irq: received report #%d\n", data[0]);
|
||||
dev_dbg(input->dev.parent,
|
||||
"%s: received report #%d", __func__, data[0]);
|
||||
|
||||
if (prox) {
|
||||
/* Going into proximity select tool */
|
||||
@ -212,9 +215,8 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
|
||||
int retval = 0;
|
||||
|
||||
if (data[0] != WACOM_REPORT_PENABLED) {
|
||||
dev_dbg(&input->dev,
|
||||
"wacom_graphire_irq: received unknown report #%d\n",
|
||||
data[0]);
|
||||
dev_dbg(input->dev.parent,
|
||||
"%s: received unknown report #%d\n", __func__, data[0]);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@ -324,6 +326,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
||||
|
||||
/* Enter report */
|
||||
if ((data[1] & 0xfc) == 0xc0) {
|
||||
if (features->type >= INTUOS5S && features->type <= INTUOS5L)
|
||||
wacom->shared->stylus_in_proximity = true;
|
||||
|
||||
/* serial number of the tool */
|
||||
wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
|
||||
(data[4] << 20) + (data[5] << 12) +
|
||||
@ -409,6 +414,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom)
|
||||
|
||||
/* Exit report */
|
||||
if ((data[1] & 0xfe) == 0x80) {
|
||||
if (features->type >= INTUOS5S && features->type <= INTUOS5L)
|
||||
wacom->shared->stylus_in_proximity = false;
|
||||
|
||||
/*
|
||||
* Reset all states otherwise we lose the initial states
|
||||
* when in-prox next time
|
||||
@ -455,6 +463,7 @@ static void wacom_intuos_general(struct wacom_wac *wacom)
|
||||
if ((data[1] & 0xb8) == 0xa0) {
|
||||
t = (data[6] << 2) | ((data[7] >> 6) & 3);
|
||||
if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
|
||||
(features->type >= INTUOS5S && features->type <= INTUOS5L) ||
|
||||
features->type == WACOM_21UX2 || features->type == WACOM_24HD) {
|
||||
t = (t << 1) | (data[1] & 1);
|
||||
}
|
||||
@ -485,11 +494,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
unsigned int t;
|
||||
int idx = 0, result;
|
||||
|
||||
if (data[0] != WACOM_REPORT_PENABLED && data[0] != WACOM_REPORT_INTUOSREAD
|
||||
&& data[0] != WACOM_REPORT_INTUOSWRITE && data[0] != WACOM_REPORT_INTUOSPAD) {
|
||||
dev_dbg(&input->dev,
|
||||
"wacom_intuos_irq: received unknown report #%d\n",
|
||||
data[0]);
|
||||
if (data[0] != WACOM_REPORT_PENABLED &&
|
||||
data[0] != WACOM_REPORT_INTUOSREAD &&
|
||||
data[0] != WACOM_REPORT_INTUOSWRITE &&
|
||||
data[0] != WACOM_REPORT_INTUOSPAD &&
|
||||
data[0] != WACOM_REPORT_INTUOS5PAD) {
|
||||
dev_dbg(input->dev.parent,
|
||||
"%s: received unknown report #%d\n", __func__, data[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -498,7 +509,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
idx = data[1] & 0x01;
|
||||
|
||||
/* pad packets. Works as a second tool and is always in prox */
|
||||
if (data[0] == WACOM_REPORT_INTUOSPAD) {
|
||||
if (data[0] == WACOM_REPORT_INTUOSPAD || data[0] == WACOM_REPORT_INTUOS5PAD) {
|
||||
if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
|
||||
input_report_key(input, BTN_0, (data[2] & 0x01));
|
||||
input_report_key(input, BTN_1, (data[3] & 0x01));
|
||||
@ -574,6 +585,34 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
input_report_key(input, wacom->tool[1], 0);
|
||||
input_report_abs(input, ABS_MISC, 0);
|
||||
}
|
||||
} else if (features->type >= INTUOS5S && features->type <= INTUOS5L) {
|
||||
int i;
|
||||
|
||||
/* Touch ring mode switch has no capacitive sensor */
|
||||
input_report_key(input, BTN_0, (data[3] & 0x01));
|
||||
|
||||
/*
|
||||
* ExpressKeys on Intuos5 have a capacitive sensor in
|
||||
* addition to the mechanical switch. Switch data is
|
||||
* stored in data[4], capacitive data in data[5].
|
||||
*/
|
||||
for (i = 0; i < 8; i++)
|
||||
input_report_key(input, BTN_1 + i, data[4] & (1 << i));
|
||||
|
||||
if (data[2] & 0x80) {
|
||||
input_report_abs(input, ABS_WHEEL, (data[2] & 0x7f));
|
||||
} else {
|
||||
/* Out of proximity, clear wheel value. */
|
||||
input_report_abs(input, ABS_WHEEL, 0);
|
||||
}
|
||||
|
||||
if (data[2] | (data[3] & 0x01) | data[4]) {
|
||||
input_report_key(input, wacom->tool[1], 1);
|
||||
input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
|
||||
} else {
|
||||
input_report_key(input, wacom->tool[1], 0);
|
||||
input_report_abs(input, ABS_MISC, 0);
|
||||
}
|
||||
} else {
|
||||
if (features->type == WACOM_21UX2) {
|
||||
input_report_key(input, BTN_0, (data[5] & 0x01));
|
||||
@ -637,7 +676,9 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
(features->type == INTUOS3 ||
|
||||
features->type == INTUOS3S ||
|
||||
features->type == INTUOS4 ||
|
||||
features->type == INTUOS4S)) {
|
||||
features->type == INTUOS4S ||
|
||||
features->type == INTUOS5 ||
|
||||
features->type == INTUOS5S)) {
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -690,7 +731,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
|
||||
} else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
|
||||
/* I4 mouse */
|
||||
if (features->type >= INTUOS4S && features->type <= INTUOS4L) {
|
||||
if ((features->type >= INTUOS4S && features->type <= INTUOS4L) ||
|
||||
(features->type >= INTUOS5S && features->type <= INTUOS5L)) {
|
||||
input_report_key(input, BTN_LEFT, data[6] & 0x01);
|
||||
input_report_key(input, BTN_MIDDLE, data[6] & 0x02);
|
||||
input_report_key(input, BTN_RIGHT, data[6] & 0x04);
|
||||
@ -717,7 +759,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
}
|
||||
}
|
||||
} else if ((features->type < INTUOS3S || features->type == INTUOS3L ||
|
||||
features->type == INTUOS4L) &&
|
||||
features->type == INTUOS4L || features->type == INTUOS5L) &&
|
||||
wacom->tool[idx] == BTN_TOOL_LENS) {
|
||||
/* Lens cursor packets */
|
||||
input_report_key(input, BTN_LEFT, data[8] & 0x01);
|
||||
@ -734,6 +776,72 @@ static int wacom_intuos_irq(struct wacom_wac *wacom)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid)
|
||||
{
|
||||
int touch_max = wacom->features.touch_max;
|
||||
int i;
|
||||
|
||||
if (!wacom->slots)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < touch_max; ++i) {
|
||||
if (wacom->slots[i] == contactid)
|
||||
return i;
|
||||
}
|
||||
for (i = 0; i < touch_max; ++i) {
|
||||
if (wacom->slots[i] == -1)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int wacom_mt_touch(struct wacom_wac *wacom)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
char *data = wacom->data;
|
||||
int i;
|
||||
int current_num_contacts = data[2];
|
||||
int contacts_to_send = 0;
|
||||
|
||||
/*
|
||||
* First packet resets the counter since only the first
|
||||
* packet in series will have non-zero current_num_contacts.
|
||||
*/
|
||||
if (current_num_contacts)
|
||||
wacom->num_contacts_left = current_num_contacts;
|
||||
|
||||
/* There are at most 5 contacts per packet */
|
||||
contacts_to_send = min(5, wacom->num_contacts_left);
|
||||
|
||||
for (i = 0; i < contacts_to_send; i++) {
|
||||
int offset = (WACOM_BYTES_PER_MT_PACKET * i) + 3;
|
||||
bool touch = data[offset] & 0x1;
|
||||
int id = le16_to_cpup((__le16 *)&data[offset + 1]);
|
||||
int slot = find_slot_from_contactid(wacom, id);
|
||||
|
||||
if (slot < 0)
|
||||
continue;
|
||||
|
||||
input_mt_slot(input, slot);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||
if (touch) {
|
||||
int x = le16_to_cpup((__le16 *)&data[offset + 7]);
|
||||
int y = le16_to_cpup((__le16 *)&data[offset + 9]);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
wacom->slots[slot] = touch ? id : -1;
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input, true);
|
||||
|
||||
wacom->num_contacts_left -= contacts_to_send;
|
||||
if (wacom->num_contacts_left < 0)
|
||||
wacom->num_contacts_left = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_tpc_mt_touch(struct wacom_wac *wacom)
|
||||
{
|
||||
struct input_dev *input = wacom->input;
|
||||
@ -772,6 +880,9 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
|
||||
bool prox;
|
||||
int x = 0, y = 0;
|
||||
|
||||
if (wacom->features.touch_max > 1 || len > WACOM_PKGLEN_TPC2FG)
|
||||
return 0;
|
||||
|
||||
if (!wacom->shared->stylus_in_proximity) {
|
||||
if (len == WACOM_PKGLEN_TPC1FG) {
|
||||
prox = data[0] & 0x01;
|
||||
@ -835,15 +946,15 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
|
||||
{
|
||||
char *data = wacom->data;
|
||||
|
||||
dev_dbg(&wacom->input->dev, "wacom_tpc_irq: received report #%d\n",
|
||||
data[0]);
|
||||
dev_dbg(wacom->input->dev.parent,
|
||||
"%s: received report #%d\n", __func__, data[0]);
|
||||
|
||||
switch (len) {
|
||||
case WACOM_PKGLEN_TPC1FG:
|
||||
return wacom_tpc_single_touch(wacom, len);
|
||||
return wacom_tpc_single_touch(wacom, len);
|
||||
|
||||
case WACOM_PKGLEN_TPC2FG:
|
||||
return wacom_tpc_mt_touch(wacom);
|
||||
return wacom_tpc_mt_touch(wacom);
|
||||
|
||||
default:
|
||||
switch (data[0]) {
|
||||
@ -852,6 +963,9 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
|
||||
case WACOM_REPORT_TPCST:
|
||||
return wacom_tpc_single_touch(wacom, len);
|
||||
|
||||
case WACOM_REPORT_TPCMT:
|
||||
return wacom_mt_touch(wacom);
|
||||
|
||||
case WACOM_REPORT_PENABLED:
|
||||
return wacom_tpc_pen(wacom);
|
||||
}
|
||||
@ -1120,8 +1234,18 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
|
||||
sync = wacom_intuos_irq(wacom_wac);
|
||||
break;
|
||||
|
||||
case INTUOS5S:
|
||||
case INTUOS5:
|
||||
case INTUOS5L:
|
||||
if (len == WACOM_PKGLEN_BBTOUCH3)
|
||||
sync = wacom_bpt3_touch(wacom_wac);
|
||||
else
|
||||
sync = wacom_intuos_irq(wacom_wac);
|
||||
break;
|
||||
|
||||
case TABLETPC:
|
||||
case TABLETPC2FG:
|
||||
case MTSCREEN:
|
||||
sync = wacom_tpc_irq(wacom_wac, len);
|
||||
break;
|
||||
|
||||
@ -1194,7 +1318,9 @@ void wacom_setup_device_quirks(struct wacom_features *features)
|
||||
|
||||
/* these device have multiple inputs */
|
||||
if (features->type == TABLETPC || features->type == TABLETPC2FG ||
|
||||
features->type == BAMBOO_PT || features->type == WIRELESS)
|
||||
features->type == BAMBOO_PT || features->type == WIRELESS ||
|
||||
(features->type >= INTUOS5S && features->type <= INTUOS5L) ||
|
||||
features->type == MTSCREEN)
|
||||
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
|
||||
|
||||
/* quirk for bamboo touch with 2 low res touches */
|
||||
@ -1225,8 +1351,8 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max,
|
||||
return (logical_max * 100) / physical_max;
|
||||
}
|
||||
|
||||
void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac)
|
||||
int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct wacom_features *features = &wacom_wac->features;
|
||||
int i;
|
||||
@ -1361,6 +1487,50 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
wacom_setup_intuos(wacom_wac);
|
||||
break;
|
||||
|
||||
case INTUOS5:
|
||||
case INTUOS5L:
|
||||
if (features->device_type == BTN_TOOL_PEN) {
|
||||
__set_bit(BTN_7, input_dev->keybit);
|
||||
__set_bit(BTN_8, input_dev->keybit);
|
||||
}
|
||||
/* fall through */
|
||||
|
||||
case INTUOS5S:
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
if (features->device_type == BTN_TOOL_PEN) {
|
||||
for (i = 0; i < 7; i++)
|
||||
__set_bit(BTN_0 + i, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_DISTANCE, 0,
|
||||
features->distance_max,
|
||||
0, 0);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
|
||||
|
||||
wacom_setup_intuos(wacom_wac);
|
||||
} else if (features->device_type == BTN_TOOL_FINGER) {
|
||||
__clear_bit(ABS_MISC, input_dev->absbit);
|
||||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
||||
|
||||
input_mt_init_slots(input_dev, features->touch_max);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
0, 255, 0, 0);
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
case INTUOS4:
|
||||
case INTUOS4L:
|
||||
__set_bit(BTN_7, input_dev->keybit);
|
||||
@ -1378,9 +1548,19 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
break;
|
||||
|
||||
case TABLETPC2FG:
|
||||
case MTSCREEN:
|
||||
if (features->device_type == BTN_TOOL_FINGER) {
|
||||
|
||||
input_mt_init_slots(input_dev, 2);
|
||||
wacom_wac->slots = kmalloc(features->touch_max *
|
||||
sizeof(int),
|
||||
GFP_KERNEL);
|
||||
if (!wacom_wac->slots)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < features->touch_max; i++)
|
||||
wacom_wac->slots[i] = -1;
|
||||
|
||||
input_mt_init_slots(input_dev, features->touch_max);
|
||||
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
@ -1435,6 +1615,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
|
||||
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
|
||||
input_mt_init_slots(input_dev, features->touch_max);
|
||||
|
||||
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
|
||||
__set_bit(BTN_TOOL_TRIPLETAP,
|
||||
@ -1442,13 +1623,9 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
__set_bit(BTN_TOOL_QUADTAP,
|
||||
input_dev->keybit);
|
||||
|
||||
input_mt_init_slots(input_dev, 16);
|
||||
|
||||
input_set_abs_params(input_dev,
|
||||
ABS_MT_TOUCH_MAJOR,
|
||||
0, 255, 0, 0);
|
||||
} else {
|
||||
input_mt_init_slots(input_dev, 2);
|
||||
}
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
@ -1468,6 +1645,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
}
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wacom_features wacom_features_0x00 =
|
||||
@ -1635,6 +1813,24 @@ static const struct wacom_features wacom_features_0xBB =
|
||||
static const struct wacom_features wacom_features_0xBC =
|
||||
{ "Wacom Intuos4 WL", WACOM_PKGLEN_INTUOS, 40840, 25400, 2047,
|
||||
63, INTUOS4, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
|
||||
static const struct wacom_features wacom_features_0x26 =
|
||||
{ "Wacom Intuos5 touch S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047,
|
||||
63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
|
||||
.touch_max = 16 };
|
||||
static const struct wacom_features wacom_features_0x27 =
|
||||
{ "Wacom Intuos5 touch M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047,
|
||||
63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
|
||||
.touch_max = 16 };
|
||||
static const struct wacom_features wacom_features_0x28 =
|
||||
{ "Wacom Intuos5 touch L", WACOM_PKGLEN_INTUOS, 65024, 40640, 2047,
|
||||
63, INTUOS5L, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES,
|
||||
.touch_max = 16 };
|
||||
static const struct wacom_features wacom_features_0x29 =
|
||||
{ "Wacom Intuos5 S", WACOM_PKGLEN_INTUOS, 31496, 19685, 2047,
|
||||
63, INTUOS5S, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
|
||||
static const struct wacom_features wacom_features_0x2A =
|
||||
{ "Wacom Intuos5 M", WACOM_PKGLEN_INTUOS, 44704, 27940, 2047,
|
||||
63, INTUOS5, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
|
||||
static const struct wacom_features wacom_features_0xF4 =
|
||||
{ "Wacom Cintiq 24HD", WACOM_PKGLEN_INTUOS, 104480, 65600, 2047,
|
||||
63, WACOM_24HD, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES };
|
||||
@ -1676,13 +1872,19 @@ static const struct wacom_features wacom_features_0x9F =
|
||||
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0xE2 =
|
||||
{ "Wacom ISDv4 E2", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255,
|
||||
0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xE3 =
|
||||
{ "Wacom ISDv4 E3", WACOM_PKGLEN_TPC2FG, 26202, 16325, 255,
|
||||
0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xE5 =
|
||||
{ "Wacom ISDv4 E5", WACOM_PKGLEN_MTOUCH, 26202, 16325, 255,
|
||||
0, MTSCREEN, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0xE6 =
|
||||
{ "Wacom ISDv4 E6", WACOM_PKGLEN_TPC2FG, 27760, 15694, 255,
|
||||
0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
0, TABLETPC2FG, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xEC =
|
||||
{ "Wacom ISDv4 EC", WACOM_PKGLEN_GRAPHIRE, 25710, 14500, 255,
|
||||
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
@ -1691,19 +1893,22 @@ static const struct wacom_features wacom_features_0x47 =
|
||||
31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0x84 =
|
||||
{ "Wacom Wireless Receiver", WACOM_PKGLEN_WIRELESS, 0, 0, 0,
|
||||
0, WIRELESS, 0, 0 };
|
||||
0, WIRELESS, 0, 0, .touch_max = 16 };
|
||||
static const struct wacom_features wacom_features_0xD0 =
|
||||
{ "Wacom Bamboo 2FG", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xD1 =
|
||||
{ "Wacom Bamboo 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xD2 =
|
||||
{ "Wacom Bamboo Craft", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0xD3 =
|
||||
{ "Wacom Bamboo 2FG 6x8", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xD4 =
|
||||
{ "Wacom Bamboo Pen", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
@ -1712,28 +1917,35 @@ static const struct wacom_features wacom_features_0xD5 =
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0xD6 =
|
||||
{ "Wacom BambooPT 2FG 4x5", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xD7 =
|
||||
{ "Wacom BambooPT 2FG Small", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xD8 =
|
||||
{ "Wacom Bamboo Comic 2FG", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xDA =
|
||||
{ "Wacom Bamboo 2FG 4x5 SE", WACOM_PKGLEN_BBFUN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static struct wacom_features wacom_features_0xDB =
|
||||
{ "Wacom Bamboo 2FG 6x8 SE", WACOM_PKGLEN_BBFUN, 21648, 13700, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 2 };
|
||||
static const struct wacom_features wacom_features_0xDD =
|
||||
{ "Wacom Bamboo Connect", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
static const struct wacom_features wacom_features_0xDE =
|
||||
{ "Wacom Bamboo 16FG 4x5", WACOM_PKGLEN_BBPEN, 14720, 9200, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 16 };
|
||||
static const struct wacom_features wacom_features_0xDF =
|
||||
{ "Wacom Bamboo 16FG 6x8", WACOM_PKGLEN_BBPEN, 21648, 13700, 1023,
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
31, BAMBOO_PT, WACOM_INTUOS_RES, WACOM_INTUOS_RES,
|
||||
.touch_max = 16 };
|
||||
static const struct wacom_features wacom_features_0x6004 =
|
||||
{ "ISD-V4", WACOM_PKGLEN_GRAPHIRE, 12800, 8000, 255,
|
||||
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
|
||||
@ -1807,6 +2019,11 @@ const struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE_WACOM(0xBA) },
|
||||
{ USB_DEVICE_WACOM(0xBB) },
|
||||
{ USB_DEVICE_WACOM(0xBC) },
|
||||
{ USB_DEVICE_WACOM(0x26) },
|
||||
{ USB_DEVICE_WACOM(0x27) },
|
||||
{ USB_DEVICE_WACOM(0x28) },
|
||||
{ USB_DEVICE_WACOM(0x29) },
|
||||
{ USB_DEVICE_WACOM(0x2A) },
|
||||
{ USB_DEVICE_WACOM(0x3F) },
|
||||
{ USB_DEVICE_WACOM(0xC5) },
|
||||
{ USB_DEVICE_WACOM(0xC6) },
|
||||
@ -1842,6 +2059,7 @@ const struct usb_device_id wacom_ids[] = {
|
||||
{ USB_DEVICE_WACOM(0x9F) },
|
||||
{ USB_DEVICE_WACOM(0xE2) },
|
||||
{ USB_DEVICE_WACOM(0xE3) },
|
||||
{ USB_DEVICE_WACOM(0xE5) },
|
||||
{ USB_DEVICE_WACOM(0xE6) },
|
||||
{ USB_DEVICE_WACOM(0xEC) },
|
||||
{ USB_DEVICE_WACOM(0x47) },
|
||||
|
@ -25,6 +25,10 @@
|
||||
#define WACOM_PKGLEN_BBTOUCH3 64
|
||||
#define WACOM_PKGLEN_BBPEN 10
|
||||
#define WACOM_PKGLEN_WIRELESS 32
|
||||
#define WACOM_PKGLEN_MTOUCH 62
|
||||
|
||||
/* wacom data size per MT contact */
|
||||
#define WACOM_BYTES_PER_MT_PACKET 11
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
@ -38,8 +42,10 @@
|
||||
#define WACOM_REPORT_INTUOSREAD 5
|
||||
#define WACOM_REPORT_INTUOSWRITE 6
|
||||
#define WACOM_REPORT_INTUOSPAD 12
|
||||
#define WACOM_REPORT_INTUOS5PAD 3
|
||||
#define WACOM_REPORT_TPC1FG 6
|
||||
#define WACOM_REPORT_TPC2FG 13
|
||||
#define WACOM_REPORT_TPCMT 13
|
||||
#define WACOM_REPORT_TPCHID 15
|
||||
#define WACOM_REPORT_TPCST 16
|
||||
|
||||
@ -65,6 +71,9 @@ enum {
|
||||
INTUOS4S,
|
||||
INTUOS4,
|
||||
INTUOS4L,
|
||||
INTUOS5S,
|
||||
INTUOS5,
|
||||
INTUOS5L,
|
||||
WACOM_24HD,
|
||||
WACOM_21UX2,
|
||||
CINTIQ,
|
||||
@ -72,6 +81,7 @@ enum {
|
||||
WACOM_MO,
|
||||
TABLETPC,
|
||||
TABLETPC2FG,
|
||||
MTSCREEN,
|
||||
MAX_TYPE
|
||||
};
|
||||
|
||||
@ -95,6 +105,7 @@ struct wacom_features {
|
||||
int pressure_fuzz;
|
||||
int distance_fuzz;
|
||||
unsigned quirks;
|
||||
unsigned touch_max;
|
||||
};
|
||||
|
||||
struct wacom_shared {
|
||||
@ -113,6 +124,8 @@ struct wacom_wac {
|
||||
struct input_dev *input;
|
||||
int pid;
|
||||
int battery_capacity;
|
||||
int num_contacts_left;
|
||||
int *slots;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -187,6 +187,23 @@ config TOUCHSCREEN_DA9034
|
||||
Say Y here to enable the support for the touchscreen found
|
||||
on Dialog Semiconductor DA9034 PMIC.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called da9034-ts.
|
||||
|
||||
config TOUCHSCREEN_DA9052
|
||||
tristate "Dialog DA9052/DA9053 TSI"
|
||||
depends on PMIC_DA9052
|
||||
help
|
||||
Say Y here to support the touchscreen found on Dialog Semiconductor
|
||||
DA9052-BC and DA9053-AA/Bx PMICs.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called da9052_tsi.
|
||||
|
||||
config TOUCHSCREEN_DYNAPRO
|
||||
tristate "Dynapro serial touchscreen"
|
||||
select SERIO
|
||||
@ -306,6 +323,18 @@ config TOUCHSCREEN_WACOM_W8001
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom_w8001.
|
||||
|
||||
config TOUCHSCREEN_WACOM_I2C
|
||||
tristate "Wacom Tablet support (I2C)"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to use the I2C version of the Wacom
|
||||
Pen Tablet.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called wacom_i2c.
|
||||
|
||||
config TOUCHSCREEN_LPC32XX
|
||||
tristate "LPC32XX touchscreen controller"
|
||||
depends on ARCH_LPC32XX
|
||||
@ -635,6 +664,7 @@ config TOUCHSCREEN_USB_COMPOSITE
|
||||
- Zytronic controllers
|
||||
- Elo TouchSystems 2700 IntelliTouch
|
||||
- EasyTouch USB Touch Controller from Data Modul
|
||||
- e2i (Mimo monitors)
|
||||
|
||||
Have a look at <http://linux.chapter7.ch/touchkit/> for
|
||||
a usage description and the required user-space stuff.
|
||||
@ -721,7 +751,7 @@ config TOUCHSCREEN_USB_ELO
|
||||
|
||||
config TOUCHSCREEN_USB_E2I
|
||||
default y
|
||||
bool "e2i Touchscreen controller (e.g. from Mimo 740)"
|
||||
bool "e2i Touchscreen controller (e.g. from Mimo 740)" if EXPERT
|
||||
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||
|
||||
config TOUCHSCREEN_USB_ZYTRONIC
|
||||
@ -744,7 +774,7 @@ config TOUCHSCREEN_USB_EASYTOUCH
|
||||
bool "EasyTouch USB Touch controller device support" if EMBEDDED
|
||||
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||
help
|
||||
Say Y here if you have a EasyTouch USB Touch controller device support.
|
||||
Say Y here if you have an EasyTouch USB Touch controller.
|
||||
If unsure, say N.
|
||||
|
||||
config TOUCHSCREEN_TOUCHIT213
|
||||
|
@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
|
||||
@ -59,6 +60,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2005) += tsc2005.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o
|
||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
|
||||
|
@ -236,7 +236,6 @@ struct mxt_object {
|
||||
struct mxt_message {
|
||||
u8 reportid;
|
||||
u8 message[7];
|
||||
u8 checksum;
|
||||
};
|
||||
|
||||
struct mxt_finger {
|
||||
@ -326,17 +325,12 @@ static bool mxt_object_writable(unsigned int type)
|
||||
}
|
||||
|
||||
static void mxt_dump_message(struct device *dev,
|
||||
struct mxt_message *message)
|
||||
struct mxt_message *message)
|
||||
{
|
||||
dev_dbg(dev, "reportid:\t0x%x\n", message->reportid);
|
||||
dev_dbg(dev, "message1:\t0x%x\n", message->message[0]);
|
||||
dev_dbg(dev, "message2:\t0x%x\n", message->message[1]);
|
||||
dev_dbg(dev, "message3:\t0x%x\n", message->message[2]);
|
||||
dev_dbg(dev, "message4:\t0x%x\n", message->message[3]);
|
||||
dev_dbg(dev, "message5:\t0x%x\n", message->message[4]);
|
||||
dev_dbg(dev, "message6:\t0x%x\n", message->message[5]);
|
||||
dev_dbg(dev, "message7:\t0x%x\n", message->message[6]);
|
||||
dev_dbg(dev, "checksum:\t0x%x\n", message->checksum);
|
||||
dev_dbg(dev, "reportid: %u\tmessage: %02x %02x %02x %02x %02x %02x %02x\n",
|
||||
message->reportid, message->message[0], message->message[1],
|
||||
message->message[2], message->message[3], message->message[4],
|
||||
message->message[5], message->message[6]);
|
||||
}
|
||||
|
||||
static int mxt_check_bootloader(struct i2c_client *client,
|
||||
@ -506,7 +500,7 @@ static int mxt_write_object(struct mxt_data *data,
|
||||
u16 reg;
|
||||
|
||||
object = mxt_get_object(data, type);
|
||||
if (!object)
|
||||
if (!object || offset >= object->size + 1)
|
||||
return -EINVAL;
|
||||
|
||||
reg = object->start_address;
|
||||
@ -1049,8 +1043,8 @@ static ssize_t mxt_update_fw_store(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(object, 0444, mxt_object_show, NULL);
|
||||
static DEVICE_ATTR(update_fw, 0664, NULL, mxt_update_fw_store);
|
||||
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
|
||||
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
|
||||
|
||||
static struct attribute *mxt_attrs[] = {
|
||||
&dev_attr_object.attr,
|
||||
@ -1201,7 +1195,7 @@ static int __devexit mxt_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mxt_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
@ -1239,13 +1233,10 @@ static int mxt_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops mxt_pm_ops = {
|
||||
.suspend = mxt_suspend,
|
||||
.resume = mxt_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mxt_pm_ops, mxt_suspend, mxt_resume);
|
||||
|
||||
static const struct i2c_device_id mxt_id[] = {
|
||||
{ "qt602240_ts", 0 },
|
||||
{ "atmel_mxt_ts", 0 },
|
||||
@ -1258,9 +1249,7 @@ static struct i2c_driver mxt_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_mxt_ts",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &mxt_pm_ops,
|
||||
#endif
|
||||
},
|
||||
.probe = mxt_probe,
|
||||
.remove = __devexit_p(mxt_remove),
|
||||
|
370
drivers/input/touchscreen/da9052_tsi.c
Normal file
370
drivers/input/touchscreen/da9052_tsi.c
Normal file
@ -0,0 +1,370 @@
|
||||
/*
|
||||
* TSI driver for Dialog DA9052
|
||||
*
|
||||
* Copyright(c) 2012 Dialog Semiconductor Ltd.
|
||||
*
|
||||
* Author: David Dajun Chen <dchen@diasemi.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.
|
||||
*
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/mfd/da9052/reg.h>
|
||||
#include <linux/mfd/da9052/da9052.h>
|
||||
|
||||
#define TSI_PEN_DOWN_STATUS 0x40
|
||||
|
||||
struct da9052_tsi {
|
||||
struct da9052 *da9052;
|
||||
struct input_dev *dev;
|
||||
struct delayed_work ts_pen_work;
|
||||
struct mutex mutex;
|
||||
unsigned int irq_pendwn;
|
||||
unsigned int irq_datardy;
|
||||
bool stopped;
|
||||
bool adc_on;
|
||||
};
|
||||
|
||||
static void da9052_ts_adc_toggle(struct da9052_tsi *tsi, bool on)
|
||||
{
|
||||
da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 0, on);
|
||||
tsi->adc_on = on;
|
||||
}
|
||||
|
||||
static irqreturn_t da9052_ts_pendwn_irq(int irq, void *data)
|
||||
{
|
||||
struct da9052_tsi *tsi = data;
|
||||
|
||||
if (!tsi->stopped) {
|
||||
/* Mask PEN_DOWN event and unmask TSI_READY event */
|
||||
disable_irq_nosync(tsi->irq_pendwn);
|
||||
enable_irq(tsi->irq_datardy);
|
||||
|
||||
da9052_ts_adc_toggle(tsi, true);
|
||||
|
||||
schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void da9052_ts_read(struct da9052_tsi *tsi)
|
||||
{
|
||||
struct input_dev *input = tsi->dev;
|
||||
int ret;
|
||||
u16 x, y, z;
|
||||
u8 v;
|
||||
|
||||
ret = da9052_reg_read(tsi->da9052, DA9052_TSI_X_MSB_REG);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
x = (u16) ret;
|
||||
|
||||
ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Y_MSB_REG);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
y = (u16) ret;
|
||||
|
||||
ret = da9052_reg_read(tsi->da9052, DA9052_TSI_Z_MSB_REG);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
z = (u16) ret;
|
||||
|
||||
ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
v = (u8) ret;
|
||||
|
||||
x = ((x << 2) & 0x3fc) | (v & 0x3);
|
||||
y = ((y << 2) & 0x3fc) | ((v & 0xc) >> 2);
|
||||
z = ((z << 2) & 0x3fc) | ((v & 0x30) >> 4);
|
||||
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, z);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static irqreturn_t da9052_ts_datardy_irq(int irq, void *data)
|
||||
{
|
||||
struct da9052_tsi *tsi = data;
|
||||
|
||||
da9052_ts_read(tsi);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void da9052_ts_pen_work(struct work_struct *work)
|
||||
{
|
||||
struct da9052_tsi *tsi = container_of(work, struct da9052_tsi,
|
||||
ts_pen_work.work);
|
||||
if (!tsi->stopped) {
|
||||
int ret = da9052_reg_read(tsi->da9052, DA9052_TSI_LSB_REG);
|
||||
if (ret < 0 || (ret & TSI_PEN_DOWN_STATUS)) {
|
||||
/* Pen is still DOWN (or read error) */
|
||||
schedule_delayed_work(&tsi->ts_pen_work, HZ / 50);
|
||||
} else {
|
||||
struct input_dev *input = tsi->dev;
|
||||
|
||||
/* Pen UP */
|
||||
da9052_ts_adc_toggle(tsi, false);
|
||||
|
||||
/* Report Pen UP */
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_sync(input);
|
||||
|
||||
/*
|
||||
* FIXME: Fixes the unhandled irq issue when quick
|
||||
* pen down and pen up events occurs
|
||||
*/
|
||||
ret = da9052_reg_update(tsi->da9052,
|
||||
DA9052_EVENT_B_REG, 0xC0, 0xC0);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
/* Mask TSI_READY event and unmask PEN_DOWN event */
|
||||
disable_irq(tsi->irq_datardy);
|
||||
enable_irq(tsi->irq_pendwn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit da9052_ts_configure_gpio(struct da9052 *da9052)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = da9052_reg_update(da9052, DA9052_GPIO_2_3_REG, 0x30, 0);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = da9052_reg_update(da9052, DA9052_GPIO_4_5_REG, 0x33, 0);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = da9052_reg_update(da9052, DA9052_GPIO_6_7_REG, 0x33, 0);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit da9052_configure_tsi(struct da9052_tsi *tsi)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = da9052_ts_configure_gpio(tsi->da9052);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Measure TSI sample every 1ms */
|
||||
error = da9052_reg_update(tsi->da9052, DA9052_ADC_CONT_REG,
|
||||
1 << 6, 1 << 6);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* TSI_DELAY: 3 slots, TSI_SKIP: 0 slots, TSI_MODE: XYZP */
|
||||
error = da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 0xFC, 0xC0);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* Supply TSIRef through LD09 */
|
||||
error = da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x59);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int da9052_ts_input_open(struct input_dev *input_dev)
|
||||
{
|
||||
struct da9052_tsi *tsi = input_get_drvdata(input_dev);
|
||||
|
||||
tsi->stopped = false;
|
||||
mb();
|
||||
|
||||
/* Unmask PEN_DOWN event */
|
||||
enable_irq(tsi->irq_pendwn);
|
||||
|
||||
/* Enable Pen Detect Circuit */
|
||||
return da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG,
|
||||
1 << 1, 1 << 1);
|
||||
}
|
||||
|
||||
static void da9052_ts_input_close(struct input_dev *input_dev)
|
||||
{
|
||||
struct da9052_tsi *tsi = input_get_drvdata(input_dev);
|
||||
|
||||
tsi->stopped = true;
|
||||
mb();
|
||||
disable_irq(tsi->irq_pendwn);
|
||||
cancel_delayed_work_sync(&tsi->ts_pen_work);
|
||||
|
||||
if (tsi->adc_on) {
|
||||
disable_irq(tsi->irq_datardy);
|
||||
da9052_ts_adc_toggle(tsi, false);
|
||||
|
||||
/*
|
||||
* If ADC was on that means that pendwn IRQ was disabled
|
||||
* twice and we need to enable it to keep enable/disable
|
||||
* counter balanced. IRQ is still off though.
|
||||
*/
|
||||
enable_irq(tsi->irq_pendwn);
|
||||
}
|
||||
|
||||
/* Disable Pen Detect Circuit */
|
||||
da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
|
||||
}
|
||||
|
||||
static int __devinit da9052_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct da9052 *da9052;
|
||||
struct da9052_tsi *tsi;
|
||||
struct input_dev *input_dev;
|
||||
int irq_pendwn;
|
||||
int irq_datardy;
|
||||
int error;
|
||||
|
||||
da9052 = dev_get_drvdata(pdev->dev.parent);
|
||||
if (!da9052)
|
||||
return -EINVAL;
|
||||
|
||||
irq_pendwn = platform_get_irq_byname(pdev, "PENDWN");
|
||||
irq_datardy = platform_get_irq_byname(pdev, "TSIRDY");
|
||||
if (irq_pendwn < 0 || irq_datardy < 0) {
|
||||
dev_err(da9052->dev, "Unable to determine device interrupts\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
tsi = kzalloc(sizeof(struct da9052_tsi), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!tsi || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
tsi->da9052 = da9052;
|
||||
tsi->dev = input_dev;
|
||||
tsi->irq_pendwn = da9052->irq_base + irq_pendwn;
|
||||
tsi->irq_datardy = da9052->irq_base + irq_datardy;
|
||||
tsi->stopped = true;
|
||||
INIT_DELAYED_WORK(&tsi->ts_pen_work, da9052_ts_pen_work);
|
||||
|
||||
input_dev->id.version = 0x0101;
|
||||
input_dev->id.vendor = 0x15B6;
|
||||
input_dev->id.product = 0x9052;
|
||||
input_dev->name = "Dialog DA9052 TouchScreen Driver";
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
input_dev->open = da9052_ts_input_open;
|
||||
input_dev->close = da9052_ts_input_close;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 1023, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 1023, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1023, 0, 0);
|
||||
|
||||
input_set_drvdata(input_dev, tsi);
|
||||
|
||||
/* Disable Pen Detect Circuit */
|
||||
da9052_reg_update(tsi->da9052, DA9052_TSI_CONT_A_REG, 1 << 1, 0);
|
||||
|
||||
/* Disable ADC */
|
||||
da9052_ts_adc_toggle(tsi, false);
|
||||
|
||||
error = request_threaded_irq(tsi->irq_pendwn,
|
||||
NULL, da9052_ts_pendwn_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"PENDWN", tsi);
|
||||
if (error) {
|
||||
dev_err(tsi->da9052->dev,
|
||||
"Failed to register PENDWN IRQ %d, error = %d\n",
|
||||
tsi->irq_pendwn, error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(tsi->irq_datardy,
|
||||
NULL, da9052_ts_datardy_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"TSIRDY", tsi);
|
||||
if (error) {
|
||||
dev_err(tsi->da9052->dev,
|
||||
"Failed to register TSIRDY IRQ %d, error = %d\n",
|
||||
tsi->irq_datardy, error);
|
||||
goto err_free_pendwn_irq;
|
||||
}
|
||||
|
||||
/* Mask PEN_DOWN and TSI_READY events */
|
||||
disable_irq(tsi->irq_pendwn);
|
||||
disable_irq(tsi->irq_datardy);
|
||||
|
||||
error = da9052_configure_tsi(tsi);
|
||||
if (error)
|
||||
goto err_free_datardy_irq;
|
||||
|
||||
error = input_register_device(tsi->dev);
|
||||
if (error)
|
||||
goto err_free_datardy_irq;
|
||||
|
||||
platform_set_drvdata(pdev, tsi);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_datardy_irq:
|
||||
free_irq(tsi->irq_datardy, tsi);
|
||||
err_free_pendwn_irq:
|
||||
free_irq(tsi->irq_pendwn, tsi);
|
||||
err_free_mem:
|
||||
kfree(tsi);
|
||||
input_free_device(input_dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit da9052_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct da9052_tsi *tsi = platform_get_drvdata(pdev);
|
||||
|
||||
da9052_reg_write(tsi->da9052, DA9052_LDO9_REG, 0x19);
|
||||
|
||||
free_irq(tsi->irq_pendwn, tsi);
|
||||
free_irq(tsi->irq_datardy, tsi);
|
||||
|
||||
input_unregister_device(tsi->dev);
|
||||
kfree(tsi);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver da9052_tsi_driver = {
|
||||
.probe = da9052_ts_probe,
|
||||
.remove = __devexit_p(da9052_ts_remove),
|
||||
.driver = {
|
||||
.name = "da9052-tsi",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(da9052_tsi_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Touchscreen driver for Dialog Semiconductor DA9052");
|
||||
MODULE_AUTHOR("Anthony Olech <Anthony.Olech@diasemi.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:da9052-tsi");
|
@ -188,19 +188,4 @@ static struct serio_driver dynapro_drv = {
|
||||
.disconnect = dynapro_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init dynapro_init(void)
|
||||
{
|
||||
return serio_register_driver(&dynapro_drv);
|
||||
}
|
||||
|
||||
static void __exit dynapro_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&dynapro_drv);
|
||||
}
|
||||
|
||||
module_init(dynapro_init);
|
||||
module_exit(dynapro_exit);
|
||||
module_serio_driver(dynapro_drv);
|
||||
|
@ -405,19 +405,4 @@ static struct serio_driver elo_drv = {
|
||||
.disconnect = elo_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init elo_init(void)
|
||||
{
|
||||
return serio_register_driver(&elo_drv);
|
||||
}
|
||||
|
||||
static void __exit elo_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&elo_drv);
|
||||
}
|
||||
|
||||
module_init(elo_init);
|
||||
module_exit(elo_exit);
|
||||
module_serio_driver(elo_drv);
|
||||
|
@ -175,15 +175,4 @@ static struct serio_driver fujitsu_drv = {
|
||||
.disconnect = fujitsu_disconnect,
|
||||
};
|
||||
|
||||
static int __init fujitsu_init(void)
|
||||
{
|
||||
return serio_register_driver(&fujitsu_drv);
|
||||
}
|
||||
|
||||
static void __exit fujitsu_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&fujitsu_drv);
|
||||
}
|
||||
|
||||
module_init(fujitsu_init);
|
||||
module_exit(fujitsu_exit);
|
||||
module_serio_driver(fujitsu_drv);
|
||||
|
@ -186,19 +186,4 @@ static struct serio_driver gunze_drv = {
|
||||
.disconnect = gunze_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init gunze_init(void)
|
||||
{
|
||||
return serio_register_driver(&gunze_drv);
|
||||
}
|
||||
|
||||
static void __exit gunze_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&gunze_drv);
|
||||
}
|
||||
|
||||
module_init(gunze_init);
|
||||
module_exit(gunze_exit);
|
||||
module_serio_driver(gunze_drv);
|
||||
|
@ -476,19 +476,4 @@ static struct serio_driver h3600ts_drv = {
|
||||
.disconnect = h3600ts_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init h3600ts_init(void)
|
||||
{
|
||||
return serio_register_driver(&h3600ts_drv);
|
||||
}
|
||||
|
||||
static void __exit h3600ts_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&h3600ts_drv);
|
||||
}
|
||||
|
||||
module_init(h3600ts_init);
|
||||
module_exit(h3600ts_exit);
|
||||
module_serio_driver(h3600ts_drv);
|
||||
|
@ -187,19 +187,4 @@ static struct serio_driver hampshire_drv = {
|
||||
.disconnect = hampshire_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init hampshire_init(void)
|
||||
{
|
||||
return serio_register_driver(&hampshire_drv);
|
||||
}
|
||||
|
||||
static void __exit hampshire_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&hampshire_drv);
|
||||
}
|
||||
|
||||
module_init(hampshire_init);
|
||||
module_exit(hampshire_exit);
|
||||
module_serio_driver(hampshire_drv);
|
||||
|
@ -189,19 +189,4 @@ static struct serio_driver inexio_drv = {
|
||||
.disconnect = inexio_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init inexio_init(void)
|
||||
{
|
||||
return serio_register_driver(&inexio_drv);
|
||||
}
|
||||
|
||||
static void __exit inexio_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&inexio_drv);
|
||||
}
|
||||
|
||||
module_init(inexio_init);
|
||||
module_exit(inexio_exit);
|
||||
module_serio_driver(inexio_drv);
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/*
|
||||
* Touchscreen controller register offsets
|
||||
@ -383,6 +384,14 @@ static const struct dev_pm_ops lpc32xx_ts_pm_ops = {
|
||||
#define LPC32XX_TS_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct of_device_id lpc32xx_tsc_of_match[] = {
|
||||
{ .compatible = "nxp,lpc3220-tsc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lpc32xx_tsc_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver lpc32xx_ts_driver = {
|
||||
.probe = lpc32xx_ts_probe,
|
||||
.remove = __devexit_p(lpc32xx_ts_remove),
|
||||
@ -390,6 +399,7 @@ static struct platform_driver lpc32xx_ts_driver = {
|
||||
.name = MOD_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = LPC32XX_TS_PM_OPS,
|
||||
.of_match_table = of_match_ptr(lpc32xx_tsc_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(lpc32xx_ts_driver);
|
||||
|
@ -202,19 +202,4 @@ static struct serio_driver mtouch_drv = {
|
||||
.disconnect = mtouch_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init mtouch_init(void)
|
||||
{
|
||||
return serio_register_driver(&mtouch_drv);
|
||||
}
|
||||
|
||||
static void __exit mtouch_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&mtouch_drv);
|
||||
}
|
||||
|
||||
module_init(mtouch_init);
|
||||
module_exit(mtouch_exit);
|
||||
module_serio_driver(mtouch_drv);
|
||||
|
@ -317,19 +317,4 @@ static struct serio_driver pm_drv = {
|
||||
.disconnect = pm_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init pm_init(void)
|
||||
{
|
||||
return serio_register_driver(&pm_drv);
|
||||
}
|
||||
|
||||
static void __exit pm_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&pm_drv);
|
||||
}
|
||||
|
||||
module_init(pm_init);
|
||||
module_exit(pm_exit);
|
||||
module_serio_driver(pm_drv);
|
||||
|
@ -218,7 +218,7 @@ static int __devexit st1232_ts_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int st1232_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
@ -243,18 +243,25 @@ static int st1232_ts_resume(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops st1232_ts_pm_ops = {
|
||||
.suspend = st1232_ts_suspend,
|
||||
.resume = st1232_ts_resume,
|
||||
};
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
|
||||
st1232_ts_suspend, st1232_ts_resume);
|
||||
|
||||
static const struct i2c_device_id st1232_ts_id[] = {
|
||||
{ ST1232_TS_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st1232_ts_dt_ids[] __devinitconst = {
|
||||
{ .compatible = "sitronix,st1232", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver st1232_ts_driver = {
|
||||
.probe = st1232_ts_probe,
|
||||
.remove = __devexit_p(st1232_ts_remove),
|
||||
@ -262,9 +269,8 @@ static struct i2c_driver st1232_ts_driver = {
|
||||
.driver = {
|
||||
.name = ST1232_TS_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.of_match_table = of_match_ptr(st1232_ts_dt_ids),
|
||||
.pm = &st1232_ts_pm_ops,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -216,19 +216,4 @@ static struct serio_driver touchit213_drv = {
|
||||
.disconnect = touchit213_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init touchit213_init(void)
|
||||
{
|
||||
return serio_register_driver(&touchit213_drv);
|
||||
}
|
||||
|
||||
static void __exit touchit213_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&touchit213_drv);
|
||||
}
|
||||
|
||||
module_init(touchit213_init);
|
||||
module_exit(touchit213_exit);
|
||||
module_serio_driver(touchit213_drv);
|
||||
|
@ -176,19 +176,4 @@ static struct serio_driver tr_drv = {
|
||||
.disconnect = tr_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init tr_init(void)
|
||||
{
|
||||
return serio_register_driver(&tr_drv);
|
||||
}
|
||||
|
||||
static void __exit tr_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&tr_drv);
|
||||
}
|
||||
|
||||
module_init(tr_init);
|
||||
module_exit(tr_exit);
|
||||
module_serio_driver(tr_drv);
|
||||
|
@ -183,19 +183,4 @@ static struct serio_driver tw_drv = {
|
||||
.disconnect = tw_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init tw_init(void)
|
||||
{
|
||||
return serio_register_driver(&tw_drv);
|
||||
}
|
||||
|
||||
static void __exit tw_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&tw_drv);
|
||||
}
|
||||
|
||||
module_init(tw_init);
|
||||
module_exit(tw_exit);
|
||||
module_serio_driver(tw_drv);
|
||||
|
@ -167,17 +167,7 @@ static struct serio_driver tsc_drv = {
|
||||
.disconnect = tsc_disconnect,
|
||||
};
|
||||
|
||||
static int __init tsc_ser_init(void)
|
||||
{
|
||||
return serio_register_driver(&tsc_drv);
|
||||
}
|
||||
module_init(tsc_ser_init);
|
||||
|
||||
static void __exit tsc_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&tsc_drv);
|
||||
}
|
||||
module_exit(tsc_exit);
|
||||
module_serio_driver(tsc_drv);
|
||||
|
||||
MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
|
282
drivers/input/touchscreen/wacom_i2c.c
Normal file
282
drivers/input/touchscreen/wacom_i2c.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* Wacom Penabled Driver for I2C
|
||||
*
|
||||
* Copyright (c) 2011 Tatsunosuke Tobita, Wacom.
|
||||
* <tobita.tatsunosuke@wacom.co.jp>
|
||||
*
|
||||
* 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 of 2 of the License,
|
||||
* or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define WACOM_CMD_QUERY0 0x04
|
||||
#define WACOM_CMD_QUERY1 0x00
|
||||
#define WACOM_CMD_QUERY2 0x33
|
||||
#define WACOM_CMD_QUERY3 0x02
|
||||
#define WACOM_CMD_THROW0 0x05
|
||||
#define WACOM_CMD_THROW1 0x00
|
||||
#define WACOM_QUERY_SIZE 19
|
||||
#define WACOM_RETRY_CNT 100
|
||||
|
||||
struct wacom_features {
|
||||
int x_max;
|
||||
int y_max;
|
||||
int pressure_max;
|
||||
char fw_version;
|
||||
};
|
||||
|
||||
struct wacom_i2c {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
u8 data[WACOM_QUERY_SIZE];
|
||||
};
|
||||
|
||||
static int wacom_query_device(struct i2c_client *client,
|
||||
struct wacom_features *features)
|
||||
{
|
||||
int ret;
|
||||
u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1,
|
||||
WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 };
|
||||
u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 };
|
||||
u8 data[WACOM_QUERY_SIZE];
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(cmd1),
|
||||
.buf = cmd1,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = sizeof(cmd2),
|
||||
.buf = cmd2,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(data),
|
||||
.buf = data,
|
||||
},
|
||||
};
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EIO;
|
||||
|
||||
features->x_max = get_unaligned_le16(&data[3]);
|
||||
features->y_max = get_unaligned_le16(&data[5]);
|
||||
features->pressure_max = get_unaligned_le16(&data[11]);
|
||||
features->fw_version = get_unaligned_le16(&data[13]);
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
"x_max:%d, y_max:%d, pressure:%d, fw:%d\n",
|
||||
features->x_max, features->y_max,
|
||||
features->pressure_max, features->fw_version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t wacom_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct wacom_i2c *wac_i2c = dev_id;
|
||||
struct input_dev *input = wac_i2c->input;
|
||||
u8 *data = wac_i2c->data;
|
||||
unsigned int x, y, pressure;
|
||||
unsigned char tsw, f1, f2, ers;
|
||||
int error;
|
||||
|
||||
error = i2c_master_recv(wac_i2c->client,
|
||||
wac_i2c->data, sizeof(wac_i2c->data));
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
tsw = data[3] & 0x01;
|
||||
ers = data[3] & 0x04;
|
||||
f1 = data[3] & 0x02;
|
||||
f2 = data[3] & 0x10;
|
||||
x = le16_to_cpup((__le16 *)&data[4]);
|
||||
y = le16_to_cpup((__le16 *)&data[6]);
|
||||
pressure = le16_to_cpup((__le16 *)&data[8]);
|
||||
|
||||
input_report_key(input, BTN_TOUCH, tsw || ers);
|
||||
input_report_key(input, BTN_TOOL_PEN, tsw);
|
||||
input_report_key(input, BTN_TOOL_RUBBER, ers);
|
||||
input_report_key(input, BTN_STYLUS, f1);
|
||||
input_report_key(input, BTN_STYLUS2, f2);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, pressure);
|
||||
input_sync(input);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int wacom_i2c_open(struct input_dev *dev)
|
||||
{
|
||||
struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
|
||||
struct i2c_client *client = wac_i2c->client;
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wacom_i2c_close(struct input_dev *dev)
|
||||
{
|
||||
struct wacom_i2c *wac_i2c = input_get_drvdata(dev);
|
||||
struct i2c_client *client = wac_i2c->client;
|
||||
|
||||
disable_irq(client->irq);
|
||||
}
|
||||
|
||||
static int __devinit wacom_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct wacom_i2c *wac_i2c;
|
||||
struct input_dev *input;
|
||||
struct wacom_features features;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "i2c_check_functionality error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
error = wacom_query_device(client, &features);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!wac_i2c || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
wac_i2c->client = client;
|
||||
wac_i2c->input = input;
|
||||
|
||||
input->name = "Wacom I2C Digitizer";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->id.vendor = 0x56a;
|
||||
input->id.version = features.fw_version;
|
||||
input->dev.parent = &client->dev;
|
||||
input->open = wacom_i2c_open;
|
||||
input->close = wacom_i2c_close;
|
||||
|
||||
input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
|
||||
__set_bit(BTN_TOOL_PEN, input->keybit);
|
||||
__set_bit(BTN_TOOL_RUBBER, input->keybit);
|
||||
__set_bit(BTN_STYLUS, input->keybit);
|
||||
__set_bit(BTN_STYLUS2, input->keybit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
|
||||
input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE,
|
||||
0, features.pressure_max, 0, 0);
|
||||
|
||||
input_set_drvdata(input, wac_i2c);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
"wacom_i2c", wac_i2c);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable IRQ, error: %d\n", error);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
/* Disable the IRQ, we'll enable it in wac_i2c_open() */
|
||||
disable_irq(client->irq);
|
||||
|
||||
error = input_register_device(wac_i2c->input);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register input device, error: %d\n", error);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, wac_i2c);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(client->irq, wac_i2c);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(wac_i2c);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit wacom_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct wacom_i2c *wac_i2c = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, wac_i2c);
|
||||
input_unregister_device(wac_i2c->input);
|
||||
kfree(wac_i2c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int wacom_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume);
|
||||
|
||||
static const struct i2c_device_id wacom_i2c_id[] = {
|
||||
{ "WAC_I2C_EMR", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, wacom_i2c_id);
|
||||
|
||||
static struct i2c_driver wacom_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wacom_i2c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &wacom_i2c_pm,
|
||||
},
|
||||
|
||||
.probe = wacom_i2c_probe,
|
||||
.remove = __devexit_p(wacom_i2c_remove),
|
||||
.id_table = wacom_i2c_id,
|
||||
};
|
||||
module_i2c_driver(wacom_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Tatsunosuke Tobita <tobita.tatsunosuke@wacom.co.jp>");
|
||||
MODULE_DESCRIPTION("WACOM EMR I2C Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -594,15 +594,4 @@ static struct serio_driver w8001_drv = {
|
||||
.disconnect = w8001_disconnect,
|
||||
};
|
||||
|
||||
static int __init w8001_init(void)
|
||||
{
|
||||
return serio_register_driver(&w8001_drv);
|
||||
}
|
||||
|
||||
static void __exit w8001_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&w8001_drv);
|
||||
}
|
||||
|
||||
module_init(w8001_init);
|
||||
module_exit(w8001_exit);
|
||||
module_serio_driver(w8001_drv);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user