forked from luck/tmp_suning_uos_patched
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - a new driver for STM FingerTip touchscreen - a new driver for D-Link DIR-685 touch keys - updated list of supported devices in xpad driver - other assorted updates and fixes * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (23 commits) MAINTAINERS: update input subsystem patterns Input: introduce KEY_ASSISTANT Input: xpad - sync supported devices with XBCD Input: xpad - sync supported devices with 360Controller Input: xen-kbdfront - use string constants from PV protocol Input: stmfts - mark all PM functions as __maybe_unused Input: add support for the STMicroelectronics FingerTip touchscreen Input: add D-Link DIR-685 touchkeys driver Input: s3c2410_ts - handle return value of clk_prepare_enable Input: axp20x-pek - add wakeup support Input: synaptics-rmi4 - use %phN to form F34 configuration ID Input: synaptics-rmi4 - change a char type to u8 Input: sparse-keymap - remove sparse_keymap_free() Input: tsc2007 - move header file out of I2C realm Input: mms114 - move header file out of I2C realm Input: mcs - move header file out of I2C realm Input: lm8323 - move header file out of I2C realm Input: elantech - force relative mode on a certain module Input: elan_i2c - add support for fetching chip type on newer hardware Input: elan_i2c - check if device is there before really probing ...
This commit is contained in:
commit
43d012099f
|
@ -0,0 +1,21 @@
|
|||
* D-Link DIR-685 Touchkeys
|
||||
|
||||
This is a I2C one-off touchkey controller based on the Cypress Semiconductor
|
||||
CY8C214 MCU with some firmware in its internal 8KB flash. The circuit
|
||||
board inside the router is named E119921.
|
||||
|
||||
The touchkey device node should be placed inside an I2C bus node.
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "dlink,dir685-touchkeys"
|
||||
- reg: the I2C address of the touchkeys
|
||||
- interrupts: reference to the interrupt number
|
||||
|
||||
Example:
|
||||
|
||||
touchkeys@26 {
|
||||
compatible = "dlink,dir685-touchkeys";
|
||||
reg = <0x26>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <17 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
|
@ -0,0 +1,43 @@
|
|||
* ST-Microelectronics FingerTip touchscreen controller
|
||||
|
||||
The ST-Microelectronics FingerTip device provides a basic touchscreen
|
||||
functionality. Along with it the user can enable the touchkey which can work as
|
||||
a basic HOME and BACK key for phones.
|
||||
|
||||
The driver supports also hovering as an absolute single touch event with x, y, z
|
||||
coordinates.
|
||||
|
||||
Required properties:
|
||||
- compatible : must be "st,stmfts"
|
||||
- reg : I2C slave address, (e.g. 0x49)
|
||||
- interrupt-parent : the phandle to the interrupt controller which provides
|
||||
the interrupt
|
||||
- interrupts : interrupt specification
|
||||
- avdd-supply : analogic power supply
|
||||
- vdd-supply : power supply
|
||||
- touchscreen-size-x : see touchscreen.txt
|
||||
- touchscreen-size-y : see touchscreen.txt
|
||||
|
||||
Optional properties:
|
||||
- touch-key-connected : specifies whether the touchkey feature is connected
|
||||
- ledvdd-supply : power supply to the touch key leds
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
|
||||
/* ... */
|
||||
|
||||
touchscreen@49 {
|
||||
compatible = "st,stmfts";
|
||||
reg = <0x49>;
|
||||
interrupt-parent = <&gpa1>;
|
||||
interrupts = <1 IRQ_TYPE_NONE>;
|
||||
touchscreen-size-x = <1599>;
|
||||
touchscreen-size-y = <2559>;
|
||||
touch-key-connected;
|
||||
avdd-supply = <&ldo30_reg>;
|
||||
vdd-supply = <&ldo31_reg>;
|
||||
ledvdd-supply = <&ldo33_reg>;
|
||||
};
|
||||
};
|
|
@ -3846,6 +3846,12 @@ S: Supported
|
|||
F: drivers/input/touchscreen/cyttsp*
|
||||
F: include/linux/input/cyttsp.h
|
||||
|
||||
D-LINK DIR-685 TOUCHKEYS DRIVER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Supported
|
||||
F: drivers/input/dlink-dir685-touchkeys.c
|
||||
|
||||
DALLAS/MAXIM DS1685-FAMILY REAL TIME CLOCK
|
||||
M: Joshua Kinard <kumba@gentoo.org>
|
||||
S: Maintained
|
||||
|
@ -6689,8 +6695,10 @@ S: Maintained
|
|||
F: drivers/input/
|
||||
F: include/linux/input.h
|
||||
F: include/uapi/linux/input.h
|
||||
F: include/uapi/linux/input-event-codes.h
|
||||
F: include/linux/input/
|
||||
F: Documentation/devicetree/bindings/input/
|
||||
F: Documentation/input/
|
||||
|
||||
INPUT MULTITOUCH (MT) PROTOCOL
|
||||
M: Henrik Rydberg <rydberg@bitmath.org>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include <linux/usb/r8a66597.h>
|
||||
#include <linux/usb/renesas_usbhs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
#include <linux/platform_data/tsc2007.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/sh_msiof.h>
|
||||
#include <linux/spi/mmc_spi.h>
|
||||
|
|
|
@ -481,7 +481,7 @@ EXPORT_SYMBOL(input_inject_event);
|
|||
void input_alloc_absinfo(struct input_dev *dev)
|
||||
{
|
||||
if (!dev->absinfo)
|
||||
dev->absinfo = kcalloc(ABS_CNT, sizeof(struct input_absinfo),
|
||||
dev->absinfo = kcalloc(ABS_CNT, sizeof(*dev->absinfo),
|
||||
GFP_KERNEL);
|
||||
|
||||
WARN(!dev->absinfo, "%s(): kcalloc() failed?\n", __func__);
|
||||
|
@ -1126,7 +1126,7 @@ static void input_seq_print_bitmap(struct seq_file *seq, const char *name,
|
|||
* If no output was produced print a single 0.
|
||||
*/
|
||||
if (skip_empty)
|
||||
seq_puts(seq, "0");
|
||||
seq_putc(seq, '0');
|
||||
|
||||
seq_putc(seq, '\n');
|
||||
}
|
||||
|
@ -1144,7 +1144,7 @@ static int input_devices_seq_show(struct seq_file *seq, void *v)
|
|||
seq_printf(seq, "P: Phys=%s\n", dev->phys ? dev->phys : "");
|
||||
seq_printf(seq, "S: Sysfs=%s\n", path ? path : "");
|
||||
seq_printf(seq, "U: Uniq=%s\n", dev->uniq ? dev->uniq : "");
|
||||
seq_printf(seq, "H: Handlers=");
|
||||
seq_puts(seq, "H: Handlers=");
|
||||
|
||||
list_for_each_entry(handle, &dev->h_list, d_node)
|
||||
seq_printf(seq, "%s ", handle->name);
|
||||
|
@ -1783,7 +1783,7 @@ struct input_dev *input_allocate_device(void)
|
|||
static atomic_t input_no = ATOMIC_INIT(-1);
|
||||
struct input_dev *dev;
|
||||
|
||||
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (dev) {
|
||||
dev->dev.type = &input_dev_type;
|
||||
dev->dev.class = &input_class;
|
||||
|
@ -1849,7 +1849,7 @@ struct input_dev *devm_input_allocate_device(struct device *dev)
|
|||
struct input_devres *devres;
|
||||
|
||||
devres = devres_alloc(devm_input_device_release,
|
||||
sizeof(struct input_devres), GFP_KERNEL);
|
||||
sizeof(*devres), GFP_KERNEL);
|
||||
if (!devres)
|
||||
return NULL;
|
||||
|
||||
|
@ -2099,7 +2099,7 @@ int input_register_device(struct input_dev *dev)
|
|||
|
||||
if (dev->devres_managed) {
|
||||
devres = devres_alloc(devm_input_device_unregister,
|
||||
sizeof(struct input_devres), GFP_KERNEL);
|
||||
sizeof(*devres), GFP_KERNEL);
|
||||
if (!devres)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
@ -126,13 +126,18 @@ static const struct xpad_device {
|
|||
u8 mapping;
|
||||
u8 xtype;
|
||||
} xpad_device[] = {
|
||||
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0x0f10, "Thrustmaster Modena GT Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x044f, 0xb326, "Thrustmaster Gamepad GP XID", 0, XTYPE_XBOX360 },
|
||||
{ 0x045e, 0x0202, "Microsoft X-Box pad v1 (US)", 0, XTYPE_XBOX },
|
||||
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },
|
||||
{ 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },
|
||||
{ 0x045e, 0x0288, "Microsoft Xbox Controller S v2", 0, XTYPE_XBOX },
|
||||
{ 0x045e, 0x0289, "Microsoft X-Box pad v2 (US)", 0, XTYPE_XBOX },
|
||||
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
|
||||
{ 0x045e, 0x028f, "Microsoft X-Box 360 pad v2", 0, XTYPE_XBOX360 },
|
||||
{ 0x045e, 0x0291, "Xbox 360 Wireless Receiver (XBOX)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
||||
{ 0x045e, 0x02d1, "Microsoft X-Box One pad", 0, XTYPE_XBOXONE },
|
||||
{ 0x045e, 0x02dd, "Microsoft X-Box One pad (Firmware 2015)", 0, XTYPE_XBOXONE },
|
||||
|
@ -145,28 +150,52 @@ static const struct xpad_device {
|
|||
{ 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x046d, 0xca88, "Logitech Compact Controller for Xbox", 0, XTYPE_XBOX },
|
||||
{ 0x046d, 0xca8a, "Logitech Precision Vibration Feedback Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x046d, 0xcaa3, "Logitech DriveFx Racing Wheel", 0, XTYPE_XBOX360 },
|
||||
{ 0x056e, 0x2004, "Elecom JC-U3613M", 0, XTYPE_XBOX360 },
|
||||
{ 0x05fd, 0x1007, "Mad Catz Controller (unverified)", 0, XTYPE_XBOX },
|
||||
{ 0x05fd, 0x107a, "InterAct 'PowerPad Pro' X-Box pad (Germany)", 0, XTYPE_XBOX },
|
||||
{ 0x05fe, 0x3030, "Chic Controller", 0, XTYPE_XBOX },
|
||||
{ 0x05fe, 0x3031, "Chic Controller", 0, XTYPE_XBOX },
|
||||
{ 0x062a, 0x0020, "Logic3 Xbox GamePad", 0, XTYPE_XBOX },
|
||||
{ 0x062a, 0x0033, "Competition Pro Steering Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x06a3, 0x0200, "Saitek Racing Wheel", 0, XTYPE_XBOX },
|
||||
{ 0x06a3, 0x0201, "Saitek Adrenalin", 0, XTYPE_XBOX },
|
||||
{ 0x06a3, 0xf51a, "Saitek P3600", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4506, "Mad Catz 4506 Wireless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4516, "Mad Catz Control Pad", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4520, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4522, "Mad Catz LumiCON", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4526, "Mad Catz Control Pad Pro", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4530, "Mad Catz Universal MC2 Racing Wheel and Pedals", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4536, "Mad Catz MicroCON", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4540, "Mad Catz Beat Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4556, "Mad Catz Lynx Wireless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4586, "Mad Catz MicroCon Wireless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4588, "Mad Catz Blaster", 0, XTYPE_XBOX },
|
||||
{ 0x0738, 0x45ff, "Mad Catz Beat Pad (w/ Handle)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4716, "Mad Catz Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4718, "Mad Catz Street Fighter IV FightStick SE", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4726, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4736, "Mad Catz MicroCon Gamepad", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4743, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x0738, 0x4758, "Mad Catz Arcade Game Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0x4a01, "Mad Catz FightStick TE 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x0738, 0x9871, "Mad Catz Portable Drum", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xb738, "Mad Catz MVC2TE Stick 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xcb02, "Saitek Cyborg Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xcb03, "Saitek P3200 Rumble Pad - PC/Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xcb29, "Saitek Aviator Stick AV8R02", 0, XTYPE_XBOX360 },
|
||||
{ 0x0738, 0xf738, "Super SFIV FightStick TE S", 0, XTYPE_XBOX360 },
|
||||
{ 0x07ff, 0xffff, "Mad Catz GamePad", 0, XTYPE_XBOX360 },
|
||||
{ 0x0c12, 0x0005, "Intec wireless", 0, XTYPE_XBOX },
|
||||
{ 0x0c12, 0x8801, "Nyko Xbox Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0c12, 0x8802, "Zeroplus Xbox Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX },
|
||||
{ 0x0c12, 0x880a, "Pelican Eclipse PL-2023", 0, XTYPE_XBOX },
|
||||
|
@ -174,37 +203,63 @@ static const struct xpad_device {
|
|||
{ 0x0c12, 0x9902, "HAMA VibraX - *FAULTY HARDWARE*", 0, XTYPE_XBOX },
|
||||
{ 0x0d2f, 0x0002, "Andamiro Pump It Up pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x0e4c, 0x1097, "Radica Gamester Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0e4c, 0x1103, "Radica Gamester Reflex", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x0e4c, 0x2390, "Radica Games Jtech Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0e4c, 0x3510, "Radica Gamester", 0, XTYPE_XBOX },
|
||||
{ 0x0e6f, 0x0003, "Logic3 Freebird wireless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0e6f, 0x0005, "Eclipse wireless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0e6f, 0x0006, "Edge wireless Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0e6f, 0x0008, "After Glow Pro Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0e6f, 0x0105, "HSM3 Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0113, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x011f, "Rock Candy Gamepad Wired Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0131, "PDP EA Sports Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0133, "Xbox 360 Wired Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0139, "Afterglow Prismatic Wired Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x013a, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0146, "Rock Candy Wired Controller for Xbox One", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0147, "PDP Marvel Xbox One Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x015c, "PDP Xbox One Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0161, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0162, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0163, "PDP Xbox One Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0164, "PDP Battlefield One", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0165, "PDP Titanfall 2", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0201, "Pelican PL-3601 'TSZ' Wired Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0213, "Afterglow Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x021f, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0246, "Rock Candy Gamepad for Xbox One 2015", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0301, "Logic3 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0346, "Rock Candy Gamepad for Xbox One 2016", 0, XTYPE_XBOXONE },
|
||||
{ 0x0e6f, 0x0401, "Logic3 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0413, "Afterglow AX.1 Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0x0501, "PDP Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e6f, 0xf900, "PDP Afterglow AX.1", 0, XTYPE_XBOX360 },
|
||||
{ 0x0e8f, 0x0201, "SmartJoy Frag Xpad/PS2 adaptor", 0, XTYPE_XBOX },
|
||||
{ 0x0e8f, 0x3008, "Generic xbox control (dealextreme)", 0, XTYPE_XBOX },
|
||||
{ 0x0f0d, 0x000a, "Hori Co. DOA4 FightStick", 0, XTYPE_XBOX360 },
|
||||
{ 0x0f0d, 0x000c, "Hori PadEX Turbo", 0, XTYPE_XBOX360 },
|
||||
{ 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0f0d, 0x001b, "Hori Real Arcade Pro VX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x0f0d, 0x0063, "Hori Real Arcade Pro Hayabusa (USA) Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0f0d, 0x0067, "HORIPAD ONE", 0, XTYPE_XBOXONE },
|
||||
{ 0x0f0d, 0x0078, "Hori Real Arcade Pro V Kai Xbox One", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x0f30, 0x010b, "Philips Recoil", 0, XTYPE_XBOX },
|
||||
{ 0x0f30, 0x0202, "Joytech Advanced Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0f30, 0x8888, "BigBen XBMiniPad Controller", 0, XTYPE_XBOX },
|
||||
{ 0x102c, 0xff0c, "Joytech Wireless Advanced Controller", 0, XTYPE_XBOX },
|
||||
{ 0x11c9, 0x55f0, "Nacon GC-100XF", 0, XTYPE_XBOX360 },
|
||||
{ 0x12ab, 0x0004, "Honey Bee Xbox360 dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x12ab, 0x0301, "PDP AFTERGLOW AX.1", 0, XTYPE_XBOX360 },
|
||||
{ 0x12ab, 0x0303, "Mortal Kombat Klassic FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x12ab, 0x8809, "Xbox DDR dancepad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x1430, 0x4748, "RedOctane Guitar Hero X-plorer", 0, XTYPE_XBOX360 },
|
||||
{ 0x1430, 0x8888, "TX6500+ Dance Pad (first generation)", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX },
|
||||
{ 0x1430, 0xf801, "RedOctane Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x146b, 0x0601, "BigBen Interactive XBOX 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x1532, 0x0037, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||
{ 0x1532, 0x0a00, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE },
|
||||
{ 0x1532, 0x0a03, "Razer Wildcat", 0, XTYPE_XBOXONE },
|
||||
{ 0x15e4, 0x3f00, "Power A Mini Pro Elite", 0, XTYPE_XBOX360 },
|
||||
{ 0x15e4, 0x3f0a, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
|
||||
|
@ -215,22 +270,44 @@ static const struct xpad_device {
|
|||
{ 0x1689, 0xfe00, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0x0002, "Harmonix Rock Band Guitar", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0x0130, "Ion Drum Rocker", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf016, "Mad Catz Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf018, "Mad Catz Street Fighter IV SE Fighting Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf019, "Mad Catz Brawlstick for Xbox 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf021, "Mad Cats Ghost Recon FS GamePad", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf023, "MLG Pro Circuit Controller (Xbox)", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf025, "Mad Catz Call Of Duty", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf027, "Mad Catz FPS Pro", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf028, "Street Fighter IV FightPad", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf02e, "Mad Catz Fightpad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf030, "Mad Catz Xbox 360 MC2 MicroCon Racing Wheel", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf036, "Mad Catz MicroCon GamePad Pro", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf038, "Street Fighter IV FightStick TE", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf039, "Mad Catz MvC2 TE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf03a, "Mad Catz SFxT Fightstick Pro", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf03d, "Street Fighter IV Arcade Stick TE - Chun Li", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf03e, "Mad Catz MLG FightStick TE", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf03f, "Mad Catz FightStick SoulCaliber", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf042, "Mad Catz FightStick TES+", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf080, "Mad Catz FightStick TE2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf501, "HoriPad EX2 Turbo", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf502, "Hori Real Arcade Pro.VX SA", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf503, "Hori Fighting Stick VX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf504, "Hori Real Arcade Pro. EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf505, "Hori Fighting Stick EX2B", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf506, "Hori Real Arcade Pro.EX Premium VLX", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf900, "Harmonix Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf901, "Gamestop Xbox 360 Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf903, "Tron Xbox 360 controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf904, "PDP Versus Fighting Pad", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xf906, "MortalKombat FightStick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xfa01, "MadCatz GamePad", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xfd00, "Razer Onza TE", 0, XTYPE_XBOX360 },
|
||||
{ 0x1bad, 0xfd01, "Razer Onza", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5000, "Razer Atrox Arcade Stick", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5300, "PowerA MINI PROEX Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5303, "Xbox Airflo wired controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x530a, "Xbox 360 Pro EX Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x531a, "PowerA Pro Ex", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5397, "FUS1ON Tournament Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x541a, "PowerA Xbox One Mini Wired Controller", 0, XTYPE_XBOXONE },
|
||||
|
@ -238,12 +315,19 @@ static const struct xpad_device {
|
|||
{ 0x24c6, 0x543a, "PowerA Xbox One wired controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x24c6, 0x5500, "Hori XBOX 360 EX 2 with Turbo", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5501, "Hori Real Arcade Pro VX-SA", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5502, "Hori Fighting Stick VX Alt", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5503, "Hori Fighting Edge", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5506, "Hori SOULCALIBUR V Stick", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x550d, "Hori GEM Xbox controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x550e, "Hori Real Arcade Pro V Kai 360", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x551a, "PowerA FUSION Pro Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x24c6, 0x561a, "PowerA FUSION Controller", 0, XTYPE_XBOXONE },
|
||||
{ 0x24c6, 0x5b00, "ThrustMaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5b02, "Thrustmaster, Inc. GPX Controller", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5b03, "Thrustmaster Ferrari 458 Racing Wheel", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0x5d04, "Razer Sabertooth", 0, XTYPE_XBOX360 },
|
||||
{ 0x24c6, 0xfafe, "Rock Candy Gamepad for Xbox 360", 0, XTYPE_XBOX360 },
|
||||
{ 0x3767, 0x0101, "Fanatec Speedster 3 Forceshock Wheel", 0, XTYPE_XBOX },
|
||||
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
||||
{ 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
|
||||
};
|
||||
|
@ -331,13 +415,16 @@ static struct usb_device_id xpad_table[] = {
|
|||
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
|
||||
XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */
|
||||
XPAD_XBOX360_VENDOR(0x056e), /* Elecom JC-U3613M */
|
||||
XPAD_XBOX360_VENDOR(0x06a3), /* Saitek P3600 */
|
||||
XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */
|
||||
{ USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */
|
||||
XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */
|
||||
XPAD_XBOX360_VENDOR(0x07ff), /* Mad Catz GamePad */
|
||||
XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x0e6f), /* 0x0e6f X-Box One controllers */
|
||||
XPAD_XBOX360_VENDOR(0x0f0d), /* Hori Controllers */
|
||||
XPAD_XBOXONE_VENDOR(0x0f0d), /* Hori Controllers */
|
||||
XPAD_XBOX360_VENDOR(0x11c9), /* Nacon GC100XF */
|
||||
XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */
|
||||
XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */
|
||||
XPAD_XBOX360_VENDOR(0x146b), /* BigBen Interactive Controllers */
|
||||
|
|
|
@ -178,6 +178,17 @@ config KEYBOARD_CLPS711X
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called clps711x-keypad.
|
||||
|
||||
config KEYBOARD_DLINK_DIR685
|
||||
tristate "D-Link DIR-685 touchkeys support"
|
||||
depends on I2C
|
||||
default ARCH_GEMINI
|
||||
help
|
||||
If you say yes here you get support for the D-Link DIR-685
|
||||
touchkeys.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called dlink-dir685-touchkeys.
|
||||
|
||||
config KEYBOARD_LKKBD
|
||||
tristate "DECstation/VAXstation LK201/LK401 keyboard"
|
||||
select SERIO
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
|
|||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
||||
obj-$(CONFIG_KEYBOARD_DLINK_DIR685) += dlink-dir685-touchkeys.o
|
||||
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o
|
||||
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
||||
|
|
155
drivers/input/keyboard/dlink-dir685-touchkeys.c
Normal file
155
drivers/input/keyboard/dlink-dir685-touchkeys.c
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* D-Link DIR-685 router I2C-based Touchkeys input driver
|
||||
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* This is a one-off touchkey controller based on the Cypress Semiconductor
|
||||
* CY8C214 MCU with some firmware in its internal 8KB flash. The circuit
|
||||
* board inside the router is named E119921
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
struct dir685_touchkeys {
|
||||
struct device *dev;
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
unsigned long cur_key;
|
||||
u16 codes[7];
|
||||
};
|
||||
|
||||
static irqreturn_t dir685_tk_irq_thread(int irq, void *data)
|
||||
{
|
||||
struct dir685_touchkeys *tk = data;
|
||||
const int num_bits = min_t(int, ARRAY_SIZE(tk->codes), 16);
|
||||
unsigned long changed;
|
||||
u8 buf[6];
|
||||
unsigned long key;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
err = i2c_master_recv(tk->client, buf, sizeof(buf));
|
||||
if (err != sizeof(buf)) {
|
||||
dev_err(tk->dev, "short read %d\n", err);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
dev_dbg(tk->dev, "IN: %*ph\n", (int)sizeof(buf), buf);
|
||||
key = be16_to_cpup((__be16 *) &buf[4]);
|
||||
|
||||
/* Figure out if any bits went high or low since last message */
|
||||
changed = tk->cur_key ^ key;
|
||||
for_each_set_bit(i, &changed, num_bits) {
|
||||
dev_dbg(tk->dev, "key %d is %s\n", i,
|
||||
test_bit(i, &key) ? "down" : "up");
|
||||
input_report_key(tk->input, tk->codes[i], test_bit(i, &key));
|
||||
}
|
||||
|
||||
/* Store currently down keys */
|
||||
tk->cur_key = key;
|
||||
input_sync(tk->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dir685_tk_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct dir685_touchkeys *tk;
|
||||
struct device *dev = &client->dev;
|
||||
u8 bl_data[] = { 0xa7, 0x40 };
|
||||
int err;
|
||||
int i;
|
||||
|
||||
tk = devm_kzalloc(&client->dev, sizeof(*tk), GFP_KERNEL);
|
||||
if (!tk)
|
||||
return -ENOMEM;
|
||||
|
||||
tk->input = devm_input_allocate_device(dev);
|
||||
if (!tk->input)
|
||||
return -ENOMEM;
|
||||
|
||||
tk->client = client;
|
||||
tk->dev = dev;
|
||||
|
||||
tk->input->keycodesize = sizeof(u16);
|
||||
tk->input->keycodemax = ARRAY_SIZE(tk->codes);
|
||||
tk->input->keycode = tk->codes;
|
||||
tk->codes[0] = KEY_UP;
|
||||
tk->codes[1] = KEY_DOWN;
|
||||
tk->codes[2] = KEY_LEFT;
|
||||
tk->codes[3] = KEY_RIGHT;
|
||||
tk->codes[4] = KEY_ENTER;
|
||||
tk->codes[5] = KEY_WPS_BUTTON;
|
||||
/*
|
||||
* This key appears in the vendor driver, but I have
|
||||
* not been able to activate it.
|
||||
*/
|
||||
tk->codes[6] = KEY_RESERVED;
|
||||
|
||||
__set_bit(EV_KEY, tk->input->evbit);
|
||||
for (i = 0; i < ARRAY_SIZE(tk->codes); i++)
|
||||
__set_bit(tk->codes[i], tk->input->keybit);
|
||||
__clear_bit(KEY_RESERVED, tk->input->keybit);
|
||||
|
||||
tk->input->name = "D-Link DIR-685 touchkeys";
|
||||
tk->input->id.bustype = BUS_I2C;
|
||||
|
||||
err = input_register_device(tk->input);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Set the brightness to max level */
|
||||
err = i2c_master_send(client, bl_data, sizeof(bl_data));
|
||||
if (err != sizeof(bl_data))
|
||||
dev_warn(tk->dev, "error setting brightness level\n");
|
||||
|
||||
if (!client->irq) {
|
||||
dev_err(dev, "no IRQ on the I2C device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
err = devm_request_threaded_irq(dev, client->irq,
|
||||
NULL, dir685_tk_irq_thread,
|
||||
IRQF_ONESHOT,
|
||||
"dir685-tk", tk);
|
||||
if (err) {
|
||||
dev_err(dev, "can't request IRQ\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dir685_tk_id[] = {
|
||||
{ "dir685tk", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dir685_tk_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id dir685_tk_of_match[] = {
|
||||
{ .compatible = "dlink,dir685-touchkeys" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dir685_tk_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver dir685_tk_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "dlin-dir685-touchkeys",
|
||||
.of_match_table = of_match_ptr(dir685_tk_of_match),
|
||||
},
|
||||
.probe = dir685_tk_probe,
|
||||
.id_table = dir685_tk_id,
|
||||
};
|
||||
module_i2c_driver(dir685_tk_i2c_driver);
|
||||
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||
MODULE_DESCRIPTION("D-Link DIR-685 touchkeys driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -30,8 +30,8 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/platform_data/lm8323.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/i2c/lm8323.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Commands to send to the chip. */
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/mcs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_data/mcs.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
/* MCS5000 Touchkey */
|
||||
|
|
|
@ -253,6 +253,9 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek,
|
|||
return error;
|
||||
}
|
||||
|
||||
if (axp20x_pek->axp20x->variant == AXP288_ID)
|
||||
enable_irq_wake(axp20x_pek->irq_dbr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -331,10 +334,35 @@ static int axp20x_pek_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused axp20x_pek_resume_noirq(struct device *dev)
|
||||
{
|
||||
struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
|
||||
|
||||
if (axp20x_pek->axp20x->variant != AXP288_ID)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Clear interrupts from button presses during suspend, to avoid
|
||||
* a wakeup power-button press getting reported to userspace.
|
||||
*/
|
||||
regmap_write(axp20x_pek->axp20x->regmap,
|
||||
AXP20X_IRQ1_STATE + AXP288_IRQ_POKN / 8,
|
||||
BIT(AXP288_IRQ_POKN % 8));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops axp20x_pek_pm_ops = {
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
.resume_noirq = axp20x_pek_resume_noirq,
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct platform_driver axp20x_pek_driver = {
|
||||
.probe = axp20x_pek_probe,
|
||||
.driver = {
|
||||
.name = "axp20x-pek",
|
||||
.pm = &axp20x_pek_pm_ops,
|
||||
},
|
||||
};
|
||||
module_platform_driver(axp20x_pek_driver);
|
||||
|
|
|
@ -135,14 +135,17 @@ static int xenkbd_probe(struct xenbus_device *dev,
|
|||
goto error_nomem;
|
||||
|
||||
/* Set input abs params to match backend screen res */
|
||||
abs = xenbus_read_unsigned(dev->otherend, "feature-abs-pointer", 0);
|
||||
ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend, "width",
|
||||
abs = xenbus_read_unsigned(dev->otherend,
|
||||
XENKBD_FIELD_FEAT_ABS_POINTER, 0);
|
||||
ptr_size[KPARAM_X] = xenbus_read_unsigned(dev->otherend,
|
||||
XENKBD_FIELD_WIDTH,
|
||||
ptr_size[KPARAM_X]);
|
||||
ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend, "height",
|
||||
ptr_size[KPARAM_Y] = xenbus_read_unsigned(dev->otherend,
|
||||
XENKBD_FIELD_HEIGHT,
|
||||
ptr_size[KPARAM_Y]);
|
||||
if (abs) {
|
||||
ret = xenbus_write(XBT_NIL, dev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
XENKBD_FIELD_REQ_ABS_POINTER, "1");
|
||||
if (ret) {
|
||||
pr_warn("xenkbd: can't request abs-pointer\n");
|
||||
abs = 0;
|
||||
|
@ -271,14 +274,15 @@ static int xenkbd_connect_backend(struct xenbus_device *dev,
|
|||
xenbus_dev_fatal(dev, ret, "starting transaction");
|
||||
goto error_irqh;
|
||||
}
|
||||
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
|
||||
ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_REF, "%lu",
|
||||
virt_to_gfn(info->page));
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
|
||||
ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_RING_GREF,
|
||||
"%u", info->gref);
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
|
||||
ret = xenbus_printf(xbt, dev->nodename, XENKBD_FIELD_EVT_CHANNEL, "%u",
|
||||
evtchn);
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
|
@ -353,7 +357,7 @@ static void xenkbd_backend_changed(struct xenbus_device *dev,
|
|||
}
|
||||
|
||||
static const struct xenbus_device_id xenkbd_ids[] = {
|
||||
{ "vkbd" },
|
||||
{ XENKBD_DRIVER_NAME },
|
||||
{ "" }
|
||||
};
|
||||
|
||||
|
@ -390,4 +394,4 @@ module_exit(xenkbd_cleanup);
|
|||
|
||||
MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("xen:vkbd");
|
||||
MODULE_ALIAS("xen:" XENKBD_DRIVER_NAME);
|
||||
|
|
|
@ -58,7 +58,7 @@ struct elan_transport_ops {
|
|||
|
||||
int (*get_version)(struct i2c_client *client, bool iap, u8 *version);
|
||||
int (*get_sm_version)(struct i2c_client *client,
|
||||
u8* ic_type, u8 *version);
|
||||
u16 *ic_type, u8 *version);
|
||||
int (*get_checksum)(struct i2c_client *client, bool iap, u16 *csum);
|
||||
int (*get_product_id)(struct i2c_client *client, u16 *id);
|
||||
|
||||
|
@ -82,6 +82,7 @@ struct elan_transport_ops {
|
|||
int (*get_report)(struct i2c_client *client, u8 *report);
|
||||
int (*get_pressure_adjustment)(struct i2c_client *client,
|
||||
int *adjustment);
|
||||
int (*get_pattern)(struct i2c_client *client, u8 *pattern);
|
||||
};
|
||||
|
||||
extern const struct elan_transport_ops elan_smbus_ops, elan_i2c_ops;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*
|
||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Author: KT Liao <kt.liao@emc.com.tw>
|
||||
* Version: 1.6.2
|
||||
* Version: 1.6.3
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
|
@ -41,7 +41,7 @@
|
|||
#include "elan_i2c.h"
|
||||
|
||||
#define DRIVER_NAME "elan_i2c"
|
||||
#define ELAN_DRIVER_VERSION "1.6.2"
|
||||
#define ELAN_DRIVER_VERSION "1.6.3"
|
||||
#define ELAN_VENDOR_ID 0x04f3
|
||||
#define ETP_MAX_PRESSURE 255
|
||||
#define ETP_FWIDTH_REDUCE 90
|
||||
|
@ -78,6 +78,7 @@ struct elan_tp_data {
|
|||
unsigned int x_res;
|
||||
unsigned int y_res;
|
||||
|
||||
u8 pattern;
|
||||
u16 product_id;
|
||||
u8 fw_version;
|
||||
u8 sm_version;
|
||||
|
@ -85,7 +86,7 @@ struct elan_tp_data {
|
|||
u16 fw_checksum;
|
||||
int pressure_adjustment;
|
||||
u8 mode;
|
||||
u8 ic_type;
|
||||
u16 ic_type;
|
||||
u16 fw_validpage_count;
|
||||
u16 fw_signature_address;
|
||||
|
||||
|
@ -96,10 +97,10 @@ struct elan_tp_data {
|
|||
bool baseline_ready;
|
||||
};
|
||||
|
||||
static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count,
|
||||
static int elan_get_fwinfo(u16 ic_type, u16 *validpage_count,
|
||||
u16 *signature_address)
|
||||
{
|
||||
switch (iap_version) {
|
||||
switch (ic_type) {
|
||||
case 0x00:
|
||||
case 0x06:
|
||||
case 0x08:
|
||||
|
@ -119,6 +120,9 @@ static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count,
|
|||
case 0x0E:
|
||||
*validpage_count = 640;
|
||||
break;
|
||||
case 0x10:
|
||||
*validpage_count = 1024;
|
||||
break;
|
||||
default:
|
||||
/* unknown ic type clear value */
|
||||
*validpage_count = 0;
|
||||
|
@ -305,6 +309,7 @@ static int elan_initialize(struct elan_tp_data *data)
|
|||
static int elan_query_device_info(struct elan_tp_data *data)
|
||||
{
|
||||
int error;
|
||||
u16 ic_type;
|
||||
|
||||
error = data->ops->get_version(data->client, false, &data->fw_version);
|
||||
if (error)
|
||||
|
@ -324,7 +329,16 @@ static int elan_query_device_info(struct elan_tp_data *data)
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = elan_get_fwinfo(data->iap_version, &data->fw_validpage_count,
|
||||
error = data->ops->get_pattern(data->client, &data->pattern);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (data->pattern == 0x01)
|
||||
ic_type = data->ic_type;
|
||||
else
|
||||
ic_type = data->iap_version;
|
||||
|
||||
error = elan_get_fwinfo(ic_type, &data->fw_validpage_count,
|
||||
&data->fw_signature_address);
|
||||
if (error)
|
||||
dev_warn(&data->client->dev,
|
||||
|
@ -1077,6 +1091,13 @@ static int elan_probe(struct i2c_client *client,
|
|||
return error;
|
||||
}
|
||||
|
||||
/* Make sure there is something at this address */
|
||||
error = i2c_smbus_read_byte(client);
|
||||
if (error < 0) {
|
||||
dev_dbg(&client->dev, "nothing at this address: %d\n", error);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Initialize the touchpad. */
|
||||
error = elan_initialize(data);
|
||||
if (error)
|
||||
|
@ -1101,10 +1122,13 @@ static int elan_probe(struct i2c_client *client,
|
|||
"Elan Touchpad Extra Information:\n"
|
||||
" Max ABS X,Y: %d,%d\n"
|
||||
" Width X,Y: %d,%d\n"
|
||||
" Resolution X,Y: %d,%d (dots/mm)\n",
|
||||
" Resolution X,Y: %d,%d (dots/mm)\n"
|
||||
" ic type: 0x%x\n"
|
||||
" info pattern: 0x%x\n",
|
||||
data->max_x, data->max_y,
|
||||
data->width_x, data->width_y,
|
||||
data->x_res, data->y_res);
|
||||
data->x_res, data->y_res,
|
||||
data->ic_type, data->pattern);
|
||||
|
||||
/* Set up input device properties based on queried parameters. */
|
||||
error = elan_setup_input_device(data);
|
||||
|
|
|
@ -34,9 +34,12 @@
|
|||
#define ETP_I2C_DESC_CMD 0x0001
|
||||
#define ETP_I2C_REPORT_DESC_CMD 0x0002
|
||||
#define ETP_I2C_STAND_CMD 0x0005
|
||||
#define ETP_I2C_PATTERN_CMD 0x0100
|
||||
#define ETP_I2C_UNIQUEID_CMD 0x0101
|
||||
#define ETP_I2C_FW_VERSION_CMD 0x0102
|
||||
#define ETP_I2C_SM_VERSION_CMD 0x0103
|
||||
#define ETP_I2C_IC_TYPE_CMD 0x0103
|
||||
#define ETP_I2C_OSM_VERSION_CMD 0x0103
|
||||
#define ETP_I2C_NSM_VERSION_CMD 0x0104
|
||||
#define ETP_I2C_XY_TRACENUM_CMD 0x0105
|
||||
#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
|
||||
#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
|
||||
|
@ -239,12 +242,34 @@ static int elan_i2c_get_baseline_data(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int elan_i2c_get_pattern(struct i2c_client *client, u8 *pattern)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
|
||||
error = elan_i2c_read_cmd(client, ETP_I2C_PATTERN_CMD, val);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get pattern: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
*pattern = val[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elan_i2c_get_version(struct i2c_client *client,
|
||||
bool iap, u8 *version)
|
||||
{
|
||||
int error;
|
||||
u8 pattern_ver;
|
||||
u8 val[3];
|
||||
|
||||
error = elan_i2c_get_pattern(client, &pattern_ver);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get pattern version\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = elan_i2c_read_cmd(client,
|
||||
iap ? ETP_I2C_IAP_VERSION_CMD :
|
||||
ETP_I2C_FW_VERSION_CMD,
|
||||
|
@ -255,24 +280,54 @@ static int elan_i2c_get_version(struct i2c_client *client,
|
|||
return error;
|
||||
}
|
||||
|
||||
*version = val[0];
|
||||
if (pattern_ver == 0x01)
|
||||
*version = iap ? val[1] : val[0];
|
||||
else
|
||||
*version = val[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int elan_i2c_get_sm_version(struct i2c_client *client,
|
||||
u8 *ic_type, u8 *version)
|
||||
u16 *ic_type, u8 *version)
|
||||
{
|
||||
int error;
|
||||
u8 pattern_ver;
|
||||
u8 val[3];
|
||||
|
||||
error = elan_i2c_read_cmd(client, ETP_I2C_SM_VERSION_CMD, val);
|
||||
error = elan_i2c_get_pattern(client, &pattern_ver);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get SM version: %d\n", error);
|
||||
dev_err(&client->dev, "failed to get pattern version\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
*version = val[0];
|
||||
*ic_type = val[1];
|
||||
if (pattern_ver == 0x01) {
|
||||
error = elan_i2c_read_cmd(client, ETP_I2C_IC_TYPE_CMD, val);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get ic type: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
*ic_type = be16_to_cpup((__be16 *)val);
|
||||
|
||||
error = elan_i2c_read_cmd(client, ETP_I2C_NSM_VERSION_CMD,
|
||||
val);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get SM version: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
*version = val[1];
|
||||
} else {
|
||||
error = elan_i2c_read_cmd(client, ETP_I2C_OSM_VERSION_CMD, val);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get SM version: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
*version = val[0];
|
||||
*ic_type = val[1];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -641,5 +696,7 @@ const struct elan_transport_ops elan_i2c_ops = {
|
|||
.write_fw_block = elan_i2c_write_fw_block,
|
||||
.finish_fw_update = elan_i2c_finish_fw_update,
|
||||
|
||||
.get_pattern = elan_i2c_get_pattern,
|
||||
|
||||
.get_report = elan_i2c_get_report,
|
||||
};
|
||||
|
|
|
@ -166,7 +166,7 @@ static int elan_smbus_get_version(struct i2c_client *client,
|
|||
}
|
||||
|
||||
static int elan_smbus_get_sm_version(struct i2c_client *client,
|
||||
u8 *ic_type, u8 *version)
|
||||
u16 *ic_type, u8 *version)
|
||||
{
|
||||
int error;
|
||||
u8 val[3];
|
||||
|
@ -495,6 +495,12 @@ static int elan_smbus_finish_fw_update(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int elan_smbus_get_pattern(struct i2c_client *client, u8 *pattern)
|
||||
{
|
||||
*pattern = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct elan_transport_ops elan_smbus_ops = {
|
||||
.initialize = elan_smbus_initialize,
|
||||
.sleep_control = elan_smbus_sleep_control,
|
||||
|
@ -524,4 +530,5 @@ const struct elan_transport_ops elan_smbus_ops = {
|
|||
.finish_fw_update = elan_smbus_finish_fw_update,
|
||||
|
||||
.get_report = elan_smbus_get_report,
|
||||
.get_pattern = elan_smbus_get_pattern,
|
||||
};
|
||||
|
|
|
@ -1711,6 +1711,17 @@ int elantech_init(struct psmouse *psmouse)
|
|||
etd->samples[0], etd->samples[1], etd->samples[2]);
|
||||
}
|
||||
|
||||
if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) {
|
||||
/*
|
||||
* This module has a bug which makes absolute mode
|
||||
* unusable, so let's abort so we'll be using standard
|
||||
* PS/2 protocol.
|
||||
*/
|
||||
psmouse_info(psmouse,
|
||||
"absolute mode broken, forcing standard PS/2 protocol\n");
|
||||
goto init_fail;
|
||||
}
|
||||
|
||||
if (elantech_set_absolute_mode(psmouse)) {
|
||||
psmouse_err(psmouse,
|
||||
"failed to put touchpad into absolute mode.\n");
|
||||
|
|
|
@ -9,13 +9,14 @@
|
|||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/rmi.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include "rmi_driver.h"
|
||||
#include "rmi_f34.h"
|
||||
|
@ -464,7 +465,7 @@ static int rmi_f34v7_read_queries_bl_version(struct f34_data *f34)
|
|||
static int rmi_f34v7_read_queries(struct f34_data *f34)
|
||||
{
|
||||
int ret;
|
||||
int i, j;
|
||||
int i;
|
||||
u8 base;
|
||||
int offset;
|
||||
u8 *ptable;
|
||||
|
@ -518,10 +519,7 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
|
|||
query_1_7.partition_support[1] & HAS_GUEST_CODE;
|
||||
|
||||
if (query_0 & HAS_CONFIG_ID) {
|
||||
char f34_ctrl[CONFIG_ID_SIZE];
|
||||
int i = 0;
|
||||
u8 *p = f34->configuration_id;
|
||||
*p = '\0';
|
||||
u8 f34_ctrl[CONFIG_ID_SIZE];
|
||||
|
||||
ret = rmi_read_block(f34->fn->rmi_dev,
|
||||
f34->fn->fd.control_base_addr,
|
||||
|
@ -531,13 +529,11 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
|
|||
return ret;
|
||||
|
||||
/* Eat leading zeros */
|
||||
while (i < sizeof(f34_ctrl) && !f34_ctrl[i])
|
||||
i++;
|
||||
for (i = 0; i < sizeof(f34_ctrl) - 1 && !f34_ctrl[i]; i++)
|
||||
/* Empty */;
|
||||
|
||||
for (; i < sizeof(f34_ctrl); i++)
|
||||
p += snprintf(p, f34->configuration_id
|
||||
+ sizeof(f34->configuration_id) - p,
|
||||
"%02X", f34_ctrl[i]);
|
||||
snprintf(f34->configuration_id, sizeof(f34->configuration_id),
|
||||
"%*phN", (int)sizeof(f34_ctrl) - i, f34_ctrl + i);
|
||||
|
||||
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "Configuration ID: %s\n",
|
||||
f34->configuration_id);
|
||||
|
@ -545,9 +541,7 @@ static int rmi_f34v7_read_queries(struct f34_data *f34)
|
|||
|
||||
f34->v7.partitions = 0;
|
||||
for (i = 0; i < sizeof(query_1_7.partition_support); i++)
|
||||
for (j = 0; j < 8; j++)
|
||||
if (query_1_7.partition_support[i] & (1 << j))
|
||||
f34->v7.partitions++;
|
||||
f34->v7.partitions += hweight8(query_1_7.partition_support[i]);
|
||||
|
||||
rmi_dbg(RMI_DEBUG_FN, &f34->fn->dev, "%s: Supported partitions: %*ph\n",
|
||||
__func__, sizeof(query_1_7.partition_support),
|
||||
|
|
|
@ -223,20 +223,6 @@ int sparse_keymap_setup(struct input_dev *dev,
|
|||
}
|
||||
EXPORT_SYMBOL(sparse_keymap_setup);
|
||||
|
||||
/**
|
||||
* sparse_keymap_free - free memory allocated for sparse keymap
|
||||
* @dev: Input device using sparse keymap
|
||||
*
|
||||
* This function used to free memory allocated by sparse keymap
|
||||
* in an input device that was set up by sparse_keymap_setup().
|
||||
* Since sparse_keymap_setup() now uses a managed allocation for the
|
||||
* keymap copy, use of this function is deprecated.
|
||||
*/
|
||||
void sparse_keymap_free(struct input_dev *dev)
|
||||
{
|
||||
}
|
||||
EXPORT_SYMBOL(sparse_keymap_free);
|
||||
|
||||
/**
|
||||
* sparse_keymap_report_entry - report event corresponding to given key entry
|
||||
* @dev: Input device for which event should be reported
|
||||
|
|
|
@ -1114,6 +1114,17 @@ config TOUCHSCREEN_ST1232
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called st1232_ts.
|
||||
|
||||
config TOUCHSCREEN_STMFTS
|
||||
tristate "STMicroelectronics STMFTS touchscreen"
|
||||
depends on I2C
|
||||
depends on LEDS_CLASS
|
||||
help
|
||||
Say Y here if you want support for STMicroelectronics
|
||||
STMFTS touchscreen.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stmfts.
|
||||
|
||||
config TOUCHSCREEN_STMPE
|
||||
tristate "STMicroelectronics STMPE touchscreens"
|
||||
depends on MFD_STMPE
|
||||
|
|
|
@ -67,6 +67,7 @@ obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_SILEAD) += silead.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SIS_I2C) += sis_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_STMFTS) += stmfts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SUN4I) += sun4i-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/mcs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_data/mcs.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* Registers */
|
||||
|
|
|
@ -11,9 +11,9 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/mms114.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_data/mms114.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
|
|
|
@ -264,7 +264,11 @@ static int s3c2410ts_probe(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
clk_prepare_enable(ts.clock);
|
||||
ret = clk_prepare_enable(ts.clock);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed! to enabled clocks\n");
|
||||
goto err_clk_get;
|
||||
}
|
||||
dev_dbg(dev, "got and enabled clocks\n");
|
||||
|
||||
ts.irq_tc = ret = platform_get_irq(pdev, 0);
|
||||
|
@ -353,7 +357,9 @@ static int s3c2410ts_probe(struct platform_device *pdev)
|
|||
err_iomap:
|
||||
iounmap(ts.io);
|
||||
err_clk:
|
||||
clk_disable_unprepare(ts.clock);
|
||||
del_timer_sync(&touch_timer);
|
||||
err_clk_get:
|
||||
clk_put(ts.clock);
|
||||
return ret;
|
||||
}
|
||||
|
|
822
drivers/input/touchscreen/stmfts.c
Normal file
822
drivers/input/touchscreen/stmfts.c
Normal file
|
@ -0,0 +1,822 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
|
||||
* Author: Andi Shyti <andi.shyti@samsung.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.
|
||||
*
|
||||
* STMicroelectronics FTS Touchscreen device driver
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* I2C commands */
|
||||
#define STMFTS_READ_INFO 0x80
|
||||
#define STMFTS_READ_STATUS 0x84
|
||||
#define STMFTS_READ_ONE_EVENT 0x85
|
||||
#define STMFTS_READ_ALL_EVENT 0x86
|
||||
#define STMFTS_LATEST_EVENT 0x87
|
||||
#define STMFTS_SLEEP_IN 0x90
|
||||
#define STMFTS_SLEEP_OUT 0x91
|
||||
#define STMFTS_MS_MT_SENSE_OFF 0x92
|
||||
#define STMFTS_MS_MT_SENSE_ON 0x93
|
||||
#define STMFTS_SS_HOVER_SENSE_OFF 0x94
|
||||
#define STMFTS_SS_HOVER_SENSE_ON 0x95
|
||||
#define STMFTS_MS_KEY_SENSE_OFF 0x9a
|
||||
#define STMFTS_MS_KEY_SENSE_ON 0x9b
|
||||
#define STMFTS_SYSTEM_RESET 0xa0
|
||||
#define STMFTS_CLEAR_EVENT_STACK 0xa1
|
||||
#define STMFTS_FULL_FORCE_CALIBRATION 0xa2
|
||||
#define STMFTS_MS_CX_TUNING 0xa3
|
||||
#define STMFTS_SS_CX_TUNING 0xa4
|
||||
|
||||
/* events */
|
||||
#define STMFTS_EV_NO_EVENT 0x00
|
||||
#define STMFTS_EV_MULTI_TOUCH_DETECTED 0x02
|
||||
#define STMFTS_EV_MULTI_TOUCH_ENTER 0x03
|
||||
#define STMFTS_EV_MULTI_TOUCH_LEAVE 0x04
|
||||
#define STMFTS_EV_MULTI_TOUCH_MOTION 0x05
|
||||
#define STMFTS_EV_HOVER_ENTER 0x07
|
||||
#define STMFTS_EV_HOVER_LEAVE 0x08
|
||||
#define STMFTS_EV_HOVER_MOTION 0x09
|
||||
#define STMFTS_EV_KEY_STATUS 0x0e
|
||||
#define STMFTS_EV_ERROR 0x0f
|
||||
#define STMFTS_EV_CONTROLLER_READY 0x10
|
||||
#define STMFTS_EV_SLEEP_OUT_CONTROLLER_READY 0x11
|
||||
#define STMFTS_EV_STATUS 0x16
|
||||
#define STMFTS_EV_DEBUG 0xdb
|
||||
|
||||
/* multi touch related event masks */
|
||||
#define STMFTS_MASK_EVENT_ID 0x0f
|
||||
#define STMFTS_MASK_TOUCH_ID 0xf0
|
||||
#define STMFTS_MASK_LEFT_EVENT 0x0f
|
||||
#define STMFTS_MASK_X_MSB 0x0f
|
||||
#define STMFTS_MASK_Y_LSB 0xf0
|
||||
|
||||
/* key related event masks */
|
||||
#define STMFTS_MASK_KEY_NO_TOUCH 0x00
|
||||
#define STMFTS_MASK_KEY_MENU 0x01
|
||||
#define STMFTS_MASK_KEY_BACK 0x02
|
||||
|
||||
#define STMFTS_EVENT_SIZE 8
|
||||
#define STMFTS_STACK_DEPTH 32
|
||||
#define STMFTS_DATA_MAX_SIZE (STMFTS_EVENT_SIZE * STMFTS_STACK_DEPTH)
|
||||
#define STMFTS_MAX_FINGERS 10
|
||||
#define STMFTS_DEV_NAME "stmfts"
|
||||
|
||||
enum stmfts_regulators {
|
||||
STMFTS_REGULATOR_VDD,
|
||||
STMFTS_REGULATOR_AVDD,
|
||||
};
|
||||
|
||||
struct stmfts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct led_classdev led_cdev;
|
||||
struct mutex mutex;
|
||||
|
||||
struct touchscreen_properties prop;
|
||||
|
||||
struct regulator_bulk_data regulators[2];
|
||||
|
||||
/*
|
||||
* Presence of ledvdd will be used also to check
|
||||
* whether the LED is supported.
|
||||
*/
|
||||
struct regulator *ledvdd;
|
||||
|
||||
u16 chip_id;
|
||||
u8 chip_ver;
|
||||
u16 fw_ver;
|
||||
u8 config_id;
|
||||
u8 config_ver;
|
||||
|
||||
u8 data[STMFTS_DATA_MAX_SIZE];
|
||||
|
||||
struct completion cmd_done;
|
||||
|
||||
bool use_key;
|
||||
bool led_status;
|
||||
bool hover_enabled;
|
||||
bool running;
|
||||
};
|
||||
|
||||
static void stmfts_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct stmfts_data *sdata = container_of(led_cdev,
|
||||
struct stmfts_data, led_cdev);
|
||||
int err;
|
||||
|
||||
if (value == sdata->led_status || !sdata->ledvdd)
|
||||
return;
|
||||
|
||||
if (!value) {
|
||||
regulator_disable(sdata->ledvdd);
|
||||
} else {
|
||||
err = regulator_enable(sdata->ledvdd);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to disable ledvdd regulator: %d\n",
|
||||
err);
|
||||
}
|
||||
|
||||
sdata->led_status = value;
|
||||
}
|
||||
|
||||
static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct stmfts_data *sdata = container_of(led_cdev,
|
||||
struct stmfts_data, led_cdev);
|
||||
|
||||
return !!regulator_is_enabled(sdata->ledvdd);
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't simply use i2c_smbus_read_i2c_block_data because we
|
||||
* need to read more than 255 bytes (
|
||||
*/
|
||||
static int stmfts_read_events(struct stmfts_data *sdata)
|
||||
{
|
||||
u8 cmd = STMFTS_READ_ALL_EVENT;
|
||||
struct i2c_msg msgs[2] = {
|
||||
{
|
||||
.addr = sdata->client->addr,
|
||||
.len = 1,
|
||||
.buf = &cmd,
|
||||
},
|
||||
{
|
||||
.addr = sdata->client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = STMFTS_DATA_MAX_SIZE,
|
||||
.buf = sdata->data,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = i2c_transfer(sdata->client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret == ARRAY_SIZE(msgs) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static void stmfts_report_contact_event(struct stmfts_data *sdata,
|
||||
const u8 event[])
|
||||
{
|
||||
u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4;
|
||||
u16 x = event[1] | ((event[2] & STMFTS_MASK_X_MSB) << 8);
|
||||
u16 y = (event[2] >> 4) | (event[3] << 4);
|
||||
u8 maj = event[4];
|
||||
u8 min = event[5];
|
||||
u8 orientation = event[6];
|
||||
u8 area = event[7];
|
||||
|
||||
input_mt_slot(sdata->input, slot_id);
|
||||
|
||||
input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, true);
|
||||
input_report_abs(sdata->input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(sdata->input, ABS_MT_POSITION_Y, y);
|
||||
input_report_abs(sdata->input, ABS_MT_TOUCH_MAJOR, maj);
|
||||
input_report_abs(sdata->input, ABS_MT_TOUCH_MINOR, min);
|
||||
input_report_abs(sdata->input, ABS_MT_PRESSURE, area);
|
||||
input_report_abs(sdata->input, ABS_MT_ORIENTATION, orientation);
|
||||
|
||||
input_sync(sdata->input);
|
||||
}
|
||||
|
||||
static void stmfts_report_contact_release(struct stmfts_data *sdata,
|
||||
const u8 event[])
|
||||
{
|
||||
u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4;
|
||||
|
||||
input_mt_slot(sdata->input, slot_id);
|
||||
input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, false);
|
||||
|
||||
input_sync(sdata->input);
|
||||
}
|
||||
|
||||
static void stmfts_report_hover_event(struct stmfts_data *sdata,
|
||||
const u8 event[])
|
||||
{
|
||||
u16 x = (event[2] << 4) | (event[4] >> 4);
|
||||
u16 y = (event[3] << 4) | (event[4] & STMFTS_MASK_Y_LSB);
|
||||
u8 z = event[5];
|
||||
|
||||
input_report_abs(sdata->input, ABS_X, x);
|
||||
input_report_abs(sdata->input, ABS_Y, y);
|
||||
input_report_abs(sdata->input, ABS_DISTANCE, z);
|
||||
|
||||
input_sync(sdata->input);
|
||||
}
|
||||
|
||||
static void stmfts_report_key_event(struct stmfts_data *sdata, const u8 event[])
|
||||
{
|
||||
switch (event[2]) {
|
||||
case 0:
|
||||
input_report_key(sdata->input, KEY_BACK, 0);
|
||||
input_report_key(sdata->input, KEY_MENU, 0);
|
||||
break;
|
||||
|
||||
case STMFTS_MASK_KEY_BACK:
|
||||
input_report_key(sdata->input, KEY_BACK, 1);
|
||||
break;
|
||||
|
||||
case STMFTS_MASK_KEY_MENU:
|
||||
input_report_key(sdata->input, KEY_MENU, 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_warn(&sdata->client->dev,
|
||||
"unknown key event: %#02x\n", event[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(sdata->input);
|
||||
}
|
||||
|
||||
static void stmfts_parse_events(struct stmfts_data *sdata)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < STMFTS_STACK_DEPTH; i++) {
|
||||
u8 *event = &sdata->data[i * STMFTS_EVENT_SIZE];
|
||||
|
||||
switch (event[0]) {
|
||||
|
||||
case STMFTS_EV_CONTROLLER_READY:
|
||||
case STMFTS_EV_SLEEP_OUT_CONTROLLER_READY:
|
||||
case STMFTS_EV_STATUS:
|
||||
complete(&sdata->cmd_done);
|
||||
/* fall through */
|
||||
|
||||
case STMFTS_EV_NO_EVENT:
|
||||
case STMFTS_EV_DEBUG:
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event[0] & STMFTS_MASK_EVENT_ID) {
|
||||
|
||||
case STMFTS_EV_MULTI_TOUCH_ENTER:
|
||||
case STMFTS_EV_MULTI_TOUCH_MOTION:
|
||||
stmfts_report_contact_event(sdata, event);
|
||||
break;
|
||||
|
||||
case STMFTS_EV_MULTI_TOUCH_LEAVE:
|
||||
stmfts_report_contact_release(sdata, event);
|
||||
break;
|
||||
|
||||
case STMFTS_EV_HOVER_ENTER:
|
||||
case STMFTS_EV_HOVER_LEAVE:
|
||||
case STMFTS_EV_HOVER_MOTION:
|
||||
stmfts_report_hover_event(sdata, event);
|
||||
break;
|
||||
|
||||
case STMFTS_EV_KEY_STATUS:
|
||||
stmfts_report_key_event(sdata, event);
|
||||
break;
|
||||
|
||||
case STMFTS_EV_ERROR:
|
||||
dev_warn(&sdata->client->dev,
|
||||
"error code: 0x%x%x%x%x%x%x",
|
||||
event[6], event[5], event[4],
|
||||
event[3], event[2], event[1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&sdata->client->dev,
|
||||
"unknown event %#02x\n", event[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t stmfts_irq_handler(int irq, void *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = dev;
|
||||
int err;
|
||||
|
||||
mutex_lock(&sdata->mutex);
|
||||
|
||||
err = stmfts_read_events(sdata);
|
||||
if (unlikely(err))
|
||||
dev_err(&sdata->client->dev,
|
||||
"failed to read events: %d\n", err);
|
||||
else
|
||||
stmfts_parse_events(sdata);
|
||||
|
||||
mutex_unlock(&sdata->mutex);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int stmfts_command(struct stmfts_data *sdata, const u8 cmd)
|
||||
{
|
||||
int err;
|
||||
|
||||
reinit_completion(&sdata->cmd_done);
|
||||
|
||||
err = i2c_smbus_write_byte(sdata->client, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!wait_for_completion_timeout(&sdata->cmd_done,
|
||||
msecs_to_jiffies(1000)))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmfts_input_open(struct input_dev *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = input_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = pm_runtime_get_sync(&sdata->client->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_ON);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&sdata->mutex);
|
||||
sdata->running = true;
|
||||
|
||||
if (sdata->hover_enabled) {
|
||||
err = i2c_smbus_write_byte(sdata->client,
|
||||
STMFTS_SS_HOVER_SENSE_ON);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to enable hover\n");
|
||||
}
|
||||
mutex_unlock(&sdata->mutex);
|
||||
|
||||
if (sdata->use_key) {
|
||||
err = i2c_smbus_write_byte(sdata->client,
|
||||
STMFTS_MS_KEY_SENSE_ON);
|
||||
if (err)
|
||||
/* I can still use only the touch screen */
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to enable touchkey\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stmfts_input_close(struct input_dev *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = input_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_write_byte(sdata->client, STMFTS_MS_MT_SENSE_OFF);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to disable touchscreen: %d\n", err);
|
||||
|
||||
mutex_lock(&sdata->mutex);
|
||||
|
||||
sdata->running = false;
|
||||
|
||||
if (sdata->hover_enabled) {
|
||||
err = i2c_smbus_write_byte(sdata->client,
|
||||
STMFTS_SS_HOVER_SENSE_OFF);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to disable hover: %d\n", err);
|
||||
}
|
||||
mutex_unlock(&sdata->mutex);
|
||||
|
||||
if (sdata->use_key) {
|
||||
err = i2c_smbus_write_byte(sdata->client,
|
||||
STMFTS_MS_KEY_SENSE_OFF);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to disable touchkey: %d\n", err);
|
||||
}
|
||||
|
||||
pm_runtime_put_sync(&sdata->client->dev);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_chip_id(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", sdata->chip_id);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_chip_version(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sdata->chip_ver);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_fw_ver(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sdata->fw_ver);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_config_id(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%#x\n", sdata->config_id);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_config_version(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sdata->config_ver);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_read_status(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
u8 status[4];
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_STATUS,
|
||||
sizeof(status), status);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return sprintf(buf, "%#02x\n", status[0]);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_hover_enable_read(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", sdata->hover_enabled);
|
||||
}
|
||||
|
||||
static ssize_t stmfts_sysfs_hover_enable_write(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
unsigned long value;
|
||||
int err = 0;
|
||||
|
||||
if (kstrtoul(buf, 0, &value))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&sdata->mutex);
|
||||
|
||||
if (value & sdata->hover_enabled)
|
||||
goto out;
|
||||
|
||||
if (sdata->running)
|
||||
err = i2c_smbus_write_byte(sdata->client,
|
||||
value ? STMFTS_SS_HOVER_SENSE_ON :
|
||||
STMFTS_SS_HOVER_SENSE_OFF);
|
||||
|
||||
if (!err)
|
||||
sdata->hover_enabled = !!value;
|
||||
|
||||
out:
|
||||
mutex_unlock(&sdata->mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(chip_id, 0444, stmfts_sysfs_chip_id, NULL);
|
||||
static DEVICE_ATTR(chip_version, 0444, stmfts_sysfs_chip_version, NULL);
|
||||
static DEVICE_ATTR(fw_ver, 0444, stmfts_sysfs_fw_ver, NULL);
|
||||
static DEVICE_ATTR(config_id, 0444, stmfts_sysfs_config_id, NULL);
|
||||
static DEVICE_ATTR(config_version, 0444, stmfts_sysfs_config_version, NULL);
|
||||
static DEVICE_ATTR(status, 0444, stmfts_sysfs_read_status, NULL);
|
||||
static DEVICE_ATTR(hover_enable, 0644, stmfts_sysfs_hover_enable_read,
|
||||
stmfts_sysfs_hover_enable_write);
|
||||
|
||||
static struct attribute *stmfts_sysfs_attrs[] = {
|
||||
&dev_attr_chip_id.attr,
|
||||
&dev_attr_chip_version.attr,
|
||||
&dev_attr_fw_ver.attr,
|
||||
&dev_attr_config_id.attr,
|
||||
&dev_attr_config_version.attr,
|
||||
&dev_attr_status.attr,
|
||||
&dev_attr_hover_enable.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group stmfts_attribute_group = {
|
||||
.attrs = stmfts_sysfs_attrs
|
||||
};
|
||||
|
||||
static int stmfts_power_on(struct stmfts_data *sdata)
|
||||
{
|
||||
int err;
|
||||
u8 reg[8];
|
||||
|
||||
err = regulator_bulk_enable(ARRAY_SIZE(sdata->regulators),
|
||||
sdata->regulators);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* The datasheet does not specify the power on time, but considering
|
||||
* that the reset time is < 10ms, I sleep 20ms to be sure
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
err = i2c_smbus_read_i2c_block_data(sdata->client, STMFTS_READ_INFO,
|
||||
sizeof(reg), reg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (err != sizeof(reg))
|
||||
return -EIO;
|
||||
|
||||
sdata->chip_id = be16_to_cpup((__be16 *)®[6]);
|
||||
sdata->chip_ver = reg[0];
|
||||
sdata->fw_ver = be16_to_cpup((__be16 *)®[2]);
|
||||
sdata->config_id = reg[4];
|
||||
sdata->config_ver = reg[5];
|
||||
|
||||
enable_irq(sdata->client->irq);
|
||||
|
||||
msleep(50);
|
||||
|
||||
err = stmfts_command(sdata, STMFTS_SYSTEM_RESET);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = stmfts_command(sdata, STMFTS_SLEEP_OUT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* optional tuning */
|
||||
err = stmfts_command(sdata, STMFTS_MS_CX_TUNING);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to perform mutual auto tune: %d\n", err);
|
||||
|
||||
/* optional tuning */
|
||||
err = stmfts_command(sdata, STMFTS_SS_CX_TUNING);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to perform self auto tune: %d\n", err);
|
||||
|
||||
err = stmfts_command(sdata, STMFTS_FULL_FORCE_CALIBRATION);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* At this point no one is using the touchscreen
|
||||
* and I don't really care about the return value
|
||||
*/
|
||||
(void) i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stmfts_power_off(void *data)
|
||||
{
|
||||
struct stmfts_data *sdata = data;
|
||||
|
||||
disable_irq(sdata->client->irq);
|
||||
regulator_bulk_disable(ARRAY_SIZE(sdata->regulators),
|
||||
sdata->regulators);
|
||||
}
|
||||
|
||||
/* This function is void because I don't want to prevent using the touch key
|
||||
* only because the LEDs don't get registered
|
||||
*/
|
||||
static int stmfts_enable_led(struct stmfts_data *sdata)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* get the regulator for powering the leds on */
|
||||
sdata->ledvdd = devm_regulator_get(&sdata->client->dev, "ledvdd");
|
||||
if (IS_ERR(sdata->ledvdd))
|
||||
return PTR_ERR(sdata->ledvdd);
|
||||
|
||||
sdata->led_cdev.name = STMFTS_DEV_NAME;
|
||||
sdata->led_cdev.max_brightness = LED_ON;
|
||||
sdata->led_cdev.brightness = LED_OFF;
|
||||
sdata->led_cdev.brightness_set = stmfts_brightness_set;
|
||||
sdata->led_cdev.brightness_get = stmfts_brightness_get;
|
||||
|
||||
err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev);
|
||||
if (err) {
|
||||
devm_regulator_put(sdata->ledvdd);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmfts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int err;
|
||||
struct stmfts_data *sdata;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
return -ENODEV;
|
||||
|
||||
sdata = devm_kzalloc(&client->dev, sizeof(*sdata), GFP_KERNEL);
|
||||
if (!sdata)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, sdata);
|
||||
|
||||
sdata->client = client;
|
||||
mutex_init(&sdata->mutex);
|
||||
init_completion(&sdata->cmd_done);
|
||||
|
||||
sdata->regulators[STMFTS_REGULATOR_VDD].supply = "vdd";
|
||||
sdata->regulators[STMFTS_REGULATOR_AVDD].supply = "avdd";
|
||||
err = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(sdata->regulators),
|
||||
sdata->regulators);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sdata->input = devm_input_allocate_device(&client->dev);
|
||||
if (!sdata->input)
|
||||
return -ENOMEM;
|
||||
|
||||
sdata->input->name = STMFTS_DEV_NAME;
|
||||
sdata->input->id.bustype = BUS_I2C;
|
||||
sdata->input->open = stmfts_input_open;
|
||||
sdata->input->close = stmfts_input_close;
|
||||
|
||||
touchscreen_parse_properties(sdata->input, true, &sdata->prop);
|
||||
|
||||
input_set_abs_params(sdata->input, ABS_MT_POSITION_X, 0,
|
||||
sdata->prop.max_x, 0, 0);
|
||||
input_set_abs_params(sdata->input, ABS_MT_POSITION_Y, 0,
|
||||
sdata->prop.max_y, 0, 0);
|
||||
input_set_abs_params(sdata->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
|
||||
input_set_abs_params(sdata->input, ABS_MT_TOUCH_MINOR, 0, 255, 0, 0);
|
||||
input_set_abs_params(sdata->input, ABS_MT_ORIENTATION, 0, 255, 0, 0);
|
||||
input_set_abs_params(sdata->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
|
||||
input_set_abs_params(sdata->input, ABS_DISTANCE, 0, 255, 0, 0);
|
||||
|
||||
sdata->use_key = device_property_read_bool(&client->dev,
|
||||
"touch-key-connected");
|
||||
if (sdata->use_key) {
|
||||
input_set_capability(sdata->input, EV_KEY, KEY_MENU);
|
||||
input_set_capability(sdata->input, EV_KEY, KEY_BACK);
|
||||
}
|
||||
|
||||
err = input_mt_init_slots(sdata->input,
|
||||
STMFTS_MAX_FINGERS, INPUT_MT_DIRECT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
input_set_drvdata(sdata->input, sdata);
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, stmfts_irq_handler,
|
||||
IRQF_ONESHOT,
|
||||
"stmfts_irq", sdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* stmfts_power_on expects interrupt to be disabled */
|
||||
disable_irq(client->irq);
|
||||
|
||||
dev_dbg(&client->dev, "initializing ST-Microelectronics FTS...\n");
|
||||
|
||||
err = stmfts_power_on(sdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = devm_add_action_or_reset(&client->dev, stmfts_power_off, sdata);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = input_register_device(sdata->input);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (sdata->use_key) {
|
||||
err = stmfts_enable_led(sdata);
|
||||
if (err) {
|
||||
/*
|
||||
* Even if the LEDs have failed to be initialized and
|
||||
* used in the driver, I can still use the device even
|
||||
* without LEDs. The ledvdd regulator pointer will be
|
||||
* used as a flag.
|
||||
*/
|
||||
dev_warn(&client->dev, "unable to use touchkey leds\n");
|
||||
sdata->ledvdd = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&sdata->client->dev.kobj,
|
||||
&stmfts_attribute_group);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pm_runtime_enable(&client->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stmfts_remove(struct i2c_client *client)
|
||||
{
|
||||
pm_runtime_disable(&client->dev);
|
||||
sysfs_remove_group(&client->dev.kobj, &stmfts_attribute_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stmfts_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_IN);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to suspend device: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused stmfts_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte(sdata->client, STMFTS_SLEEP_OUT);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to resume device: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __maybe_unused stmfts_suspend(struct device *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
stmfts_power_off(sdata);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused stmfts_resume(struct device *dev)
|
||||
{
|
||||
struct stmfts_data *sdata = dev_get_drvdata(dev);
|
||||
|
||||
return stmfts_power_on(sdata);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops stmfts_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(stmfts_suspend, stmfts_resume)
|
||||
SET_RUNTIME_PM_OPS(stmfts_runtime_suspend, stmfts_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id stmfts_of_match[] = {
|
||||
{ .compatible = "st,stmfts", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stmfts_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id stmfts_id[] = {
|
||||
{ "stmfts", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stmfts_id);
|
||||
|
||||
static struct i2c_driver stmfts_driver = {
|
||||
.driver = {
|
||||
.name = STMFTS_DEV_NAME,
|
||||
.of_match_table = of_match_ptr(stmfts_of_match),
|
||||
.pm = &stmfts_pm_ops,
|
||||
},
|
||||
.probe = stmfts_probe,
|
||||
.remove = stmfts_remove,
|
||||
.id_table = stmfts_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(stmfts_driver);
|
||||
|
||||
MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics FTS Touch Screen");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -25,9 +25,9 @@
|
|||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_data/tsc2007.h>
|
||||
#include "tsc2007.h"
|
||||
|
||||
int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)
|
||||
|
|
|
@ -32,13 +32,13 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-ocores.h>
|
||||
#include <linux/i2c-xiic.h>
|
||||
#include <linux/i2c/tsc2007.h>
|
||||
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/xilinx_spi.h>
|
||||
#include <linux/spi/max7301.h>
|
||||
#include <linux/spi/mc33880.h>
|
||||
|
||||
#include <linux/platform_data/tsc2007.h>
|
||||
#include <linux/platform_data/media/timb_radio.h>
|
||||
#include <linux/platform_data/media/timb_video.h>
|
||||
|
||||
|
|
|
@ -51,7 +51,6 @@ struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
|
|||
int sparse_keymap_setup(struct input_dev *dev,
|
||||
const struct key_entry *keymap,
|
||||
int (*setup)(struct input_dev *, struct key_entry *));
|
||||
void sparse_keymap_free(struct input_dev *dev);
|
||||
|
||||
void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
|
||||
unsigned int value, bool autorelease);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef __LINUX_I2C_TSC2007_H
|
||||
#define __LINUX_I2C_TSC2007_H
|
||||
|
||||
/* linux/i2c/tsc2007.h */
|
||||
/* linux/platform_data/tsc2007.h */
|
||||
|
||||
struct tsc2007_platform_data {
|
||||
u16 model; /* 2007. */
|
|
@ -600,6 +600,7 @@
|
|||
#define KEY_APPSELECT 0x244 /* AL Select Task/Application */
|
||||
#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */
|
||||
#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */
|
||||
#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */
|
||||
|
||||
#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */
|
||||
#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */
|
||||
|
|
Loading…
Reference in New Issue
Block a user