Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - big update to Wacom driver by Benjamin Tissoires, converting it to HID infrastructure and unifying USB and Bluetooth models - large update to ALPS driver by Hans de Goede, which adds support for newer touchpad models as well as cleans up and restructures the code - more changes to Atmel MXT driver, including device tree support - new driver for iPaq x3xxx touchscreen - driver for serial Wacom tablets - driver for Microchip's CAP1106 - assorted cleanups and improvements to existing drover and input core * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (93 commits) Input: wacom - update the ABI doc according to latest changes Input: wacom - only register once the MODULE_* macros Input: HID - remove hid-wacom Bluetooth driver Input: wacom - add copyright note and bump version to 2.0 Input: wacom - remove passing id for wacom_set_report Input: wacom - check for bluetooth protocol while setting OLEDs Input: wacom - handle Intuos 4 BT in wacom.ko Input: wacom - handle Graphire BT tablets in wacom.ko Input: wacom - prepare the driver to include BT devices Input: hyperv-keyboard - register as a wakeup source Input: imx_keypad - remove ifdef round PM methods Input: jornada720_ts - get rid of space indentation and use tab Input: jornada720_ts - switch to using managed resources Input: alps - Rushmore and v7 resolution support Input: mcs5000_ts - remove ifdef around power management methods Input: mcs5000_ts - protect PM functions with CONFIG_PM_SLEEP Input: ads7846 - release resources on failure for clean exit Input: wacom - add support for 0x12C ISDv4 sensor Input: atmel_mxt_ts - use deep sleep mode when stopped ARM: dts: am437x-gp-evm: Update binding for touchscreen size ...
This commit is contained in:
commit
664fb23070
@ -1,48 +1,27 @@
|
||||
WWhat: /sys/class/hidraw/hidraw*/device/oled*_img
|
||||
Date: June 2012
|
||||
Contact: linux-bluetooth@vger.kernel.org
|
||||
Description:
|
||||
The /sys/class/hidraw/hidraw*/device/oled*_img files control
|
||||
OLED mocro displays on Intuos4 Wireless tablet. Accepted image
|
||||
has to contain 256 bytes (64x32 px 1 bit colour). The format
|
||||
is the same as PBM image 62x32px without header (64 bits per
|
||||
horizontal line, 32 lines). An example of setting OLED No. 0:
|
||||
dd bs=256 count=1 if=img_file of=[path to oled0_img]/oled0_img
|
||||
The attribute is read only and no local copy of the image is
|
||||
stored.
|
||||
|
||||
What: /sys/class/hidraw/hidraw*/device/speed
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/speed
|
||||
Date: April 2010
|
||||
Kernel Version: 2.6.35
|
||||
Contact: linux-bluetooth@vger.kernel.org
|
||||
Description:
|
||||
The /sys/class/hidraw/hidraw*/device/speed file controls
|
||||
reporting speed of Wacom bluetooth tablet. Reading from
|
||||
this file returns 1 if tablet reports in high speed mode
|
||||
The /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/speed file
|
||||
controls reporting speed of Wacom bluetooth tablet. Reading
|
||||
from this file returns 1 if tablet reports in high speed mode
|
||||
or 0 otherwise. Writing to this file one of these values
|
||||
switches reporting speed.
|
||||
|
||||
What: /sys/class/leds/0005\:056A\:00BD.0001\:selector\:*/
|
||||
Date: May 2012
|
||||
Kernel Version: 3.5
|
||||
Contact: linux-bluetooth@vger.kernel.org
|
||||
Description:
|
||||
LED selector for Intuos4 WL. There are 4 leds, but only one LED
|
||||
can be lit at a time. Max brightness is 127.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/led
|
||||
Date: August 2011
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/led
|
||||
Date: August 2014
|
||||
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), 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.
|
||||
and XL (with LEDs and OLEDs), Intuos 4 WL, Intuos 5 (LEDs only),
|
||||
Intuos Pro (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
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status0_luminance
|
||||
Date: August 2014
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets the status LED luminance (1..127)
|
||||
@ -50,16 +29,16 @@ Description:
|
||||
button is pressed on the stylus. This luminance level is
|
||||
normally lower than the level when a button is pressed.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status1_luminance
|
||||
Date: August 2011
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status1_luminance
|
||||
Date: August 2014
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets the status LED luminance (1..127)
|
||||
when the stylus touches the tablet surface, or any button is
|
||||
pressed on the stylus.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/status_led0_select
|
||||
Date: August 2011
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led0_select
|
||||
Date: August 2014
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets which one of the four (for Intuos 4
|
||||
@ -67,23 +46,23 @@ Description:
|
||||
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
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/status_led1_select
|
||||
Date: August 2014
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets which one of the left four (for Cintiq 21UX2
|
||||
and Cintiq 24HD) status LEDs is active (0..3). The other three LEDs on
|
||||
the left are always inactive.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/buttons_luminance
|
||||
Date: August 2011
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/buttons_luminance
|
||||
Date: August 2014
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
Writing to this file sets the overall luminance level (0..15)
|
||||
of all eight button OLED displays.
|
||||
|
||||
What: /sys/bus/usb/devices/<busnum>-<devnum>:<cfg>.<intf>/wacom_led/button<n>_rawimg
|
||||
Date: August 2011
|
||||
What: /sys/bus/hid/devices/<bus>:<vid>:<pid>.<n>/wacom_led/button<n>_rawimg
|
||||
Date: August 2014
|
||||
Contact: linux-input@vger.kernel.org
|
||||
Description:
|
||||
When writing a 1024 byte raw image in Wacom Intuos 4
|
||||
@ -93,3 +72,8 @@ Description:
|
||||
byte chunk encodes the image data for two consecutive lines on
|
||||
the display. The low nibble of each byte contains the first
|
||||
line, and the high nibble contains the second line.
|
||||
When the Wacom Intuos 4 is connected over Bluetooth, the
|
||||
image has to contain 256 bytes (64x32 px 1 bit colour).
|
||||
The format is also scrambled, like in the USB mode, and it can
|
||||
be summarized by converting 76543210 into GECA6420.
|
||||
HGFEDCBA HFDB7531
|
||||
|
25
Documentation/devicetree/bindings/input/atmel,maxtouch.txt
Normal file
25
Documentation/devicetree/bindings/input/atmel,maxtouch.txt
Normal file
@ -0,0 +1,25 @@
|
||||
Atmel maXTouch touchscreen/touchpad
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
atmel,maxtouch
|
||||
|
||||
- reg: The I2C address of the device
|
||||
|
||||
- interrupts: The sink for the touchpad's IRQ output
|
||||
See ../interrupt-controller/interrupts.txt
|
||||
|
||||
Optional properties for main touchpad device:
|
||||
|
||||
- linux,gpio-keymap: An array of up to 4 entries indicating the Linux
|
||||
keycode generated by each GPIO. Linux keycodes are defined in
|
||||
<dt-bindings/input/input.h>.
|
||||
|
||||
Example:
|
||||
|
||||
touch@4b {
|
||||
compatible = "atmel,maxtouch";
|
||||
reg = <0x4b>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <TEGRA_GPIO(W, 3) IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
53
Documentation/devicetree/bindings/input/cap1106.txt
Normal file
53
Documentation/devicetree/bindings/input/cap1106.txt
Normal file
@ -0,0 +1,53 @@
|
||||
Device tree bindings for Microchip CAP1106, 6 channel capacitive touch sensor
|
||||
|
||||
The node for this driver must be a child of a I2C controller node, as the
|
||||
device communication via I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
compatible: Must be "microchip,cap1106"
|
||||
|
||||
reg: The I2C slave address of the device.
|
||||
Only 0x28 is valid.
|
||||
|
||||
interrupts: Property describing the interrupt line the
|
||||
device's ALERT#/CM_IRQ# pin is connected to.
|
||||
The device only has one interrupt source.
|
||||
|
||||
Optional properties:
|
||||
|
||||
autorepeat: Enables the Linux input system's autorepeat
|
||||
feature on the input device.
|
||||
|
||||
microchip,sensor-gain: Defines the gain of the sensor circuitry. This
|
||||
effectively controls the sensitivity, as a
|
||||
smaller delta capacitance is required to
|
||||
generate the same delta count values.
|
||||
Valid values are 1, 2, 4, and 8.
|
||||
By default, a gain of 1 is set.
|
||||
|
||||
linux,keycodes: Specifies an array of numeric keycode values to
|
||||
be used for the channels. If this property is
|
||||
omitted, KEY_A, KEY_B, etc are used as
|
||||
defaults. The array must have exactly six
|
||||
entries.
|
||||
|
||||
Example:
|
||||
|
||||
i2c_controller {
|
||||
cap1106@28 {
|
||||
compatible = "microchip,cap1106";
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <0 0>;
|
||||
reg = <0x28>;
|
||||
autorepeat;
|
||||
microchip,sensor-gain = <2>;
|
||||
|
||||
linux,keycodes = <103 /* KEY_UP */
|
||||
106 /* KEY_RIGHT */
|
||||
108 /* KEY_DOWN */
|
||||
105 /* KEY_LEFT */
|
||||
109 /* KEY_PAGEDOWN */
|
||||
104>; /* KEY_PAGEUP */
|
||||
};
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
* Pixcir I2C touchscreen controllers
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "pixcir,pixcir_ts" or "pixcir,pixcir_tangoc"
|
||||
- reg: I2C address of the chip
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
- attb-gpio: GPIO connected to the ATTB line of the chip
|
||||
- touchscreen-size-x: horizontal resolution of touchscreen (in pixels)
|
||||
- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
/* ... */
|
||||
|
||||
pixcir_ts@5c {
|
||||
compatible = "pixcir,pixcir_ts";
|
||||
reg = <0x5c>;
|
||||
interrupts = <2 0>;
|
||||
attb-gpio = <&gpf 2 0 2>;
|
||||
touchscreen-size-x = <800>;
|
||||
touchscreen-size-y = <600>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
@ -9,6 +9,9 @@ Required properties:
|
||||
- x-size: horizontal resolution of touchscreen
|
||||
- y-size: vertical resolution of touchscreen
|
||||
|
||||
Optional properties:
|
||||
- vdd-supply: Regulator controlling the controller supply
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
@ -18,6 +21,7 @@ Example:
|
||||
compatible = "neonode,zforce";
|
||||
reg = <0x50>;
|
||||
interrupts = <2 0>;
|
||||
vdd-supply = <®_zforce_vdd>;
|
||||
|
||||
gpios = <&gpio5 6 0>, /* INT */
|
||||
<&gpio5 9 0>; /* RST */
|
||||
|
@ -103,6 +103,7 @@ panasonic Panasonic Corporation
|
||||
phytec PHYTEC Messtechnik GmbH
|
||||
picochip Picochip Ltd
|
||||
plathome Plat'Home Co., Ltd.
|
||||
pixcir PIXCIR MICROELECTRONICS Co., Ltd
|
||||
powervr PowerVR (deprecated, use img)
|
||||
qca Qualcomm Atheros, Inc.
|
||||
qcom Qualcomm Technologies, Inc
|
||||
|
@ -9877,6 +9877,13 @@ M: Pierre Ossman <pierre@ossman.eu>
|
||||
S: Maintained
|
||||
F: drivers/mmc/host/wbsd.*
|
||||
|
||||
WACOM PROTOCOL 4 SERIAL TABLETS
|
||||
M: Julian Squires <julian@cipht.net>
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/input/tablet/wacom_serial4.c
|
||||
|
||||
WATCHDOG DEVICE DRIVERS
|
||||
M: Wim Van Sebroeck <wim@iguana.be>
|
||||
L: linux-watchdog@vger.kernel.org
|
||||
|
@ -334,8 +334,8 @@
|
||||
|
||||
attb-gpio = <&gpio3 22 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
x-size = <1024>;
|
||||
y-size = <600>;
|
||||
touchscreen-size-x = <1024>;
|
||||
touchscreen-size-y = <600>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -403,8 +403,8 @@
|
||||
|
||||
attb-gpio = <&gpio1 17 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
x-size = <1024>;
|
||||
y-size = <600>;
|
||||
touchscreen-size-x = <1024>;
|
||||
touchscreen-size-y = <600>;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -764,12 +764,17 @@ config THRUSTMASTER_FF
|
||||
Rumble Force or Force Feedback Wheel.
|
||||
|
||||
config HID_WACOM
|
||||
tristate "Wacom Bluetooth devices support"
|
||||
tristate "Wacom Intuos/Graphire tablet support (USB)"
|
||||
depends on HID
|
||||
depends on LEDS_CLASS
|
||||
select POWER_SUPPLY
|
||||
---help---
|
||||
Support for Wacom Graphire Bluetooth and Intuos4 WL tablets.
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
help
|
||||
Say Y here if you want to use the USB or BT version of the Wacom Intuos
|
||||
or Graphire tablet.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom.
|
||||
|
||||
config HID_WIIMOTE
|
||||
tristate "Nintendo Wii / Wii U peripherals"
|
||||
|
@ -116,7 +116,9 @@ obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
|
||||
obj-$(CONFIG_HID_XINMO) += hid-xinmo.o
|
||||
obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o
|
||||
obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
|
||||
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
|
||||
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
obj-$(CONFIG_HID_WACOM) += wacom.o
|
||||
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
|
||||
obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
|
||||
obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
|
||||
|
@ -789,6 +789,15 @@ static int hid_scan_report(struct hid_device *hid)
|
||||
/* hid-rmi should take care of them, not hid-generic */
|
||||
hid->group = HID_GROUP_RMI;
|
||||
|
||||
/*
|
||||
* Vendor specific handlings
|
||||
*/
|
||||
switch (hid->vendor) {
|
||||
case USB_VENDOR_ID_WACOM:
|
||||
hid->group = HID_GROUP_WACOM;
|
||||
break;
|
||||
}
|
||||
|
||||
vfree(parser);
|
||||
return 0;
|
||||
}
|
||||
@ -1938,8 +1947,6 @@ static const struct hid_device_id hid_have_special_driver[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_3_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_DUAL_BOX_PRO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SUPER_JOY_BOX_5_PRO) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_SLIM_TABLET_12_1_INCH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP, USB_DEVICE_ID_WALTOP_Q_PAD) },
|
||||
@ -2345,7 +2352,6 @@ static const struct hid_device_id hid_ignore_list[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LCSPEC) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WACOM, HID_ANY_ID) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
|
||||
|
@ -1,973 +0,0 @@
|
||||
/*
|
||||
* Bluetooth Wacom Tablet support
|
||||
*
|
||||
* Copyright (c) 1999 Andreas Gal
|
||||
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
|
||||
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
|
||||
* Copyright (c) 2006-2007 Jiri Kosina
|
||||
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
|
||||
* Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
|
||||
* Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
|
||||
* Copyright (c) 2011 Przemysław Firszt <przemo@firszt.eu>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/power_supply.h>
|
||||
|
||||
#include "hid-ids.h"
|
||||
|
||||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
#define WAC_CMD_LED_CONTROL 0x20
|
||||
#define WAC_CMD_ICON_START_STOP 0x21
|
||||
#define WAC_CMD_ICON_TRANSFER 0x26
|
||||
|
||||
struct wacom_data {
|
||||
__u16 tool;
|
||||
__u16 butstate;
|
||||
__u8 whlstate;
|
||||
__u8 features;
|
||||
__u32 id;
|
||||
__u32 serial;
|
||||
unsigned char high_speed;
|
||||
__u8 battery_capacity;
|
||||
__u8 power_raw;
|
||||
__u8 ps_connected;
|
||||
__u8 bat_charging;
|
||||
struct power_supply battery;
|
||||
struct power_supply ac;
|
||||
__u8 led_selector;
|
||||
struct led_classdev *leds[4];
|
||||
};
|
||||
|
||||
/*percent of battery capacity for Graphire
|
||||
8th value means AC online and show 100% capacity */
|
||||
static unsigned short batcap_gr[8] = { 1, 15, 25, 35, 50, 70, 100, 100 };
|
||||
/*percent of battery capacity for Intuos4 WL, AC has a separate bit*/
|
||||
static unsigned short batcap_i4[8] = { 1, 15, 30, 45, 60, 70, 85, 100 };
|
||||
|
||||
static enum power_supply_property wacom_battery_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
};
|
||||
|
||||
static enum power_supply_property wacom_ac_props[] = {
|
||||
POWER_SUPPLY_PROP_PRESENT,
|
||||
POWER_SUPPLY_PROP_ONLINE,
|
||||
POWER_SUPPLY_PROP_SCOPE,
|
||||
};
|
||||
|
||||
static void wacom_scramble(__u8 *image)
|
||||
{
|
||||
__u16 mask;
|
||||
__u16 s1;
|
||||
__u16 s2;
|
||||
__u16 r1 ;
|
||||
__u16 r2 ;
|
||||
__u16 r;
|
||||
__u8 buf[256];
|
||||
int i, w, x, y, z;
|
||||
|
||||
for (x = 0; x < 32; x++) {
|
||||
for (y = 0; y < 8; y++)
|
||||
buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
|
||||
}
|
||||
|
||||
/* Change 76543210 into GECA6420 as required by Intuos4 WL
|
||||
* HGFEDCBA HFDB7531
|
||||
*/
|
||||
for (x = 0; x < 4; x++) {
|
||||
for (y = 0; y < 4; y++) {
|
||||
for (z = 0; z < 8; z++) {
|
||||
mask = 0x0001;
|
||||
r1 = 0;
|
||||
r2 = 0;
|
||||
i = (x << 6) + (y << 4) + z;
|
||||
s1 = buf[i];
|
||||
s2 = buf[i+8];
|
||||
for (w = 0; w < 8; w++) {
|
||||
r1 |= (s1 & mask);
|
||||
r2 |= (s2 & mask);
|
||||
s1 <<= 1;
|
||||
s2 <<= 1;
|
||||
mask <<= 2;
|
||||
}
|
||||
r = r1 | (r2 << 1);
|
||||
i = (x << 6) + (y << 4) + (z << 1);
|
||||
image[i] = 0xFF & r;
|
||||
image[i+1] = (0xFF00 & r) >> 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void wacom_set_image(struct hid_device *hdev, const char *image,
|
||||
__u8 icon_no)
|
||||
{
|
||||
__u8 rep_data[68];
|
||||
__u8 p[256];
|
||||
int ret, i, j;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
p[i] = image[i];
|
||||
|
||||
rep_data[0] = WAC_CMD_ICON_START_STOP;
|
||||
rep_data[1] = 0;
|
||||
ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
rep_data[0] = WAC_CMD_ICON_TRANSFER;
|
||||
rep_data[1] = icon_no & 0x07;
|
||||
|
||||
wacom_scramble(p);
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
for (j = 0; j < 64; j++)
|
||||
rep_data[j + 3] = p[(i << 6) + j];
|
||||
|
||||
rep_data[2] = i;
|
||||
ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 67,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
}
|
||||
|
||||
rep_data[0] = WAC_CMD_ICON_START_STOP;
|
||||
rep_data[1] = 0;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
|
||||
err:
|
||||
return;
|
||||
}
|
||||
|
||||
static void wacom_leds_set_brightness(struct led_classdev *led_dev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct device *dev = led_dev->dev->parent;
|
||||
struct hid_device *hdev;
|
||||
struct wacom_data *wdata;
|
||||
unsigned char *buf;
|
||||
__u8 led = 0;
|
||||
int i;
|
||||
|
||||
hdev = container_of(dev, struct hid_device, dev);
|
||||
wdata = hid_get_drvdata(hdev);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (wdata->leds[i] == led_dev)
|
||||
wdata->led_selector = i;
|
||||
}
|
||||
|
||||
led = wdata->led_selector | 0x04;
|
||||
buf = kzalloc(9, GFP_KERNEL);
|
||||
if (buf) {
|
||||
buf[0] = WAC_CMD_LED_CONTROL;
|
||||
buf[1] = led;
|
||||
buf[2] = value >> 2;
|
||||
buf[3] = value;
|
||||
/* use fixed brightness for OLEDs */
|
||||
buf[4] = 0x08;
|
||||
hid_hw_raw_request(hdev, buf[0], buf, 9, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
kfree(buf);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static enum led_brightness wacom_leds_get_brightness(struct led_classdev *led_dev)
|
||||
{
|
||||
struct wacom_data *wdata;
|
||||
struct device *dev = led_dev->dev->parent;
|
||||
int value = 0;
|
||||
int i;
|
||||
|
||||
wdata = hid_get_drvdata(container_of(dev, struct hid_device, dev));
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (wdata->leds[i] == led_dev) {
|
||||
value = wdata->leds[i]->brightness;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
static int wacom_initialize_leds(struct hid_device *hdev)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
struct led_classdev *led;
|
||||
struct device *dev = &hdev->dev;
|
||||
size_t namesz = strlen(dev_name(dev)) + 12;
|
||||
char *name;
|
||||
int i, ret;
|
||||
|
||||
wdata->led_selector = 0;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
led = kzalloc(sizeof(struct led_classdev) + namesz, GFP_KERNEL);
|
||||
if (!led) {
|
||||
hid_warn(hdev,
|
||||
"can't allocate memory for LED selector\n");
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
name = (void *)&led[1];
|
||||
snprintf(name, namesz, "%s:selector:%d", dev_name(dev), i);
|
||||
led->name = name;
|
||||
led->brightness = 0;
|
||||
led->max_brightness = 127;
|
||||
led->brightness_get = wacom_leds_get_brightness;
|
||||
led->brightness_set = wacom_leds_set_brightness;
|
||||
|
||||
wdata->leds[i] = led;
|
||||
|
||||
ret = led_classdev_register(dev, wdata->leds[i]);
|
||||
|
||||
if (ret) {
|
||||
wdata->leds[i] = NULL;
|
||||
kfree(led);
|
||||
hid_warn(hdev, "can't register LED\n");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wacom_destroy_leds(struct hid_device *hdev)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
struct led_classdev *led;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; ++i) {
|
||||
if (wdata->leds[i]) {
|
||||
led = wdata->leds[i];
|
||||
wdata->leds[i] = NULL;
|
||||
led_classdev_unregister(led);
|
||||
kfree(led);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int wacom_battery_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wacom_data *wdata = container_of(psy,
|
||||
struct wacom_data, battery);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
val->intval = 1;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
val->intval = wdata->battery_capacity;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
if (wdata->bat_charging)
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
else
|
||||
if (wdata->battery_capacity == 100 && wdata->ps_connected)
|
||||
val->intval = POWER_SUPPLY_STATUS_FULL;
|
||||
else
|
||||
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wacom_ac_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct wacom_data *wdata = container_of(psy, struct wacom_data, ac);
|
||||
int ret = 0;
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_PRESENT:
|
||||
/* fall through */
|
||||
case POWER_SUPPLY_PROP_ONLINE:
|
||||
val->intval = wdata->ps_connected;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_SCOPE:
|
||||
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wacom_set_features(struct hid_device *hdev, u8 speed)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
int limit, ret;
|
||||
__u8 rep_data[2];
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
|
||||
rep_data[0] = 0x03 ; rep_data[1] = 0x00;
|
||||
limit = 3;
|
||||
do {
|
||||
ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
} while (ret < 0 && limit-- > 0);
|
||||
|
||||
if (ret >= 0) {
|
||||
if (speed == 0)
|
||||
rep_data[0] = 0x05;
|
||||
else
|
||||
rep_data[0] = 0x06;
|
||||
|
||||
rep_data[1] = 0x00;
|
||||
limit = 3;
|
||||
do {
|
||||
ret = hid_hw_raw_request(hdev, rep_data[0],
|
||||
rep_data, 2, HID_FEATURE_REPORT,
|
||||
HID_REQ_SET_REPORT);
|
||||
} while (ret < 0 && limit-- > 0);
|
||||
|
||||
if (ret >= 0) {
|
||||
wdata->high_speed = speed;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that if the raw queries fail, it's not a hard failure
|
||||
* and it is safe to continue
|
||||
*/
|
||||
hid_warn(hdev, "failed to poke device, command %d, err %d\n",
|
||||
rep_data[0], ret);
|
||||
break;
|
||||
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
|
||||
if (speed == 1)
|
||||
wdata->features &= ~0x20;
|
||||
else
|
||||
wdata->features |= 0x20;
|
||||
|
||||
rep_data[0] = 0x03;
|
||||
rep_data[1] = wdata->features;
|
||||
|
||||
ret = hid_hw_raw_request(hdev, rep_data[0], rep_data, 2,
|
||||
HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
|
||||
if (ret >= 0)
|
||||
wdata->high_speed = speed;
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t wacom_show_speed(struct device *dev,
|
||||
struct device_attribute
|
||||
*attr, char *buf)
|
||||
{
|
||||
struct wacom_data *wdata = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%i\n", wdata->high_speed);
|
||||
}
|
||||
|
||||
static ssize_t wacom_store_speed(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
|
||||
int new_speed;
|
||||
|
||||
if (sscanf(buf, "%1d", &new_speed ) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if (new_speed == 0 || new_speed == 1) {
|
||||
wacom_set_features(hdev, new_speed);
|
||||
return strnlen(buf, PAGE_SIZE);
|
||||
} else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
wacom_show_speed, wacom_store_speed);
|
||||
|
||||
#define WACOM_STORE(OLED_ID) \
|
||||
static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \
|
||||
struct device_attribute *attr, \
|
||||
const char *buf, size_t count) \
|
||||
{ \
|
||||
struct hid_device *hdev = container_of(dev, struct hid_device, \
|
||||
dev); \
|
||||
\
|
||||
if (count != 256) \
|
||||
return -EINVAL; \
|
||||
\
|
||||
wacom_set_image(hdev, buf, OLED_ID); \
|
||||
\
|
||||
return count; \
|
||||
} \
|
||||
\
|
||||
static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \
|
||||
wacom_oled##OLED_ID##_store)
|
||||
|
||||
WACOM_STORE(0);
|
||||
WACOM_STORE(1);
|
||||
WACOM_STORE(2);
|
||||
WACOM_STORE(3);
|
||||
WACOM_STORE(4);
|
||||
WACOM_STORE(5);
|
||||
WACOM_STORE(6);
|
||||
WACOM_STORE(7);
|
||||
|
||||
static int wacom_gr_parse_report(struct hid_device *hdev,
|
||||
struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
int tool, x, y, rw;
|
||||
|
||||
tool = 0;
|
||||
/* Get X & Y positions */
|
||||
x = le16_to_cpu(*(__le16 *) &data[2]);
|
||||
y = le16_to_cpu(*(__le16 *) &data[4]);
|
||||
|
||||
/* Get current tool identifier */
|
||||
if (data[1] & 0x90) { /* If pen is in the in/active area */
|
||||
switch ((data[1] >> 5) & 3) {
|
||||
case 0: /* Pen */
|
||||
tool = BTN_TOOL_PEN;
|
||||
break;
|
||||
|
||||
case 1: /* Rubber */
|
||||
tool = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
|
||||
case 2: /* Mouse with wheel */
|
||||
case 3: /* Mouse without wheel */
|
||||
tool = BTN_TOOL_MOUSE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reset tool if out of active tablet area */
|
||||
if (!(data[1] & 0x10))
|
||||
tool = 0;
|
||||
}
|
||||
|
||||
/* If tool changed, notify input subsystem */
|
||||
if (wdata->tool != tool) {
|
||||
if (wdata->tool) {
|
||||
/* Completely reset old tool state */
|
||||
if (wdata->tool == BTN_TOOL_MOUSE) {
|
||||
input_report_key(input, BTN_LEFT, 0);
|
||||
input_report_key(input, BTN_RIGHT, 0);
|
||||
input_report_key(input, BTN_MIDDLE, 0);
|
||||
input_report_abs(input, ABS_DISTANCE,
|
||||
input_abs_get_max(input, ABS_DISTANCE));
|
||||
} else {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_key(input, BTN_STYLUS, 0);
|
||||
input_report_key(input, BTN_STYLUS2, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
}
|
||||
input_report_key(input, wdata->tool, 0);
|
||||
input_sync(input);
|
||||
}
|
||||
wdata->tool = tool;
|
||||
if (tool)
|
||||
input_report_key(input, tool, 1);
|
||||
}
|
||||
|
||||
if (tool) {
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
|
||||
switch ((data[1] >> 5) & 3) {
|
||||
case 2: /* Mouse with wheel */
|
||||
input_report_key(input, BTN_MIDDLE, data[1] & 0x04);
|
||||
rw = (data[6] & 0x01) ? -1 :
|
||||
(data[6] & 0x02) ? 1 : 0;
|
||||
input_report_rel(input, REL_WHEEL, rw);
|
||||
/* fall through */
|
||||
|
||||
case 3: /* Mouse without wheel */
|
||||
input_report_key(input, BTN_LEFT, data[1] & 0x01);
|
||||
input_report_key(input, BTN_RIGHT, data[1] & 0x02);
|
||||
/* Compute distance between mouse and tablet */
|
||||
rw = 44 - (data[6] >> 2);
|
||||
if (rw < 0)
|
||||
rw = 0;
|
||||
else if (rw > 31)
|
||||
rw = 31;
|
||||
input_report_abs(input, ABS_DISTANCE, rw);
|
||||
break;
|
||||
|
||||
default:
|
||||
input_report_abs(input, ABS_PRESSURE,
|
||||
data[6] | (((__u16) (data[1] & 0x08)) << 5));
|
||||
input_report_key(input, BTN_TOUCH, data[1] & 0x01);
|
||||
input_report_key(input, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(input, BTN_STYLUS2, (tool == BTN_TOOL_PEN) && data[1] & 0x04);
|
||||
break;
|
||||
}
|
||||
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
/* Report the state of the two buttons at the top of the tablet
|
||||
* as two extra fingerpad keys (buttons 4 & 5). */
|
||||
rw = data[7] & 0x03;
|
||||
if (rw != wdata->butstate) {
|
||||
wdata->butstate = rw;
|
||||
input_report_key(input, BTN_0, rw & 0x02);
|
||||
input_report_key(input, BTN_1, rw & 0x01);
|
||||
input_report_key(input, BTN_TOOL_FINGER, 0xf0);
|
||||
input_event(input, EV_MSC, MSC_SERIAL, 0xf0);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
/* Store current battery capacity and power supply state*/
|
||||
rw = (data[7] >> 2 & 0x07);
|
||||
if (rw != wdata->power_raw) {
|
||||
wdata->power_raw = rw;
|
||||
wdata->battery_capacity = batcap_gr[rw];
|
||||
if (rw == 7)
|
||||
wdata->ps_connected = 1;
|
||||
else
|
||||
wdata->ps_connected = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void wacom_i4_parse_button_report(struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
__u16 new_butstate;
|
||||
__u8 new_whlstate;
|
||||
__u8 sync = 0;
|
||||
|
||||
new_whlstate = data[1];
|
||||
if (new_whlstate != wdata->whlstate) {
|
||||
wdata->whlstate = new_whlstate;
|
||||
if (new_whlstate & 0x80) {
|
||||
input_report_key(input, BTN_TOUCH, 1);
|
||||
input_report_abs(input, ABS_WHEEL, (new_whlstate & 0x7f));
|
||||
input_report_key(input, BTN_TOOL_FINGER, 1);
|
||||
} else {
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_WHEEL, 0);
|
||||
input_report_key(input, BTN_TOOL_FINGER, 0);
|
||||
}
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
new_butstate = (data[3] << 1) | (data[2] & 0x01);
|
||||
if (new_butstate != wdata->butstate) {
|
||||
wdata->butstate = new_butstate;
|
||||
input_report_key(input, BTN_0, new_butstate & 0x001);
|
||||
input_report_key(input, BTN_1, new_butstate & 0x002);
|
||||
input_report_key(input, BTN_2, new_butstate & 0x004);
|
||||
input_report_key(input, BTN_3, new_butstate & 0x008);
|
||||
input_report_key(input, BTN_4, new_butstate & 0x010);
|
||||
input_report_key(input, BTN_5, new_butstate & 0x020);
|
||||
input_report_key(input, BTN_6, new_butstate & 0x040);
|
||||
input_report_key(input, BTN_7, new_butstate & 0x080);
|
||||
input_report_key(input, BTN_8, new_butstate & 0x100);
|
||||
input_report_key(input, BTN_TOOL_FINGER, 1);
|
||||
sync = 1;
|
||||
}
|
||||
|
||||
if (sync) {
|
||||
input_report_abs(input, ABS_MISC, PAD_DEVICE_ID);
|
||||
input_event(input, EV_MSC, MSC_SERIAL, 0xffffffff);
|
||||
input_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
static void wacom_i4_parse_pen_report(struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
__u16 x, y, pressure;
|
||||
__u8 distance;
|
||||
__u8 tilt_x, tilt_y;
|
||||
|
||||
switch (data[1]) {
|
||||
case 0x80: /* Out of proximity report */
|
||||
input_report_key(input, BTN_TOUCH, 0);
|
||||
input_report_abs(input, ABS_PRESSURE, 0);
|
||||
input_report_key(input, BTN_STYLUS, 0);
|
||||
input_report_key(input, BTN_STYLUS2, 0);
|
||||
input_report_key(input, wdata->tool, 0);
|
||||
input_report_abs(input, ABS_MISC, 0);
|
||||
input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
|
||||
wdata->tool = 0;
|
||||
input_sync(input);
|
||||
break;
|
||||
case 0xC2: /* Tool report */
|
||||
wdata->id = ((data[2] << 4) | (data[3] >> 4) |
|
||||
((data[7] & 0x0f) << 20) |
|
||||
((data[8] & 0xf0) << 12));
|
||||
wdata->serial = ((data[3] & 0x0f) << 28) +
|
||||
(data[4] << 20) + (data[5] << 12) +
|
||||
(data[6] << 4) + (data[7] >> 4);
|
||||
|
||||
switch (wdata->id) {
|
||||
case 0x100802:
|
||||
wdata->tool = BTN_TOOL_PEN;
|
||||
break;
|
||||
case 0x10080A:
|
||||
wdata->tool = BTN_TOOL_RUBBER;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default: /* Position/pressure report */
|
||||
x = data[2] << 9 | data[3] << 1 | ((data[9] & 0x02) >> 1);
|
||||
y = data[4] << 9 | data[5] << 1 | (data[9] & 0x01);
|
||||
pressure = (data[6] << 3) | ((data[7] & 0xC0) >> 5)
|
||||
| (data[1] & 0x01);
|
||||
distance = (data[9] >> 2) & 0x3f;
|
||||
tilt_x = ((data[7] << 1) & 0x7e) | (data[8] >> 7);
|
||||
tilt_y = data[8] & 0x7f;
|
||||
|
||||
input_report_key(input, BTN_TOUCH, pressure > 1);
|
||||
|
||||
input_report_key(input, BTN_STYLUS, data[1] & 0x02);
|
||||
input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
|
||||
input_report_key(input, wdata->tool, 1);
|
||||
input_report_abs(input, ABS_X, x);
|
||||
input_report_abs(input, ABS_Y, y);
|
||||
input_report_abs(input, ABS_PRESSURE, pressure);
|
||||
input_report_abs(input, ABS_DISTANCE, distance);
|
||||
input_report_abs(input, ABS_TILT_X, tilt_x);
|
||||
input_report_abs(input, ABS_TILT_Y, tilt_y);
|
||||
input_report_abs(input, ABS_MISC, wdata->id);
|
||||
input_event(input, EV_MSC, MSC_SERIAL, wdata->serial);
|
||||
input_report_key(input, wdata->tool, 1);
|
||||
input_sync(input);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void wacom_i4_parse_report(struct hid_device *hdev,
|
||||
struct wacom_data *wdata,
|
||||
struct input_dev *input, unsigned char *data)
|
||||
{
|
||||
switch (data[0]) {
|
||||
case 0x00: /* Empty report */
|
||||
break;
|
||||
case 0x02: /* Pen report */
|
||||
wacom_i4_parse_pen_report(wdata, input, data);
|
||||
break;
|
||||
case 0x03: /* Features Report */
|
||||
wdata->features = data[2];
|
||||
break;
|
||||
case 0x0C: /* Button report */
|
||||
wacom_i4_parse_button_report(wdata, input, data);
|
||||
break;
|
||||
default:
|
||||
hid_err(hdev, "Unknown report: %d,%d\n", data[0], data[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_raw_event(struct hid_device *hdev, struct hid_report *report,
|
||||
u8 *raw_data, int size)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
struct hid_input *hidinput;
|
||||
struct input_dev *input;
|
||||
unsigned char *data = (unsigned char *) raw_data;
|
||||
int i;
|
||||
__u8 power_raw;
|
||||
|
||||
if (!(hdev->claimed & HID_CLAIMED_INPUT))
|
||||
return 0;
|
||||
|
||||
hidinput = list_entry(hdev->inputs.next, struct hid_input, list);
|
||||
input = hidinput->input;
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
|
||||
if (data[0] == 0x03) {
|
||||
return wacom_gr_parse_report(hdev, wdata, input, data);
|
||||
} else {
|
||||
hid_err(hdev, "Unknown report: %d,%d size:%d\n",
|
||||
data[0], data[1], size);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
|
||||
i = 1;
|
||||
|
||||
switch (data[0]) {
|
||||
case 0x04:
|
||||
wacom_i4_parse_report(hdev, wdata, input, data + i);
|
||||
i += 10;
|
||||
/* fall through */
|
||||
case 0x03:
|
||||
wacom_i4_parse_report(hdev, wdata, input, data + i);
|
||||
i += 10;
|
||||
wacom_i4_parse_report(hdev, wdata, input, data + i);
|
||||
power_raw = data[i+10];
|
||||
if (power_raw != wdata->power_raw) {
|
||||
wdata->power_raw = power_raw;
|
||||
wdata->battery_capacity = batcap_i4[power_raw & 0x07];
|
||||
wdata->bat_charging = (power_raw & 0x08) ? 1 : 0;
|
||||
wdata->ps_connected = (power_raw & 0x10) ? 1 : 0;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
hid_err(hdev, "Unknown report: %d,%d size:%d\n",
|
||||
data[0], data[1], size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int wacom_input_mapped(struct hid_device *hdev, struct hid_input *hi,
|
||||
struct hid_field *field, struct hid_usage *usage, unsigned long **bit,
|
||||
int *max)
|
||||
{
|
||||
struct input_dev *input = hi->input;
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, input->propbit);
|
||||
|
||||
/* Basics */
|
||||
input->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_REL);
|
||||
|
||||
__set_bit(REL_WHEEL, input->relbit);
|
||||
|
||||
__set_bit(BTN_TOOL_PEN, input->keybit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
__set_bit(BTN_STYLUS, input->keybit);
|
||||
__set_bit(BTN_STYLUS2, input->keybit);
|
||||
__set_bit(BTN_LEFT, input->keybit);
|
||||
__set_bit(BTN_RIGHT, input->keybit);
|
||||
__set_bit(BTN_MIDDLE, input->keybit);
|
||||
|
||||
/* Pad */
|
||||
input_set_capability(input, EV_MSC, MSC_SERIAL);
|
||||
|
||||
__set_bit(BTN_0, input->keybit);
|
||||
__set_bit(BTN_1, input->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, input->keybit);
|
||||
|
||||
/* Distance, rubber and mouse */
|
||||
__set_bit(BTN_TOOL_RUBBER, input->keybit);
|
||||
__set_bit(BTN_TOOL_MOUSE, input->keybit);
|
||||
|
||||
switch (hdev->product) {
|
||||
case USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH:
|
||||
input_set_abs_params(input, ABS_X, 0, 16704, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, 12064, 4, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 511, 0, 0);
|
||||
input_set_abs_params(input, ABS_DISTANCE, 0, 32, 0, 0);
|
||||
break;
|
||||
case USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH:
|
||||
__set_bit(ABS_WHEEL, input->absbit);
|
||||
__set_bit(ABS_MISC, input->absbit);
|
||||
__set_bit(BTN_2, input->keybit);
|
||||
__set_bit(BTN_3, input->keybit);
|
||||
__set_bit(BTN_4, input->keybit);
|
||||
__set_bit(BTN_5, input->keybit);
|
||||
__set_bit(BTN_6, input->keybit);
|
||||
__set_bit(BTN_7, input->keybit);
|
||||
__set_bit(BTN_8, input->keybit);
|
||||
input_set_abs_params(input, ABS_WHEEL, 0, 71, 0, 0);
|
||||
input_set_abs_params(input, ABS_X, 0, 40640, 4, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, 25400, 4, 0);
|
||||
input_set_abs_params(input, ABS_PRESSURE, 0, 2047, 0, 0);
|
||||
input_set_abs_params(input, ABS_DISTANCE, 0, 63, 0, 0);
|
||||
input_set_abs_params(input, ABS_TILT_X, 0, 127, 0, 0);
|
||||
input_set_abs_params(input, ABS_TILT_Y, 0, 127, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wacom_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct wacom_data *wdata;
|
||||
int ret;
|
||||
|
||||
wdata = kzalloc(sizeof(*wdata), GFP_KERNEL);
|
||||
if (wdata == NULL) {
|
||||
hid_err(hdev, "can't alloc wacom descriptor\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hid_set_drvdata(hdev, wdata);
|
||||
|
||||
/* Parse the HID report now */
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "parse failed\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hw start failed\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = device_create_file(&hdev->dev, &dev_attr_speed);
|
||||
if (ret)
|
||||
hid_warn(hdev,
|
||||
"can't create sysfs speed attribute err: %d\n", ret);
|
||||
|
||||
#define OLED_INIT(OLED_ID) \
|
||||
do { \
|
||||
ret = device_create_file(&hdev->dev, \
|
||||
&dev_attr_oled##OLED_ID##_img); \
|
||||
if (ret) \
|
||||
hid_warn(hdev, \
|
||||
"can't create sysfs oled attribute, err: %d\n", ret);\
|
||||
} while (0)
|
||||
|
||||
OLED_INIT(0);
|
||||
OLED_INIT(1);
|
||||
OLED_INIT(2);
|
||||
OLED_INIT(3);
|
||||
OLED_INIT(4);
|
||||
OLED_INIT(5);
|
||||
OLED_INIT(6);
|
||||
OLED_INIT(7);
|
||||
|
||||
wdata->features = 0;
|
||||
wacom_set_features(hdev, 1);
|
||||
|
||||
if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
|
||||
sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
|
||||
ret = wacom_initialize_leds(hdev);
|
||||
if (ret)
|
||||
hid_warn(hdev,
|
||||
"can't create led attribute, err: %d\n", ret);
|
||||
}
|
||||
|
||||
wdata->battery.properties = wacom_battery_props;
|
||||
wdata->battery.num_properties = ARRAY_SIZE(wacom_battery_props);
|
||||
wdata->battery.get_property = wacom_battery_get_property;
|
||||
wdata->battery.name = "wacom_battery";
|
||||
wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||
wdata->battery.use_for_apm = 0;
|
||||
|
||||
|
||||
ret = power_supply_register(&hdev->dev, &wdata->battery);
|
||||
if (ret) {
|
||||
hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
|
||||
ret);
|
||||
goto err_battery;
|
||||
}
|
||||
|
||||
power_supply_powers(&wdata->battery, &hdev->dev);
|
||||
|
||||
wdata->ac.properties = wacom_ac_props;
|
||||
wdata->ac.num_properties = ARRAY_SIZE(wacom_ac_props);
|
||||
wdata->ac.get_property = wacom_ac_get_property;
|
||||
wdata->ac.name = "wacom_ac";
|
||||
wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
|
||||
wdata->ac.use_for_apm = 0;
|
||||
|
||||
ret = power_supply_register(&hdev->dev, &wdata->ac);
|
||||
if (ret) {
|
||||
hid_err(hdev,
|
||||
"can't create ac battery attribute, err: %d\n", ret);
|
||||
goto err_ac;
|
||||
}
|
||||
|
||||
power_supply_powers(&wdata->ac, &hdev->dev);
|
||||
return 0;
|
||||
|
||||
err_ac:
|
||||
power_supply_unregister(&wdata->battery);
|
||||
err_battery:
|
||||
wacom_destroy_leds(hdev);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled0_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled1_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled2_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled3_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled4_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled5_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled6_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled7_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
hid_hw_stop(hdev);
|
||||
err_free:
|
||||
kfree(wdata);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void wacom_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct wacom_data *wdata = hid_get_drvdata(hdev);
|
||||
|
||||
wacom_destroy_leds(hdev);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled0_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled1_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled2_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled3_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled4_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled5_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled6_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_oled7_img);
|
||||
device_remove_file(&hdev->dev, &dev_attr_speed);
|
||||
hid_hw_stop(hdev);
|
||||
|
||||
power_supply_unregister(&wdata->battery);
|
||||
power_supply_unregister(&wdata->ac);
|
||||
kfree(hid_get_drvdata(hdev));
|
||||
}
|
||||
|
||||
static const struct hid_device_id wacom_devices[] = {
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE_BLUETOOTH) },
|
||||
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) },
|
||||
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, wacom_devices);
|
||||
|
||||
static struct hid_driver wacom_driver = {
|
||||
.name = "wacom",
|
||||
.id_table = wacom_devices,
|
||||
.probe = wacom_probe,
|
||||
.remove = wacom_remove,
|
||||
.raw_event = wacom_raw_event,
|
||||
.input_mapped = wacom_input_mapped,
|
||||
};
|
||||
module_hid_driver(wacom_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for Wacom Graphire Bluetooth and Wacom Intuos4 WL");
|
||||
MODULE_LICENSE("GPL");
|
@ -12,6 +12,7 @@
|
||||
* Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
|
||||
* Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
|
||||
* Copyright (c) 2002-2011 Ping Cheng <pingc@wacom.com>
|
||||
* Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@redhat.com>
|
||||
*
|
||||
* ChangeLog:
|
||||
* v0.1 (vp) - Initial release
|
||||
@ -72,6 +73,8 @@
|
||||
* v1.52 (pc) - Query Wacom data upon system resume
|
||||
* - add defines for features->type
|
||||
* - add new devices (0x9F, 0xE2, and 0XE3)
|
||||
* v2.00 (bt) - conversion to a HID driver
|
||||
* - integration of the Bluetooth devices
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -93,35 +96,30 @@
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.53"
|
||||
#define DRIVER_VERSION "v2.00"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB Wacom tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
#define USB_VENDOR_ID_LENOVO 0x17ef
|
||||
|
||||
struct wacom {
|
||||
dma_addr_t data_dma;
|
||||
struct usb_device *usbdev;
|
||||
struct usb_interface *intf;
|
||||
struct urb *irq;
|
||||
struct wacom_wac wacom_wac;
|
||||
struct hid_device *hdev;
|
||||
struct mutex lock;
|
||||
struct work_struct work;
|
||||
bool open;
|
||||
char phys[32];
|
||||
struct wacom_led {
|
||||
u8 select[2]; /* status led selector (0..3) */
|
||||
u8 llv; /* status led brightness no button (1..127) */
|
||||
u8 hlv; /* status led brightness button pressed (1..127) */
|
||||
u8 img_lum; /* OLED matrix display brightness */
|
||||
} led;
|
||||
bool led_initialized;
|
||||
struct power_supply battery;
|
||||
struct power_supply ac;
|
||||
};
|
||||
|
||||
static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
|
||||
@ -130,10 +128,19 @@ static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
|
||||
schedule_work(&wacom->work);
|
||||
}
|
||||
|
||||
extern const struct usb_device_id wacom_ids[];
|
||||
static inline void wacom_notify_battery(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
|
||||
|
||||
power_supply_changed(&wacom->battery);
|
||||
}
|
||||
|
||||
extern const struct hid_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);
|
||||
int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
int wacom_setup_pad_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -46,6 +46,7 @@
|
||||
|
||||
/* wacom data packet report IDs */
|
||||
#define WACOM_REPORT_PENABLED 2
|
||||
#define WACOM_REPORT_PENABLED_BT 3
|
||||
#define WACOM_REPORT_INTUOSREAD 5
|
||||
#define WACOM_REPORT_INTUOSWRITE 6
|
||||
#define WACOM_REPORT_INTUOSPAD 12
|
||||
@ -68,10 +69,12 @@
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
|
||||
#define WACOM_QUIRK_NO_INPUT 0x0004
|
||||
#define WACOM_QUIRK_MONITOR 0x0008
|
||||
#define WACOM_QUIRK_BATTERY 0x0010
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
GRAPHIRE,
|
||||
GRAPHIRE_BT,
|
||||
WACOM_G4,
|
||||
PTU,
|
||||
PL,
|
||||
@ -83,6 +86,7 @@ enum {
|
||||
INTUOS3L,
|
||||
INTUOS4S,
|
||||
INTUOS4,
|
||||
INTUOS4WL,
|
||||
INTUOS4L,
|
||||
INTUOS5S,
|
||||
INTUOS5,
|
||||
@ -114,7 +118,6 @@ enum {
|
||||
|
||||
struct wacom_features {
|
||||
const char *name;
|
||||
int pktlen;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int pressure_max;
|
||||
@ -127,8 +130,8 @@ struct wacom_features {
|
||||
int device_type;
|
||||
int x_phy;
|
||||
int y_phy;
|
||||
unsigned char unit;
|
||||
unsigned char unitExpo;
|
||||
unsigned unit;
|
||||
int unitExpo;
|
||||
int x_fuzz;
|
||||
int y_fuzz;
|
||||
int pressure_fuzz;
|
||||
@ -137,6 +140,9 @@ struct wacom_features {
|
||||
unsigned touch_max;
|
||||
int oVid;
|
||||
int oPid;
|
||||
int pktlen;
|
||||
bool check_for_hid_type;
|
||||
int hid_type;
|
||||
};
|
||||
|
||||
struct wacom_shared {
|
||||
@ -150,16 +156,24 @@ struct wacom_shared {
|
||||
|
||||
struct wacom_wac {
|
||||
char name[WACOM_NAME_MAX];
|
||||
unsigned char *data;
|
||||
char pad_name[WACOM_NAME_MAX];
|
||||
char bat_name[WACOM_NAME_MAX];
|
||||
char ac_name[WACOM_NAME_MAX];
|
||||
unsigned char data[WACOM_PKGLEN_MAX];
|
||||
int tool[2];
|
||||
int id[2];
|
||||
__u32 serial[2];
|
||||
struct wacom_features features;
|
||||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
struct input_dev *pad_input;
|
||||
int pid;
|
||||
int battery_capacity;
|
||||
int num_contacts_left;
|
||||
int bat_charging;
|
||||
int ps_connected;
|
||||
u8 bt_features;
|
||||
u8 bt_high_speed;
|
||||
};
|
||||
|
||||
#endif
|
@ -665,4 +665,14 @@ config KEYBOARD_CROS_EC
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cros_ec_keyb.
|
||||
|
||||
config KEYBOARD_CAP1106
|
||||
tristate "Microchip CAP1106 touch sensor"
|
||||
depends on OF && I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here to enable the CAP1106 touch sensor driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cap1106.
|
||||
|
||||
endif
|
||||
|
@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_CAP1106) += cap1106.o
|
||||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
||||
|
335
drivers/input/keyboard/cap1106.c
Normal file
335
drivers/input/keyboard/cap1106.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* Input driver for Microchip CAP1106, 6 channel capacitive touch sensor
|
||||
*
|
||||
* http://www.microchip.com/wwwproducts/Devices.aspx?product=CAP1106
|
||||
*
|
||||
* (c) 2014 Daniel Mack <linux@zonque.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#define CAP1106_REG_MAIN_CONTROL 0x00
|
||||
#define CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT (6)
|
||||
#define CAP1106_REG_MAIN_CONTROL_GAIN_MASK (0xc0)
|
||||
#define CAP1106_REG_MAIN_CONTROL_DLSEEP BIT(4)
|
||||
#define CAP1106_REG_GENERAL_STATUS 0x02
|
||||
#define CAP1106_REG_SENSOR_INPUT 0x03
|
||||
#define CAP1106_REG_NOISE_FLAG_STATUS 0x0a
|
||||
#define CAP1106_REG_SENOR_DELTA(X) (0x10 + (X))
|
||||
#define CAP1106_REG_SENSITIVITY_CONTROL 0x1f
|
||||
#define CAP1106_REG_CONFIG 0x20
|
||||
#define CAP1106_REG_SENSOR_ENABLE 0x21
|
||||
#define CAP1106_REG_SENSOR_CONFIG 0x22
|
||||
#define CAP1106_REG_SENSOR_CONFIG2 0x23
|
||||
#define CAP1106_REG_SAMPLING_CONFIG 0x24
|
||||
#define CAP1106_REG_CALIBRATION 0x25
|
||||
#define CAP1106_REG_INT_ENABLE 0x26
|
||||
#define CAP1106_REG_REPEAT_RATE 0x28
|
||||
#define CAP1106_REG_MT_CONFIG 0x2a
|
||||
#define CAP1106_REG_MT_PATTERN_CONFIG 0x2b
|
||||
#define CAP1106_REG_MT_PATTERN 0x2d
|
||||
#define CAP1106_REG_RECALIB_CONFIG 0x2f
|
||||
#define CAP1106_REG_SENSOR_THRESH(X) (0x30 + (X))
|
||||
#define CAP1106_REG_SENSOR_NOISE_THRESH 0x38
|
||||
#define CAP1106_REG_STANDBY_CHANNEL 0x40
|
||||
#define CAP1106_REG_STANDBY_CONFIG 0x41
|
||||
#define CAP1106_REG_STANDBY_SENSITIVITY 0x42
|
||||
#define CAP1106_REG_STANDBY_THRESH 0x43
|
||||
#define CAP1106_REG_CONFIG2 0x44
|
||||
#define CAP1106_REG_SENSOR_BASE_CNT(X) (0x50 + (X))
|
||||
#define CAP1106_REG_SENSOR_CALIB (0xb1 + (X))
|
||||
#define CAP1106_REG_SENSOR_CALIB_LSB1 0xb9
|
||||
#define CAP1106_REG_SENSOR_CALIB_LSB2 0xba
|
||||
#define CAP1106_REG_PRODUCT_ID 0xfd
|
||||
#define CAP1106_REG_MANUFACTURER_ID 0xfe
|
||||
#define CAP1106_REG_REVISION 0xff
|
||||
|
||||
#define CAP1106_NUM_CHN 6
|
||||
#define CAP1106_PRODUCT_ID 0x55
|
||||
#define CAP1106_MANUFACTURER_ID 0x5d
|
||||
|
||||
struct cap1106_priv {
|
||||
struct regmap *regmap;
|
||||
struct input_dev *idev;
|
||||
|
||||
/* config */
|
||||
unsigned int keycodes[CAP1106_NUM_CHN];
|
||||
};
|
||||
|
||||
static const struct reg_default cap1106_reg_defaults[] = {
|
||||
{ CAP1106_REG_MAIN_CONTROL, 0x00 },
|
||||
{ CAP1106_REG_GENERAL_STATUS, 0x00 },
|
||||
{ CAP1106_REG_SENSOR_INPUT, 0x00 },
|
||||
{ CAP1106_REG_NOISE_FLAG_STATUS, 0x00 },
|
||||
{ CAP1106_REG_SENSITIVITY_CONTROL, 0x2f },
|
||||
{ CAP1106_REG_CONFIG, 0x20 },
|
||||
{ CAP1106_REG_SENSOR_ENABLE, 0x3f },
|
||||
{ CAP1106_REG_SENSOR_CONFIG, 0xa4 },
|
||||
{ CAP1106_REG_SENSOR_CONFIG2, 0x07 },
|
||||
{ CAP1106_REG_SAMPLING_CONFIG, 0x39 },
|
||||
{ CAP1106_REG_CALIBRATION, 0x00 },
|
||||
{ CAP1106_REG_INT_ENABLE, 0x3f },
|
||||
{ CAP1106_REG_REPEAT_RATE, 0x3f },
|
||||
{ CAP1106_REG_MT_CONFIG, 0x80 },
|
||||
{ CAP1106_REG_MT_PATTERN_CONFIG, 0x00 },
|
||||
{ CAP1106_REG_MT_PATTERN, 0x3f },
|
||||
{ CAP1106_REG_RECALIB_CONFIG, 0x8a },
|
||||
{ CAP1106_REG_SENSOR_THRESH(0), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(1), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(2), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(3), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(4), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(5), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_NOISE_THRESH, 0x01 },
|
||||
{ CAP1106_REG_STANDBY_CHANNEL, 0x00 },
|
||||
{ CAP1106_REG_STANDBY_CONFIG, 0x39 },
|
||||
{ CAP1106_REG_STANDBY_SENSITIVITY, 0x02 },
|
||||
{ CAP1106_REG_STANDBY_THRESH, 0x40 },
|
||||
{ CAP1106_REG_CONFIG2, 0x40 },
|
||||
{ CAP1106_REG_SENSOR_CALIB_LSB1, 0x00 },
|
||||
{ CAP1106_REG_SENSOR_CALIB_LSB2, 0x00 },
|
||||
};
|
||||
|
||||
static bool cap1106_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CAP1106_REG_MAIN_CONTROL:
|
||||
case CAP1106_REG_SENSOR_INPUT:
|
||||
case CAP1106_REG_SENOR_DELTA(0):
|
||||
case CAP1106_REG_SENOR_DELTA(1):
|
||||
case CAP1106_REG_SENOR_DELTA(2):
|
||||
case CAP1106_REG_SENOR_DELTA(3):
|
||||
case CAP1106_REG_SENOR_DELTA(4):
|
||||
case CAP1106_REG_SENOR_DELTA(5):
|
||||
case CAP1106_REG_PRODUCT_ID:
|
||||
case CAP1106_REG_MANUFACTURER_ID:
|
||||
case CAP1106_REG_REVISION:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config cap1106_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = CAP1106_REG_REVISION,
|
||||
.reg_defaults = cap1106_reg_defaults,
|
||||
|
||||
.num_reg_defaults = ARRAY_SIZE(cap1106_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = cap1106_volatile_reg,
|
||||
};
|
||||
|
||||
static irqreturn_t cap1106_thread_func(int irq_num, void *data)
|
||||
{
|
||||
struct cap1106_priv *priv = data;
|
||||
unsigned int status;
|
||||
int ret, i;
|
||||
|
||||
/*
|
||||
* Deassert interrupt. This needs to be done before reading the status
|
||||
* registers, which will not carry valid values otherwise.
|
||||
*/
|
||||
ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, 1, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read(priv->regmap, CAP1106_REG_SENSOR_INPUT, &status);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < CAP1106_NUM_CHN; i++)
|
||||
input_report_key(priv->idev, priv->keycodes[i],
|
||||
status & (1 << i));
|
||||
|
||||
input_sync(priv->idev);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cap1106_set_sleep(struct cap1106_priv *priv, bool sleep)
|
||||
{
|
||||
return regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL,
|
||||
CAP1106_REG_MAIN_CONTROL_DLSEEP,
|
||||
sleep ? CAP1106_REG_MAIN_CONTROL_DLSEEP : 0);
|
||||
}
|
||||
|
||||
static int cap1106_input_open(struct input_dev *idev)
|
||||
{
|
||||
struct cap1106_priv *priv = input_get_drvdata(idev);
|
||||
|
||||
return cap1106_set_sleep(priv, false);
|
||||
}
|
||||
|
||||
static void cap1106_input_close(struct input_dev *idev)
|
||||
{
|
||||
struct cap1106_priv *priv = input_get_drvdata(idev);
|
||||
|
||||
cap1106_set_sleep(priv, true);
|
||||
}
|
||||
|
||||
static int cap1106_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &i2c_client->dev;
|
||||
struct cap1106_priv *priv;
|
||||
struct device_node *node;
|
||||
int i, error, irq, gain = 0;
|
||||
unsigned int val, rev;
|
||||
u32 gain32, keycodes[CAP1106_NUM_CHN];
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->regmap = devm_regmap_init_i2c(i2c_client, &cap1106_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
error = regmap_read(priv->regmap, CAP1106_REG_PRODUCT_ID, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != CAP1106_PRODUCT_ID) {
|
||||
dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n",
|
||||
val, CAP1106_PRODUCT_ID);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
error = regmap_read(priv->regmap, CAP1106_REG_MANUFACTURER_ID, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != CAP1106_MANUFACTURER_ID) {
|
||||
dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n",
|
||||
val, CAP1106_MANUFACTURER_ID);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
error = regmap_read(priv->regmap, CAP1106_REG_REVISION, &rev);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
dev_info(dev, "CAP1106 detected, revision 0x%02x\n", rev);
|
||||
i2c_set_clientdata(i2c_client, priv);
|
||||
node = dev->of_node;
|
||||
|
||||
if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) {
|
||||
if (is_power_of_2(gain32) && gain32 <= 8)
|
||||
gain = ilog2(gain32);
|
||||
else
|
||||
dev_err(dev, "Invalid sensor-gain value %d\n", gain32);
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(keycodes) != ARRAY_SIZE(priv->keycodes));
|
||||
|
||||
/* Provide some useful defaults */
|
||||
for (i = 0; i < ARRAY_SIZE(keycodes); i++)
|
||||
keycodes[i] = KEY_A + i;
|
||||
|
||||
of_property_read_u32_array(node, "linux,keycodes",
|
||||
keycodes, ARRAY_SIZE(keycodes));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(keycodes); i++)
|
||||
priv->keycodes[i] = keycodes[i];
|
||||
|
||||
error = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL,
|
||||
CAP1106_REG_MAIN_CONTROL_GAIN_MASK,
|
||||
gain << CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Disable autorepeat. The Linux input system has its own handling. */
|
||||
error = regmap_write(priv->regmap, CAP1106_REG_REPEAT_RATE, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
priv->idev = devm_input_allocate_device(dev);
|
||||
if (!priv->idev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->idev->name = "CAP1106 capacitive touch sensor";
|
||||
priv->idev->id.bustype = BUS_I2C;
|
||||
priv->idev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
|
||||
if (of_property_read_bool(node, "autorepeat"))
|
||||
__set_bit(EV_REP, priv->idev->evbit);
|
||||
|
||||
for (i = 0; i < CAP1106_NUM_CHN; i++)
|
||||
__set_bit(priv->keycodes[i], priv->idev->keybit);
|
||||
|
||||
priv->idev->id.vendor = CAP1106_MANUFACTURER_ID;
|
||||
priv->idev->id.product = CAP1106_PRODUCT_ID;
|
||||
priv->idev->id.version = rev;
|
||||
|
||||
priv->idev->open = cap1106_input_open;
|
||||
priv->idev->close = cap1106_input_close;
|
||||
|
||||
input_set_drvdata(priv->idev, priv);
|
||||
|
||||
/*
|
||||
* Put the device in deep sleep mode for now.
|
||||
* ->open() will bring it back once the it is actually needed.
|
||||
*/
|
||||
cap1106_set_sleep(priv, true);
|
||||
|
||||
error = input_register_device(priv->idev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (!irq) {
|
||||
dev_err(dev, "Unable to parse or map IRQ\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, irq, NULL, cap1106_thread_func,
|
||||
IRQF_ONESHOT, dev_name(dev), priv);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cap1106_dt_ids[] = {
|
||||
{ .compatible = "microchip,cap1106", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cap1106_dt_ids);
|
||||
|
||||
static const struct i2c_device_id cap1106_i2c_ids[] = {
|
||||
{ "cap1106", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cap1106_i2c_ids);
|
||||
|
||||
static struct i2c_driver cap1106_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cap1106",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cap1106_dt_ids,
|
||||
},
|
||||
.id_table = cap1106_i2c_ids,
|
||||
.probe = cap1106_i2c_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(cap1106_i2c_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cap1106");
|
||||
MODULE_DESCRIPTION("Microchip CAP1106 driver");
|
||||
MODULE_AUTHOR("Daniel Mack <linux@zonque.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -531,8 +531,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int imx_kbd_suspend(struct device *dev)
|
||||
static int __maybe_unused imx_kbd_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct imx_keypad *kbd = platform_get_drvdata(pdev);
|
||||
@ -552,7 +551,7 @@ static int imx_kbd_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_kbd_resume(struct device *dev)
|
||||
static int __maybe_unused imx_kbd_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct imx_keypad *kbd = platform_get_drvdata(pdev);
|
||||
@ -575,7 +574,6 @@ err_clk:
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume);
|
||||
|
||||
|
@ -203,12 +203,17 @@ static int max7359_probe(struct i2c_client *client,
|
||||
|
||||
dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret);
|
||||
|
||||
keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!keypad || !input_dev) {
|
||||
keypad = devm_kzalloc(&client->dev, sizeof(struct max7359_keypad),
|
||||
GFP_KERNEL);
|
||||
if (!keypad) {
|
||||
dev_err(&client->dev, "failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto failed_free_mem;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad->client = client;
|
||||
@ -230,19 +235,20 @@ static int max7359_probe(struct i2c_client *client,
|
||||
|
||||
max7359_build_keycode(keypad, keymap_data);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, max7359_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, keypad);
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
max7359_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, keypad);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to register interrupt\n");
|
||||
goto failed_free_mem;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Register the input device */
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to register input device\n");
|
||||
goto failed_free_irq;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Initialize MAX7359 */
|
||||
@ -251,24 +257,6 @@ static int max7359_probe(struct i2c_client *client,
|
||||
i2c_set_clientdata(client, keypad);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_free_irq:
|
||||
free_irq(client->irq, keypad);
|
||||
failed_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(keypad);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int max7359_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max7359_keypad *keypad = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, keypad);
|
||||
input_unregister_device(keypad->input_dev);
|
||||
kfree(keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -313,7 +301,6 @@ static struct i2c_driver max7359_i2c_driver = {
|
||||
.pm = &max7359_pm,
|
||||
},
|
||||
.probe = max7359_probe,
|
||||
.remove = max7359_remove,
|
||||
.id_table = max7359_ids,
|
||||
};
|
||||
|
||||
|
@ -392,7 +392,6 @@ static void keyspan_irq_recv(struct urb *urb)
|
||||
|
||||
default:
|
||||
goto resubmit;
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
|
@ -83,6 +83,9 @@ soc_button_device_create(struct pnp_dev *pdev,
|
||||
sizeof(*gpio_keys_pdata) +
|
||||
sizeof(*gpio_keys) * MAX_NBUTTONS,
|
||||
GFP_KERNEL);
|
||||
if (!gpio_keys_pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
gpio_keys = (void *)(gpio_keys_pdata + 1);
|
||||
|
||||
for (info = button_info; info->name; info++) {
|
||||
|
@ -311,7 +311,14 @@ static int uinput_open(struct inode *inode, struct file *file)
|
||||
static int uinput_validate_absbits(struct input_dev *dev)
|
||||
{
|
||||
unsigned int cnt;
|
||||
int retval = 0;
|
||||
int nslot;
|
||||
|
||||
if (!test_bit(EV_ABS, dev->evbit))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check if absmin/absmax/absfuzz/absflat are sane.
|
||||
*/
|
||||
|
||||
for (cnt = 0; cnt < ABS_CNT; cnt++) {
|
||||
int min, max;
|
||||
@ -327,8 +334,7 @@ static int uinput_validate_absbits(struct input_dev *dev)
|
||||
UINPUT_NAME, cnt,
|
||||
input_abs_get_min(dev, cnt),
|
||||
input_abs_get_max(dev, cnt));
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (input_abs_get_flat(dev, cnt) >
|
||||
@ -340,11 +346,18 @@ static int uinput_validate_absbits(struct input_dev *dev)
|
||||
input_abs_get_flat(dev, cnt),
|
||||
input_abs_get_min(dev, cnt),
|
||||
input_abs_get_max(dev, cnt));
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
|
||||
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
|
||||
nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
|
||||
input_mt_init_slots(dev, nslot, 0);
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
input_set_events_per_packet(dev, 60);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_allocate_device(struct uinput_device *udev)
|
||||
@ -410,19 +423,9 @@ static int uinput_setup_device(struct uinput_device *udev,
|
||||
input_abs_set_flat(dev, i, user_dev->absflat[i]);
|
||||
}
|
||||
|
||||
/* check if absmin/absmax/absfuzz/absflat are filled as
|
||||
* told in Documentation/input/input-programming.txt */
|
||||
if (test_bit(EV_ABS, dev->evbit)) {
|
||||
retval = uinput_validate_absbits(dev);
|
||||
if (retval < 0)
|
||||
goto exit;
|
||||
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
|
||||
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
|
||||
input_mt_init_slots(dev, nslot, 0);
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
input_set_events_per_packet(dev, 60);
|
||||
}
|
||||
}
|
||||
retval = uinput_validate_absbits(dev);
|
||||
if (retval < 0)
|
||||
goto exit;
|
||||
|
||||
udev->state = UIST_SETUP_COMPLETE;
|
||||
retval = count;
|
||||
@ -720,6 +723,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case UI_GET_VERSION:
|
||||
if (put_user(UINPUT_VERSION,
|
||||
(unsigned int __user *)p))
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
|
||||
case UI_DEV_CREATE:
|
||||
retval = uinput_create_device(udev);
|
||||
goto out;
|
||||
|
@ -99,6 +99,8 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
|
||||
#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */
|
||||
#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with
|
||||
6-byte ALPS packet */
|
||||
#define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */
|
||||
#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */
|
||||
|
||||
static const struct alps_model_info alps_model_data[] = {
|
||||
{ { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
|
||||
@ -281,11 +283,10 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
|
||||
*
|
||||
* The bitmaps don't have enough data to track fingers, so this function
|
||||
* only generates points representing a bounding box of at most two contacts.
|
||||
* These two points are returned in x1, y1, x2, and y2.
|
||||
* These two points are returned in fields->mt.
|
||||
*/
|
||||
static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
||||
struct alps_fields *fields,
|
||||
int *x1, int *y1, int *x2, int *y2)
|
||||
struct alps_fields *fields)
|
||||
{
|
||||
int box_middle_x, box_middle_y;
|
||||
unsigned int x_map, y_map;
|
||||
@ -308,8 +309,6 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
||||
if (x_msb > priv->x_bits || y_msb > priv->y_bits)
|
||||
return;
|
||||
|
||||
*x1 = *y1 = *x2 = *y2 = 0;
|
||||
|
||||
if (fields->fingers > 1) {
|
||||
start_bit = priv->x_bits - x_msb;
|
||||
end_bit = priv->x_bits - x_lsb;
|
||||
@ -320,10 +319,35 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
||||
end_bit = y_msb - 1;
|
||||
box_middle_y = (priv->y_max * (start_bit + end_bit)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
*x1 = fields->x;
|
||||
*y1 = fields->y;
|
||||
*x2 = 2 * box_middle_x - *x1;
|
||||
*y2 = 2 * box_middle_y - *y1;
|
||||
fields->mt[0] = fields->st;
|
||||
fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x;
|
||||
fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y;
|
||||
}
|
||||
}
|
||||
|
||||
static void alps_get_bitmap_points(unsigned int map,
|
||||
struct alps_bitmap_point *low,
|
||||
struct alps_bitmap_point *high,
|
||||
int *fingers)
|
||||
{
|
||||
struct alps_bitmap_point *point;
|
||||
int i, bit, prev_bit = 0;
|
||||
|
||||
point = low;
|
||||
for (i = 0; map != 0; i++, map >>= 1) {
|
||||
bit = map & 1;
|
||||
if (bit) {
|
||||
if (!prev_bit) {
|
||||
point->start_bit = i;
|
||||
point->num_bits = 0;
|
||||
(*fingers)++;
|
||||
}
|
||||
point->num_bits++;
|
||||
} else {
|
||||
if (prev_bit)
|
||||
point = high;
|
||||
}
|
||||
prev_bit = bit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,71 +358,21 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
||||
*
|
||||
* The bitmaps don't have enough data to track fingers, so this function
|
||||
* only generates points representing a bounding box of all contacts.
|
||||
* These points are returned in x1, y1, x2, and y2 when the return value
|
||||
* These points are returned in fields->mt when the return value
|
||||
* is greater than 0.
|
||||
*/
|
||||
static int alps_process_bitmap(struct alps_data *priv,
|
||||
unsigned int x_map, unsigned int y_map,
|
||||
int *x1, int *y1, int *x2, int *y2)
|
||||
struct alps_fields *fields)
|
||||
{
|
||||
struct alps_bitmap_point {
|
||||
int start_bit;
|
||||
int num_bits;
|
||||
};
|
||||
|
||||
int fingers_x = 0, fingers_y = 0, fingers;
|
||||
int i, bit, prev_bit;
|
||||
int i, fingers_x = 0, fingers_y = 0, fingers;
|
||||
struct alps_bitmap_point x_low = {0,}, x_high = {0,};
|
||||
struct alps_bitmap_point y_low = {0,}, y_high = {0,};
|
||||
struct alps_bitmap_point *point;
|
||||
|
||||
if (!x_map || !y_map)
|
||||
if (!fields->x_map || !fields->y_map)
|
||||
return 0;
|
||||
|
||||
*x1 = *y1 = *x2 = *y2 = 0;
|
||||
|
||||
prev_bit = 0;
|
||||
point = &x_low;
|
||||
for (i = 0; x_map != 0; i++, x_map >>= 1) {
|
||||
bit = x_map & 1;
|
||||
if (bit) {
|
||||
if (!prev_bit) {
|
||||
point->start_bit = i;
|
||||
fingers_x++;
|
||||
}
|
||||
point->num_bits++;
|
||||
} else {
|
||||
if (prev_bit)
|
||||
point = &x_high;
|
||||
else
|
||||
point->num_bits = 0;
|
||||
}
|
||||
prev_bit = bit;
|
||||
}
|
||||
|
||||
/*
|
||||
* y bitmap is reversed for what we need (lower positions are in
|
||||
* higher bits), so we process from the top end.
|
||||
*/
|
||||
y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits);
|
||||
prev_bit = 0;
|
||||
point = &y_low;
|
||||
for (i = 0; y_map != 0; i++, y_map <<= 1) {
|
||||
bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1));
|
||||
if (bit) {
|
||||
if (!prev_bit) {
|
||||
point->start_bit = i;
|
||||
fingers_y++;
|
||||
}
|
||||
point->num_bits++;
|
||||
} else {
|
||||
if (prev_bit)
|
||||
point = &y_high;
|
||||
else
|
||||
point->num_bits = 0;
|
||||
}
|
||||
prev_bit = bit;
|
||||
}
|
||||
alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x);
|
||||
alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y);
|
||||
|
||||
/*
|
||||
* Fingers can overlap, so we use the maximum count of fingers
|
||||
@ -407,58 +381,91 @@ static int alps_process_bitmap(struct alps_data *priv,
|
||||
fingers = max(fingers_x, fingers_y);
|
||||
|
||||
/*
|
||||
* If total fingers is > 1 but either axis reports only a single
|
||||
* contact, we have overlapping or adjacent fingers. For the
|
||||
* purposes of creating a bounding box, divide the single contact
|
||||
* (roughly) equally between the two points.
|
||||
* If an axis reports only a single contact, we have overlapping or
|
||||
* adjacent fingers. Divide the single contact between the two points.
|
||||
*/
|
||||
if (fingers > 1) {
|
||||
if (fingers_x == 1) {
|
||||
i = x_low.num_bits / 2;
|
||||
x_low.num_bits = x_low.num_bits - i;
|
||||
x_high.start_bit = x_low.start_bit + i;
|
||||
x_high.num_bits = max(i, 1);
|
||||
} else if (fingers_y == 1) {
|
||||
i = y_low.num_bits / 2;
|
||||
y_low.num_bits = y_low.num_bits - i;
|
||||
y_high.start_bit = y_low.start_bit + i;
|
||||
y_high.num_bits = max(i, 1);
|
||||
}
|
||||
if (fingers_x == 1) {
|
||||
i = (x_low.num_bits - 1) / 2;
|
||||
x_low.num_bits = x_low.num_bits - i;
|
||||
x_high.start_bit = x_low.start_bit + i;
|
||||
x_high.num_bits = max(i, 1);
|
||||
}
|
||||
if (fingers_y == 1) {
|
||||
i = (y_low.num_bits - 1) / 2;
|
||||
y_low.num_bits = y_low.num_bits - i;
|
||||
y_high.start_bit = y_low.start_bit + i;
|
||||
y_high.num_bits = max(i, 1);
|
||||
}
|
||||
|
||||
*x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
|
||||
(2 * (priv->x_bits - 1));
|
||||
*y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
fields->mt[0].x =
|
||||
(priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
|
||||
(2 * (priv->x_bits - 1));
|
||||
fields->mt[0].y =
|
||||
(priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
|
||||
if (fingers > 1) {
|
||||
*x2 = (priv->x_max *
|
||||
(2 * x_high.start_bit + x_high.num_bits - 1)) /
|
||||
(2 * (priv->x_bits - 1));
|
||||
*y2 = (priv->y_max *
|
||||
(2 * y_high.start_bit + y_high.num_bits - 1)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
fields->mt[1].x =
|
||||
(priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
|
||||
(2 * (priv->x_bits - 1));
|
||||
fields->mt[1].y =
|
||||
(priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
|
||||
/* y-bitmap order is reversed, except on rushmore */
|
||||
if (!(priv->flags & ALPS_IS_RUSHMORE)) {
|
||||
fields->mt[0].y = priv->y_max - fields->mt[0].y;
|
||||
fields->mt[1].y = priv->y_max - fields->mt[1].y;
|
||||
}
|
||||
|
||||
return fingers;
|
||||
}
|
||||
|
||||
static void alps_set_slot(struct input_dev *dev, int slot, bool active,
|
||||
int x, int y)
|
||||
static void alps_set_slot(struct input_dev *dev, int slot, int x, int y)
|
||||
{
|
||||
input_mt_slot(dev, slot);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
|
||||
if (active) {
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
|
||||
static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
|
||||
int x1, int y1, int x2, int y2)
|
||||
static void alps_report_mt_data(struct psmouse *psmouse, int n)
|
||||
{
|
||||
alps_set_slot(dev, 0, num_fingers != 0, x1, y1);
|
||||
alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
int i, slot[MAX_TOUCHES];
|
||||
|
||||
input_mt_assign_slots(dev, slot, f->mt, n);
|
||||
for (i = 0; i < n; i++)
|
||||
alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
|
||||
|
||||
input_mt_sync_frame(dev);
|
||||
}
|
||||
|
||||
static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
|
||||
/* Use st data when we don't have mt data */
|
||||
if (fingers < 2) {
|
||||
f->mt[0].x = f->st.x;
|
||||
f->mt[0].y = f->st.y;
|
||||
fingers = f->pressure > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2);
|
||||
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, f->left);
|
||||
input_report_key(dev, BTN_RIGHT, f->right);
|
||||
input_report_key(dev, BTN_MIDDLE, f->middle);
|
||||
|
||||
input_report_abs(dev, ABS_PRESSURE, f->pressure);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
|
||||
@ -532,7 +539,7 @@ static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
|
||||
f->ts_middle = !!(p[3] & 0x40);
|
||||
}
|
||||
|
||||
static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
|
||||
static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
f->first_mp = !!(p[4] & 0x40);
|
||||
@ -546,24 +553,31 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
|
||||
((p[2] & 0x7f) << 1) |
|
||||
(p[4] & 0x01);
|
||||
|
||||
f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
|
||||
f->st.x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
|
||||
((p[0] & 0x30) >> 4);
|
||||
f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
|
||||
f->z = p[5] & 0x7f;
|
||||
f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
|
||||
f->pressure = p[5] & 0x7f;
|
||||
|
||||
alps_decode_buttons_v3(f, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
|
||||
static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
alps_decode_pinnacle(f, p, psmouse);
|
||||
|
||||
/* Rushmore's packet decode has a bit difference with Pinnacle's */
|
||||
f->is_mp = !!(p[5] & 0x40);
|
||||
f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
|
||||
f->x_map |= (p[5] & 0x10) << 11;
|
||||
f->y_map |= (p[5] & 0x20) << 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
||||
static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
u64 palm_data = 0;
|
||||
@ -573,9 +587,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
||||
f->is_mp = !!(p[0] & 0x20);
|
||||
|
||||
if (!f->is_mp) {
|
||||
f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
|
||||
f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
|
||||
f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
|
||||
f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
|
||||
f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
|
||||
f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f;
|
||||
alps_decode_buttons_v3(f, p);
|
||||
} else {
|
||||
f->fingers = ((p[0] & 0x6) >> 1 |
|
||||
@ -596,19 +610,21 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
||||
f->x_map = (palm_data >> priv->y_bits) &
|
||||
(BIT(priv->x_bits) - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct input_dev *dev2 = priv->dev2;
|
||||
int x1 = 0, y1 = 0, x2 = 0, y2 = 0;
|
||||
int fingers = 0, bmap_fn;
|
||||
struct alps_fields f = {0};
|
||||
struct alps_fields *f = &priv->f;
|
||||
int fingers = 0;
|
||||
|
||||
priv->decode_fields(&f, packet, psmouse);
|
||||
memset(f, 0, sizeof(*f));
|
||||
|
||||
priv->decode_fields(f, packet, psmouse);
|
||||
|
||||
/*
|
||||
* There's no single feature of touchpad position and bitmap packets
|
||||
@ -623,22 +639,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
||||
* packet. Check for this, and when it happens process the
|
||||
* position packet as usual.
|
||||
*/
|
||||
if (f.is_mp) {
|
||||
fingers = f.fingers;
|
||||
if (f->is_mp) {
|
||||
fingers = f->fingers;
|
||||
if (priv->proto_version == ALPS_PROTO_V3) {
|
||||
bmap_fn = alps_process_bitmap(priv, f.x_map,
|
||||
f.y_map, &x1, &y1,
|
||||
&x2, &y2);
|
||||
|
||||
/*
|
||||
* We shouldn't report more than one finger if
|
||||
* we don't have two coordinates.
|
||||
*/
|
||||
if (fingers > 1 && bmap_fn < 2)
|
||||
fingers = bmap_fn;
|
||||
if (alps_process_bitmap(priv, f) == 0)
|
||||
fingers = 0; /* Use st data */
|
||||
|
||||
/* Now process position packet */
|
||||
priv->decode_fields(&f, priv->multi_data,
|
||||
priv->decode_fields(f, priv->multi_data,
|
||||
psmouse);
|
||||
} else {
|
||||
/*
|
||||
@ -647,15 +655,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
||||
* calculate Pt2, so we need to do position
|
||||
* packet decode first.
|
||||
*/
|
||||
priv->decode_fields(&f, priv->multi_data,
|
||||
priv->decode_fields(f, priv->multi_data,
|
||||
psmouse);
|
||||
|
||||
/*
|
||||
* Since Dolphin's finger number is reliable,
|
||||
* there is no need to compare with bmap_fn.
|
||||
*/
|
||||
alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
|
||||
&x2, &y2);
|
||||
alps_process_bitmap_dolphin(priv, f);
|
||||
}
|
||||
} else {
|
||||
priv->multi_packet = 0;
|
||||
@ -670,10 +677,10 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
||||
* out misidentified bitmap packets, we reject anything with this
|
||||
* bit set.
|
||||
*/
|
||||
if (f.is_mp)
|
||||
if (f->is_mp)
|
||||
return;
|
||||
|
||||
if (!priv->multi_packet && f.first_mp) {
|
||||
if (!priv->multi_packet && f->first_mp) {
|
||||
priv->multi_packet = 1;
|
||||
memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
|
||||
return;
|
||||
@ -687,44 +694,15 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
||||
* with x, y, and z all zero, so these seem to be flukes.
|
||||
* Ignore them.
|
||||
*/
|
||||
if (f.x && f.y && !f.z)
|
||||
if (f->st.x && f->st.y && !f->pressure)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If we don't have MT data or the bitmaps were empty, we have
|
||||
* to rely on ST data.
|
||||
*/
|
||||
if (!fingers) {
|
||||
x1 = f.x;
|
||||
y1 = f.y;
|
||||
fingers = f.z > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (f.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, f.left);
|
||||
input_report_key(dev, BTN_RIGHT, f.right);
|
||||
input_report_key(dev, BTN_MIDDLE, f.middle);
|
||||
|
||||
if (f.z > 0) {
|
||||
input_report_abs(dev, ABS_X, f.x);
|
||||
input_report_abs(dev, ABS_Y, f.y);
|
||||
}
|
||||
input_report_abs(dev, ABS_PRESSURE, f.z);
|
||||
|
||||
input_sync(dev);
|
||||
alps_report_semi_mt_data(psmouse, fingers);
|
||||
|
||||
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
|
||||
input_report_key(dev2, BTN_LEFT, f.ts_left);
|
||||
input_report_key(dev2, BTN_RIGHT, f.ts_right);
|
||||
input_report_key(dev2, BTN_MIDDLE, f.ts_middle);
|
||||
input_report_key(dev2, BTN_LEFT, f->ts_left);
|
||||
input_report_key(dev2, BTN_RIGHT, f->ts_right);
|
||||
input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
|
||||
input_sync(dev2);
|
||||
}
|
||||
}
|
||||
@ -823,13 +801,8 @@ 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;
|
||||
struct alps_fields *f = &priv->f;
|
||||
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
|
||||
@ -851,71 +824,207 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
|
||||
if (++priv->multi_packet > 2) {
|
||||
priv->multi_packet = 0;
|
||||
|
||||
x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) |
|
||||
f->x_map = ((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) |
|
||||
f->y_map = ((priv->multi_data[5] & 0x01) << 10) |
|
||||
((priv->multi_data[3] & 0x1f) << 5) |
|
||||
(priv->multi_data[1] & 0x1f);
|
||||
|
||||
fingers = alps_process_bitmap(priv, 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;
|
||||
f->fingers = alps_process_bitmap(priv, f);
|
||||
}
|
||||
|
||||
left = packet[4] & 0x01;
|
||||
right = packet[4] & 0x02;
|
||||
f->left = packet[4] & 0x01;
|
||||
f->right = packet[4] & 0x02;
|
||||
|
||||
x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
|
||||
((packet[0] & 0x30) >> 4);
|
||||
y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
|
||||
z = packet[5] & 0x7f;
|
||||
f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
|
||||
((packet[0] & 0x30) >> 4);
|
||||
f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
|
||||
f->pressure = packet[5] & 0x7f;
|
||||
|
||||
alps_report_semi_mt_data(psmouse, f->fingers);
|
||||
}
|
||||
|
||||
static bool alps_is_valid_package_v7(struct psmouse *psmouse)
|
||||
{
|
||||
switch (psmouse->pktcnt) {
|
||||
case 3:
|
||||
return (psmouse->packet[2] & 0x40) == 0x40;
|
||||
case 4:
|
||||
return (psmouse->packet[3] & 0x48) == 0x48;
|
||||
case 6:
|
||||
return (psmouse->packet[5] & 0x40) == 0x00;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned char alps_get_packet_id_v7(char *byte)
|
||||
{
|
||||
unsigned char packet_id;
|
||||
|
||||
if (byte[4] & 0x40)
|
||||
packet_id = V7_PACKET_ID_TWO;
|
||||
else if (byte[4] & 0x01)
|
||||
packet_id = V7_PACKET_ID_MULTI;
|
||||
else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
|
||||
packet_id = V7_PACKET_ID_NEW;
|
||||
else if (byte[1] == 0x00 && byte[4] == 0x00)
|
||||
packet_id = V7_PACKET_ID_IDLE;
|
||||
else
|
||||
packet_id = V7_PACKET_ID_UNKNOWN;
|
||||
|
||||
return packet_id;
|
||||
}
|
||||
|
||||
static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
|
||||
unsigned char *pkt,
|
||||
unsigned char pkt_id)
|
||||
{
|
||||
mt[0].x = ((pkt[2] & 0x80) << 4);
|
||||
mt[0].x |= ((pkt[2] & 0x3F) << 5);
|
||||
mt[0].x |= ((pkt[3] & 0x30) >> 1);
|
||||
mt[0].x |= (pkt[3] & 0x07);
|
||||
mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
|
||||
|
||||
mt[1].x = ((pkt[3] & 0x80) << 4);
|
||||
mt[1].x |= ((pkt[4] & 0x80) << 3);
|
||||
mt[1].x |= ((pkt[4] & 0x3F) << 4);
|
||||
mt[1].y = ((pkt[5] & 0x80) << 3);
|
||||
mt[1].y |= ((pkt[5] & 0x3F) << 4);
|
||||
|
||||
switch (pkt_id) {
|
||||
case V7_PACKET_ID_TWO:
|
||||
mt[1].x &= ~0x000F;
|
||||
mt[1].y |= 0x000F;
|
||||
break;
|
||||
|
||||
case V7_PACKET_ID_MULTI:
|
||||
mt[1].x &= ~0x003F;
|
||||
mt[1].y &= ~0x0020;
|
||||
mt[1].y |= ((pkt[4] & 0x02) << 4);
|
||||
mt[1].y |= 0x001F;
|
||||
break;
|
||||
|
||||
case V7_PACKET_ID_NEW:
|
||||
mt[1].x &= ~0x003F;
|
||||
mt[1].x |= (pkt[0] & 0x20);
|
||||
mt[1].y |= 0x000F;
|
||||
break;
|
||||
}
|
||||
|
||||
mt[0].y = 0x7FF - mt[0].y;
|
||||
mt[1].y = 0x7FF - mt[1].y;
|
||||
}
|
||||
|
||||
static int alps_get_mt_count(struct input_mt_pos *mt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++)
|
||||
/* empty */;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int alps_decode_packet_v7(struct alps_fields *f,
|
||||
unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char pkt_id;
|
||||
|
||||
pkt_id = alps_get_packet_id_v7(p);
|
||||
if (pkt_id == V7_PACKET_ID_IDLE)
|
||||
return 0;
|
||||
if (pkt_id == V7_PACKET_ID_UNKNOWN)
|
||||
return -1;
|
||||
|
||||
alps_get_finger_coordinate_v7(f->mt, p, pkt_id);
|
||||
|
||||
if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) {
|
||||
f->left = (p[0] & 0x80) >> 7;
|
||||
f->right = (p[0] & 0x20) >> 5;
|
||||
f->middle = (p[0] & 0x10) >> 4;
|
||||
}
|
||||
|
||||
if (pkt_id == V7_PACKET_ID_TWO)
|
||||
f->fingers = alps_get_mt_count(f->mt);
|
||||
else if (pkt_id == V7_PACKET_ID_MULTI)
|
||||
f->fingers = 3 + (p[5] & 0x03);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev2 = priv->dev2;
|
||||
int x, y, z, left, right, middle;
|
||||
|
||||
/*
|
||||
* If there were no contacts in the bitmap, use ST
|
||||
* points in MT reports.
|
||||
* If there were two contacts or more, report MT data.
|
||||
* b7 b6 b5 b4 b3 b2 b1 b0
|
||||
* Byte0 0 1 0 0 1 0 0 0
|
||||
* Byte1 1 1 * * 1 M R L
|
||||
* Byte2 X7 1 X5 X4 X3 X2 X1 X0
|
||||
* Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0
|
||||
* Byte4 Y7 0 Y5 Y4 Y3 1 1 0
|
||||
* Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0
|
||||
* M / R / L: Middle / Right / Left button
|
||||
*/
|
||||
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);
|
||||
x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2);
|
||||
y = (packet[3] & 0x07) | (packet[4] & 0xb8) |
|
||||
((packet[3] & 0x20) << 1);
|
||||
z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
|
||||
|
||||
alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
|
||||
left = (packet[1] & 0x01);
|
||||
right = (packet[1] & 0x02) >> 1;
|
||||
middle = (packet[1] & 0x04) >> 2;
|
||||
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
/* Divide 2 since trackpoint's speed is too fast */
|
||||
input_report_rel(dev2, REL_X, (char)x / 2);
|
||||
input_report_rel(dev2, REL_Y, -((char)y / 2));
|
||||
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
input_report_key(dev2, BTN_LEFT, left);
|
||||
input_report_key(dev2, BTN_RIGHT, right);
|
||||
input_report_key(dev2, BTN_MIDDLE, middle);
|
||||
|
||||
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_sync(dev2);
|
||||
}
|
||||
|
||||
static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
|
||||
memset(f, 0, sizeof(*f));
|
||||
|
||||
if (priv->decode_fields(f, psmouse->packet, psmouse))
|
||||
return;
|
||||
|
||||
alps_report_mt_data(psmouse, alps_get_mt_count(f->mt));
|
||||
|
||||
input_mt_report_finger_count(dev, f->fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, f->left);
|
||||
input_report_key(dev, BTN_RIGHT, f->right);
|
||||
input_report_key(dev, BTN_MIDDLE, f->middle);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void alps_process_packet_v7(struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char *packet = psmouse->packet;
|
||||
|
||||
if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06)
|
||||
alps_process_trackstick_packet_v7(psmouse);
|
||||
else
|
||||
alps_process_touchpad_packet_v7(psmouse);
|
||||
}
|
||||
|
||||
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
|
||||
unsigned char packet[],
|
||||
bool report_buttons)
|
||||
@ -1080,6 +1189,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
||||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (priv->proto_version == ALPS_PROTO_V7 &&
|
||||
!alps_is_valid_package_v7(psmouse)) {
|
||||
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
|
||||
psmouse->pktcnt - 1,
|
||||
psmouse->packet[psmouse->pktcnt - 1]);
|
||||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (psmouse->pktcnt == psmouse->pktsize) {
|
||||
priv->process_packet(psmouse);
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
@ -1192,6 +1309,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool alps_check_valid_firmware_id(unsigned char id[])
|
||||
{
|
||||
if (id[0] == 0x73)
|
||||
return true;
|
||||
|
||||
if (id[0] == 0x88 &&
|
||||
(id[1] == 0x07 ||
|
||||
id[1] == 0x08 ||
|
||||
(id[1] & 0xf0) == 0xb0 ||
|
||||
(id[1] & 0xf0) == 0xc0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int alps_enter_command_mode(struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char param[4];
|
||||
@ -1201,8 +1334,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
|
||||
param[0] != 0x73) {
|
||||
if (!alps_check_valid_firmware_id(param)) {
|
||||
psmouse_dbg(psmouse,
|
||||
"unknown response while entering command mode\n");
|
||||
return -1;
|
||||
@ -1660,6 +1792,45 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
|
||||
{
|
||||
int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys;
|
||||
struct alps_data *priv = psmouse->private;
|
||||
|
||||
reg = alps_command_mode_read_reg(psmouse, reg_pitch);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
|
||||
x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
|
||||
|
||||
y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */
|
||||
y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
|
||||
|
||||
reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
|
||||
x_electrode = 17 + x_electrode;
|
||||
|
||||
y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */
|
||||
y_electrode = 13 + y_electrode;
|
||||
|
||||
x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */
|
||||
y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */
|
||||
|
||||
priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
|
||||
priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
|
||||
|
||||
psmouse_dbg(psmouse,
|
||||
"pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n",
|
||||
x_pitch, y_pitch, x_electrode, y_electrode,
|
||||
x_phys / 10, y_phys / 10, priv->x_res, priv->y_res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
@ -1680,6 +1851,9 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
|
||||
alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
|
||||
goto error;
|
||||
|
||||
if (alps_get_v3_v7_resolution(psmouse, 0xc2da))
|
||||
goto error;
|
||||
|
||||
reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
|
||||
if (reg_val == -1)
|
||||
goto error;
|
||||
@ -1856,6 +2030,35 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_hw_init_v7(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int reg_val, ret = -1;
|
||||
|
||||
if (alps_enter_command_mode(psmouse) ||
|
||||
alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
|
||||
goto error;
|
||||
|
||||
if (alps_get_v3_v7_resolution(psmouse, 0xc397))
|
||||
goto error;
|
||||
|
||||
if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
|
||||
goto error;
|
||||
|
||||
reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4);
|
||||
if (reg_val == -1)
|
||||
goto error;
|
||||
if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02))
|
||||
goto error;
|
||||
|
||||
alps_exit_command_mode(psmouse);
|
||||
return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
|
||||
|
||||
error:
|
||||
alps_exit_command_mode(psmouse);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void alps_set_defaults(struct alps_data *priv)
|
||||
{
|
||||
priv->byte0 = 0x8f;
|
||||
@ -1914,6 +2117,21 @@ static void alps_set_defaults(struct alps_data *priv)
|
||||
priv->x_max = 2047;
|
||||
priv->y_max = 1535;
|
||||
break;
|
||||
case ALPS_PROTO_V7:
|
||||
priv->hw_init = alps_hw_init_v7;
|
||||
priv->process_packet = alps_process_packet_v7;
|
||||
priv->decode_fields = alps_decode_packet_v7;
|
||||
priv->set_abs_params = alps_set_abs_params_mt;
|
||||
priv->nibble_commands = alps_v3_nibble_commands;
|
||||
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
|
||||
priv->x_max = 0xfff;
|
||||
priv->y_max = 0x7ff;
|
||||
priv->byte0 = 0x48;
|
||||
priv->mask0 = 0x48;
|
||||
|
||||
if (priv->fw_ver[1] != 0xba)
|
||||
priv->flags |= ALPS_BUTTONPAD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1972,6 +2190,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
||||
alps_exit_command_mode(psmouse))
|
||||
return -EIO;
|
||||
|
||||
/* Save the Firmware version */
|
||||
memcpy(priv->fw_ver, ec, 3);
|
||||
|
||||
if (alps_match_table(psmouse, priv, e7, ec) == 0) {
|
||||
return 0;
|
||||
} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
|
||||
@ -1982,6 +2203,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
} else if (ec[0] == 0x88 &&
|
||||
((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
|
||||
priv->proto_version = ALPS_PROTO_V7;
|
||||
alps_set_defaults(priv);
|
||||
|
||||
return 0;
|
||||
} else if (ec[0] == 0x88 && ec[1] == 0x08) {
|
||||
priv->proto_version = ALPS_PROTO_V3;
|
||||
alps_set_defaults(priv);
|
||||
@ -1990,6 +2217,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
||||
priv->decode_fields = alps_decode_rushmore;
|
||||
priv->x_bits = 16;
|
||||
priv->y_bits = 12;
|
||||
priv->flags |= ALPS_IS_RUSHMORE;
|
||||
|
||||
/* hack to make addr_command, nibble_command available */
|
||||
psmouse->private = priv;
|
||||
@ -2044,17 +2272,21 @@ static void alps_set_abs_params_st(struct alps_data *priv,
|
||||
static void alps_set_abs_params_mt(struct alps_data *priv,
|
||||
struct input_dev *dev1)
|
||||
{
|
||||
set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
|
||||
input_mt_init_slots(dev1, 2, 0);
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0);
|
||||
|
||||
set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit);
|
||||
input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
|
||||
input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
|
||||
|
||||
input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
|
||||
INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
|
||||
|
||||
set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
|
||||
set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
|
||||
|
||||
input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0);
|
||||
input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0);
|
||||
/* V7 is real multi-touch */
|
||||
if (priv->proto_version == ALPS_PROTO_V7)
|
||||
clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
|
||||
}
|
||||
|
||||
int alps_init(struct psmouse *psmouse)
|
||||
@ -2100,7 +2332,9 @@ int alps_init(struct psmouse *psmouse)
|
||||
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
|
||||
|
||||
priv->set_abs_params(priv, dev1);
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
/* No pressure on V7 */
|
||||
if (priv->proto_version != ALPS_PROTO_V7)
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
|
||||
if (priv->flags & ALPS_WHEEL) {
|
||||
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
|
||||
@ -2117,6 +2351,9 @@ int alps_init(struct psmouse *psmouse)
|
||||
dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
|
||||
dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
|
||||
dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
|
||||
} else if (priv->flags & ALPS_BUTTONPAD) {
|
||||
set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit);
|
||||
clear_bit(BTN_RIGHT, dev1->keybit);
|
||||
} else {
|
||||
dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
|
||||
}
|
||||
|
@ -12,17 +12,39 @@
|
||||
#ifndef _ALPS_H
|
||||
#define _ALPS_H
|
||||
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
#define ALPS_PROTO_V1 1
|
||||
#define ALPS_PROTO_V2 2
|
||||
#define ALPS_PROTO_V3 3
|
||||
#define ALPS_PROTO_V4 4
|
||||
#define ALPS_PROTO_V5 5
|
||||
#define ALPS_PROTO_V6 6
|
||||
#define ALPS_PROTO_V7 7 /* t3btl t4s */
|
||||
|
||||
#define MAX_TOUCHES 2
|
||||
|
||||
#define DOLPHIN_COUNT_PER_ELECTRODE 64
|
||||
#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */
|
||||
#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */
|
||||
|
||||
/*
|
||||
* enum V7_PACKET_ID - defines the packet type for V7
|
||||
* V7_PACKET_ID_IDLE: There's no finger and no button activity.
|
||||
* V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
|
||||
* or there's button activities.
|
||||
* V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
|
||||
* V7_PACKET_ID_NEW: The finger position in slot is not continues from
|
||||
* previous packet.
|
||||
*/
|
||||
enum V7_PACKET_ID {
|
||||
V7_PACKET_ID_IDLE,
|
||||
V7_PACKET_ID_TWO,
|
||||
V7_PACKET_ID_MULTI,
|
||||
V7_PACKET_ID_NEW,
|
||||
V7_PACKET_ID_UNKNOWN,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct alps_model_info - touchpad ID table
|
||||
* @signature: E7 response string to match.
|
||||
@ -46,7 +68,7 @@ struct alps_model_info {
|
||||
unsigned char command_mode_resp;
|
||||
unsigned char proto_version;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -65,14 +87,19 @@ struct alps_nibble_commands {
|
||||
unsigned char data;
|
||||
};
|
||||
|
||||
struct alps_bitmap_point {
|
||||
int start_bit;
|
||||
int num_bits;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct alps_fields - decoded version of the report packet
|
||||
* @x_map: Bitmap of active X positions for MT.
|
||||
* @y_map: Bitmap of active Y positions for MT.
|
||||
* @fingers: Number of fingers for MT.
|
||||
* @x: X position for ST.
|
||||
* @y: Y position for ST.
|
||||
* @z: Z position for ST.
|
||||
* @pressure: Pressure.
|
||||
* @st: position for ST.
|
||||
* @mt: position for MT.
|
||||
* @first_mp: Packet is the first of a multi-packet report.
|
||||
* @is_mp: Packet is part of a multi-packet report.
|
||||
* @left: Left touchpad button is active.
|
||||
@ -86,9 +113,11 @@ struct alps_fields {
|
||||
unsigned int x_map;
|
||||
unsigned int y_map;
|
||||
unsigned int fingers;
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int z;
|
||||
|
||||
int pressure;
|
||||
struct input_mt_pos st;
|
||||
struct input_mt_pos mt[MAX_TOUCHES];
|
||||
|
||||
unsigned int first_mp:1;
|
||||
unsigned int is_mp:1;
|
||||
|
||||
@ -113,6 +142,7 @@ struct alps_fields {
|
||||
* known format for this model. The first byte of the report, ANDed with
|
||||
* mask0, should match byte0.
|
||||
* @mask0: The mask used to check the first byte of the report.
|
||||
* @fw_ver: cached copy of firmware version (EC report)
|
||||
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
|
||||
* @x_max: Largest possible X position value.
|
||||
* @y_max: Largest possible Y position value.
|
||||
@ -125,11 +155,7 @@ struct alps_fields {
|
||||
* @prev_fin: Finger bit from previous packet.
|
||||
* @multi_packet: Multi-packet data in progress.
|
||||
* @multi_data: Saved multi-packet data.
|
||||
* @x1: First X coordinate from last MT report.
|
||||
* @x2: Second X coordinate from last MT report.
|
||||
* @y1: First Y coordinate from last MT report.
|
||||
* @y2: Second Y coordinate from last MT report.
|
||||
* @fingers: Number of fingers from last MT report.
|
||||
* @f: Decoded packet data fields.
|
||||
* @quirks: Bitmap of ALPS_QUIRK_*.
|
||||
* @timer: Timer for flushing out the final report packet in the stream.
|
||||
*/
|
||||
@ -142,23 +168,25 @@ struct alps_data {
|
||||
int addr_command;
|
||||
unsigned char proto_version;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
unsigned char fw_ver[3];
|
||||
int flags;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int x_bits;
|
||||
int y_bits;
|
||||
unsigned int x_res;
|
||||
unsigned int y_res;
|
||||
|
||||
int (*hw_init)(struct psmouse *psmouse);
|
||||
void (*process_packet)(struct psmouse *psmouse);
|
||||
void (*decode_fields)(struct alps_fields *f, unsigned char *p,
|
||||
int (*decode_fields)(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse);
|
||||
void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1);
|
||||
|
||||
int prev_fin;
|
||||
int multi_packet;
|
||||
unsigned char multi_data[6];
|
||||
int x1, x2, y1, y2;
|
||||
int fingers;
|
||||
struct alps_fields f;
|
||||
u8 quirks;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
@ -170,6 +170,15 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev,
|
||||
serio_interrupt(kbd_dev->hv_serio, scan_code, 0);
|
||||
}
|
||||
spin_unlock_irqrestore(&kbd_dev->lock, flags);
|
||||
|
||||
/*
|
||||
* Only trigger a wakeup on key down, otherwise
|
||||
* "echo freeze > /sys/power/state" can't really enter the
|
||||
* state because the Enter-UP can trigger a wakeup at once.
|
||||
*/
|
||||
if (!(info & IS_BREAK))
|
||||
pm_wakeup_event(&hv_dev->device, 0);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -376,6 +385,9 @@ static int hv_kbd_probe(struct hv_device *hv_dev,
|
||||
goto err_close_vmbus;
|
||||
|
||||
serio_register_port(kbd_dev->hv_serio);
|
||||
|
||||
device_init_wakeup(&hv_dev->device, true);
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_vmbus:
|
||||
@ -390,6 +402,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev)
|
||||
{
|
||||
struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
|
||||
|
||||
device_init_wakeup(&hv_dev->device, false);
|
||||
serio_unregister_port(kbd_dev->hv_serio);
|
||||
vmbus_close(hv_dev->channel);
|
||||
kfree(kbd_dev);
|
||||
|
@ -73,20 +73,14 @@ config TABLET_USB_KBTAB
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called kbtab.
|
||||
|
||||
config TABLET_USB_WACOM
|
||||
tristate "Wacom Intuos/Graphire tablet support (USB)"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select POWER_SUPPLY
|
||||
select USB
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
config TABLET_SERIAL_WACOM4
|
||||
tristate "Wacom protocol 4 serial tablet support"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Wacom Intuos
|
||||
or Graphire tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
Say Y here if you want to use Wacom protocol 4 serial tablets.
|
||||
E.g. serial versions of the Cintiq, Graphire or Penpartner.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom.
|
||||
module will be called wacom_serial4.
|
||||
|
||||
endif
|
||||
|
@ -2,12 +2,10 @@
|
||||
# Makefile for the tablet drivers
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
|
||||
obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
|
||||
obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
|
||||
obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
|
||||
obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
|
||||
obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o
|
||||
|
620
drivers/input/tablet/wacom_serial4.c
Normal file
620
drivers/input/tablet/wacom_serial4.c
Normal file
@ -0,0 +1,620 @@
|
||||
/*
|
||||
* Wacom protocol 4 serial tablet driver
|
||||
*
|
||||
* Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright 2011-2012 Julian Squires <julian@cipht.net>
|
||||
*
|
||||
* 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. See the file COPYING in the main directory of
|
||||
* this archive for more details.
|
||||
*
|
||||
* Many thanks to Bill Seremetis, without whom PenPartner support
|
||||
* would not have been possible. Thanks to Patrick Mahoney.
|
||||
*
|
||||
* This driver was developed with reference to much code written by others,
|
||||
* particularly:
|
||||
* - elo, gunze drivers by Vojtech Pavlik <vojtech@ucw.cz>;
|
||||
* - wacom_w8001 driver by Jaya Kumar <jayakumar.lkml@gmail.com>;
|
||||
* - the USB wacom input driver, credited to many people
|
||||
* (see drivers/input/tablet/wacom.h);
|
||||
* - new and old versions of linuxwacom / xf86-input-wacom credited to
|
||||
* Frederic Lepied, France. <Lepied@XFree86.org> and
|
||||
* Ping Cheng, Wacom. <pingc@wacom.com>;
|
||||
* - and xf86wacom.c (a presumably ancient version of the linuxwacom code),
|
||||
* by Frederic Lepied and Raph Levien <raph@gtk.org>.
|
||||
*
|
||||
* To do:
|
||||
* - support pad buttons; (requires access to a model with pad buttons)
|
||||
* - support (protocol 4-style) tilt (requires access to a > 1.4 rom model)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wacom serial protocol 4 documentation taken from linuxwacom-0.9.9 code,
|
||||
* protocol 4 uses 7 or 9 byte of data in the following format:
|
||||
*
|
||||
* Byte 1
|
||||
* bit 7 Sync bit always 1
|
||||
* bit 6 Pointing device detected
|
||||
* bit 5 Cursor = 0 / Stylus = 1
|
||||
* bit 4 Reserved
|
||||
* bit 3 1 if a button on the pointing device has been pressed
|
||||
* bit 2 P0 (optional)
|
||||
* bit 1 X15
|
||||
* bit 0 X14
|
||||
*
|
||||
* Byte 2
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = X13 - X7
|
||||
*
|
||||
* Byte 3
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = X6 - X0
|
||||
*
|
||||
* Byte 4
|
||||
* bit 7 Always 0
|
||||
* bit 6 B3
|
||||
* bit 5 B2
|
||||
* bit 4 B1
|
||||
* bit 3 B0
|
||||
* bit 2 P1 (optional)
|
||||
* bit 1 Y15
|
||||
* bit 0 Y14
|
||||
*
|
||||
* Byte 5
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = Y13 - Y7
|
||||
*
|
||||
* Byte 6
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = Y6 - Y0
|
||||
*
|
||||
* Byte 7
|
||||
* bit 7 Always 0
|
||||
* bit 6 Sign of pressure data; or wheel-rel for cursor tool
|
||||
* bit 5 P7; or REL1 for cursor tool
|
||||
* bit 4 P6; or REL0 for cursor tool
|
||||
* bit 3 P5
|
||||
* bit 2 P4
|
||||
* bit 1 P3
|
||||
* bit 0 P2
|
||||
*
|
||||
* byte 8 and 9 are optional and present only
|
||||
* in tilt mode.
|
||||
*
|
||||
* Byte 8
|
||||
* bit 7 Always 0
|
||||
* bit 6 Sign of tilt X
|
||||
* bit 5 Xt6
|
||||
* bit 4 Xt5
|
||||
* bit 3 Xt4
|
||||
* bit 2 Xt3
|
||||
* bit 1 Xt2
|
||||
* bit 0 Xt1
|
||||
*
|
||||
* Byte 9
|
||||
* bit 7 Always 0
|
||||
* bit 6 Sign of tilt Y
|
||||
* bit 5 Yt6
|
||||
* bit 4 Yt5
|
||||
* bit 3 Yt4
|
||||
* bit 2 Yt3
|
||||
* bit 1 Yt2
|
||||
* bit 0 Yt1
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
MODULE_AUTHOR("Julian Squires <julian@cipht.net>, Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Wacom protocol 4 serial tablet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define REQUEST_MODEL_AND_ROM_VERSION "~#"
|
||||
#define REQUEST_MAX_COORDINATES "~C\r"
|
||||
#define REQUEST_CONFIGURATION_STRING "~R\r"
|
||||
#define REQUEST_RESET_TO_PROTOCOL_IV "\r#"
|
||||
/*
|
||||
* Note: sending "\r$\r" causes at least the Digitizer II to send
|
||||
* packets in ASCII instead of binary. "\r#" seems to undo that.
|
||||
*/
|
||||
|
||||
#define COMMAND_START_SENDING_PACKETS "ST\r"
|
||||
#define COMMAND_STOP_SENDING_PACKETS "SP\r"
|
||||
#define COMMAND_MULTI_MODE_INPUT "MU1\r"
|
||||
#define COMMAND_ORIGIN_IN_UPPER_LEFT "OC1\r"
|
||||
#define COMMAND_ENABLE_ALL_MACRO_BUTTONS "~M0\r"
|
||||
#define COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS "~M1\r"
|
||||
#define COMMAND_TRANSMIT_AT_MAX_RATE "IT0\r"
|
||||
#define COMMAND_DISABLE_INCREMENTAL_MODE "IN0\r"
|
||||
#define COMMAND_ENABLE_CONTINUOUS_MODE "SR\r"
|
||||
#define COMMAND_ENABLE_PRESSURE_MODE "PH1\r"
|
||||
#define COMMAND_Z_FILTER "ZF1\r"
|
||||
|
||||
/* Note that this is a protocol 4 packet without tilt information. */
|
||||
#define PACKET_LENGTH 7
|
||||
#define DATA_SIZE 32
|
||||
|
||||
/* flags */
|
||||
#define F_COVERS_SCREEN 0x01
|
||||
#define F_HAS_STYLUS2 0x02
|
||||
#define F_HAS_SCROLLWHEEL 0x04
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
|
||||
enum { STYLUS = 1, ERASER, CURSOR };
|
||||
|
||||
static const struct {
|
||||
int device_id;
|
||||
int input_id;
|
||||
} tools[] = {
|
||||
{ 0, 0 },
|
||||
{ STYLUS_DEVICE_ID, BTN_TOOL_PEN },
|
||||
{ ERASER_DEVICE_ID, BTN_TOOL_RUBBER },
|
||||
{ CURSOR_DEVICE_ID, BTN_TOOL_MOUSE },
|
||||
};
|
||||
|
||||
struct wacom {
|
||||
struct input_dev *dev;
|
||||
struct completion cmd_done;
|
||||
int result;
|
||||
u8 expect;
|
||||
u8 eraser_mask;
|
||||
unsigned int extra_z_bits;
|
||||
unsigned int flags;
|
||||
unsigned int res_x, res_y;
|
||||
unsigned int max_x, max_y;
|
||||
unsigned int tool;
|
||||
unsigned int idx;
|
||||
u8 data[DATA_SIZE];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
enum {
|
||||
MODEL_CINTIQ = 0x504C, /* PL */
|
||||
MODEL_CINTIQ2 = 0x4454, /* DT */
|
||||
MODEL_DIGITIZER_II = 0x5544, /* UD */
|
||||
MODEL_GRAPHIRE = 0x4554, /* ET */
|
||||
MODEL_PENPARTNER = 0x4354, /* CT */
|
||||
};
|
||||
|
||||
static void wacom_handle_model_response(struct wacom *wacom)
|
||||
{
|
||||
int major_v, minor_v, r = 0;
|
||||
char *p;
|
||||
|
||||
p = strrchr(wacom->data, 'V');
|
||||
if (p)
|
||||
r = sscanf(p + 1, "%u.%u", &major_v, &minor_v);
|
||||
if (r != 2)
|
||||
major_v = minor_v = 0;
|
||||
|
||||
switch (wacom->data[2] << 8 | wacom->data[3]) {
|
||||
case MODEL_CINTIQ: /* UNTESTED */
|
||||
case MODEL_CINTIQ2:
|
||||
if ((wacom->data[2] << 8 | wacom->data[3]) == MODEL_CINTIQ) {
|
||||
wacom->dev->name = "Wacom Cintiq";
|
||||
wacom->dev->id.version = MODEL_CINTIQ;
|
||||
} else {
|
||||
wacom->dev->name = "Wacom Cintiq II";
|
||||
wacom->dev->id.version = MODEL_CINTIQ2;
|
||||
}
|
||||
wacom->res_x = 508;
|
||||
wacom->res_y = 508;
|
||||
|
||||
switch (wacom->data[5] << 8 | wacom->data[6]) {
|
||||
case 0x3731: /* PL-710 */
|
||||
wacom->res_x = 2540;
|
||||
wacom->res_y = 2540;
|
||||
/* fall through */
|
||||
case 0x3535: /* PL-550 */
|
||||
case 0x3830: /* PL-800 */
|
||||
wacom->extra_z_bits = 2;
|
||||
}
|
||||
|
||||
wacom->flags = F_COVERS_SCREEN;
|
||||
break;
|
||||
|
||||
case MODEL_PENPARTNER:
|
||||
wacom->dev->name = "Wacom Penpartner";
|
||||
wacom->dev->id.version = MODEL_PENPARTNER;
|
||||
wacom->res_x = 1000;
|
||||
wacom->res_y = 1000;
|
||||
break;
|
||||
|
||||
case MODEL_GRAPHIRE:
|
||||
wacom->dev->name = "Wacom Graphire";
|
||||
wacom->dev->id.version = MODEL_GRAPHIRE;
|
||||
wacom->res_x = 1016;
|
||||
wacom->res_y = 1016;
|
||||
wacom->max_x = 5103;
|
||||
wacom->max_y = 3711;
|
||||
wacom->extra_z_bits = 2;
|
||||
wacom->eraser_mask = 0x08;
|
||||
wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL;
|
||||
break;
|
||||
|
||||
case MODEL_DIGITIZER_II:
|
||||
wacom->dev->name = "Wacom Digitizer II";
|
||||
wacom->dev->id.version = MODEL_DIGITIZER_II;
|
||||
if (major_v == 1 && minor_v <= 2)
|
||||
wacom->extra_z_bits = 0; /* UNTESTED */
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&wacom->dev->dev, "Unsupported Wacom model %s\n",
|
||||
wacom->data);
|
||||
wacom->result = -ENODEV;
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(&wacom->dev->dev, "%s tablet, version %u.%u\n",
|
||||
wacom->dev->name, major_v, minor_v);
|
||||
}
|
||||
|
||||
static void wacom_handle_configuration_response(struct wacom *wacom)
|
||||
{
|
||||
int r, skip;
|
||||
|
||||
dev_dbg(&wacom->dev->dev, "Configuration string: %s\n", wacom->data);
|
||||
r = sscanf(wacom->data, "~R%x,%u,%u,%u,%u", &skip, &skip, &skip,
|
||||
&wacom->res_x, &wacom->res_y);
|
||||
if (r != 5)
|
||||
dev_warn(&wacom->dev->dev, "could not get resolution\n");
|
||||
}
|
||||
|
||||
static void wacom_handle_coordinates_response(struct wacom *wacom)
|
||||
{
|
||||
int r;
|
||||
|
||||
dev_dbg(&wacom->dev->dev, "Coordinates string: %s\n", wacom->data);
|
||||
r = sscanf(wacom->data, "~C%u,%u", &wacom->max_x, &wacom->max_y);
|
||||
if (r != 2)
|
||||
dev_warn(&wacom->dev->dev, "could not get max coordinates\n");
|
||||
}
|
||||
|
||||
static void wacom_handle_response(struct wacom *wacom)
|
||||
{
|
||||
if (wacom->data[0] != '~' || wacom->data[1] != wacom->expect) {
|
||||
dev_err(&wacom->dev->dev,
|
||||
"Wacom got an unexpected response: %s\n", wacom->data);
|
||||
wacom->result = -EIO;
|
||||
} else {
|
||||
wacom->result = 0;
|
||||
|
||||
switch (wacom->data[1]) {
|
||||
case '#':
|
||||
wacom_handle_model_response(wacom);
|
||||
break;
|
||||
case 'R':
|
||||
wacom_handle_configuration_response(wacom);
|
||||
break;
|
||||
case 'C':
|
||||
wacom_handle_coordinates_response(wacom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
complete(&wacom->cmd_done);
|
||||
}
|
||||
|
||||
static void wacom_handle_packet(struct wacom *wacom)
|
||||
{
|
||||
u8 in_proximity_p, stylus_p, button;
|
||||
unsigned int tool;
|
||||
int x, y, z;
|
||||
|
||||
in_proximity_p = wacom->data[0] & 0x40;
|
||||
stylus_p = wacom->data[0] & 0x20;
|
||||
button = (wacom->data[3] & 0x78) >> 3;
|
||||
x = (wacom->data[0] & 3) << 14 | wacom->data[1]<<7 | wacom->data[2];
|
||||
y = (wacom->data[3] & 3) << 14 | wacom->data[4]<<7 | wacom->data[5];
|
||||
|
||||
if (in_proximity_p && stylus_p) {
|
||||
z = wacom->data[6] & 0x7f;
|
||||
if (wacom->extra_z_bits >= 1)
|
||||
z = z << 1 | (wacom->data[3] & 0x4) >> 2;
|
||||
if (wacom->extra_z_bits > 1)
|
||||
z = z << 1 | (wacom->data[0] & 0x4) >> 2;
|
||||
z = z ^ (0x40 << wacom->extra_z_bits);
|
||||
} else {
|
||||
z = -1;
|
||||
}
|
||||
|
||||
if (stylus_p)
|
||||
tool = (button & wacom->eraser_mask) ? ERASER : STYLUS;
|
||||
else
|
||||
tool = CURSOR;
|
||||
|
||||
if (tool != wacom->tool && wacom->tool != 0) {
|
||||
input_report_key(wacom->dev, tools[wacom->tool].input_id, 0);
|
||||
input_sync(wacom->dev);
|
||||
}
|
||||
wacom->tool = tool;
|
||||
|
||||
input_report_key(wacom->dev, tools[tool].input_id, in_proximity_p);
|
||||
input_report_abs(wacom->dev, ABS_MISC,
|
||||
in_proximity_p ? tools[tool].device_id : 0);
|
||||
input_report_abs(wacom->dev, ABS_X, x);
|
||||
input_report_abs(wacom->dev, ABS_Y, y);
|
||||
input_report_abs(wacom->dev, ABS_PRESSURE, z);
|
||||
if (stylus_p) {
|
||||
input_report_key(wacom->dev, BTN_TOUCH, button & 1);
|
||||
input_report_key(wacom->dev, BTN_STYLUS, button & 2);
|
||||
input_report_key(wacom->dev, BTN_STYLUS2, button & 4);
|
||||
} else {
|
||||
input_report_key(wacom->dev, BTN_LEFT, button & 1);
|
||||
input_report_key(wacom->dev, BTN_RIGHT, button & 2);
|
||||
input_report_key(wacom->dev, BTN_MIDDLE, button & 4);
|
||||
/* handle relative wheel for non-stylus device */
|
||||
z = (wacom->data[6] & 0x30) >> 4;
|
||||
if (wacom->data[6] & 0x40)
|
||||
z = -z;
|
||||
input_report_rel(wacom->dev, REL_WHEEL, z);
|
||||
}
|
||||
input_sync(wacom->dev);
|
||||
}
|
||||
|
||||
static void wacom_clear_data_buf(struct wacom *wacom)
|
||||
{
|
||||
memset(wacom->data, 0, DATA_SIZE);
|
||||
wacom->idx = 0;
|
||||
}
|
||||
|
||||
static irqreturn_t wacom_interrupt(struct serio *serio, unsigned char data,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct wacom *wacom = serio_get_drvdata(serio);
|
||||
|
||||
if (data & 0x80)
|
||||
wacom->idx = 0;
|
||||
|
||||
/*
|
||||
* We're either expecting a carriage return-terminated ASCII
|
||||
* response string, or a seven-byte packet with the MSB set on
|
||||
* the first byte.
|
||||
*
|
||||
* Note however that some tablets (the PenPartner, for
|
||||
* example) don't send a carriage return at the end of a
|
||||
* command. We handle these by waiting for timeout.
|
||||
*/
|
||||
if (data == '\r' && !(wacom->data[0] & 0x80)) {
|
||||
wacom_handle_response(wacom);
|
||||
wacom_clear_data_buf(wacom);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Leave place for 0 termination */
|
||||
if (wacom->idx > (DATA_SIZE - 2)) {
|
||||
dev_dbg(&wacom->dev->dev,
|
||||
"throwing away %d bytes of garbage\n", wacom->idx);
|
||||
wacom_clear_data_buf(wacom);
|
||||
}
|
||||
wacom->data[wacom->idx++] = data;
|
||||
|
||||
if (wacom->idx == PACKET_LENGTH && (wacom->data[0] & 0x80)) {
|
||||
wacom_handle_packet(wacom);
|
||||
wacom_clear_data_buf(wacom);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void wacom_disconnect(struct serio *serio)
|
||||
{
|
||||
struct wacom *wacom = serio_get_drvdata(serio);
|
||||
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_unregister_device(wacom->dev);
|
||||
kfree(wacom);
|
||||
}
|
||||
|
||||
static int wacom_send(struct serio *serio, const u8 *command)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
for (; !err && *command; command++)
|
||||
err = serio_write(serio, *command);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int wacom_send_setup_string(struct wacom *wacom, struct serio *serio)
|
||||
{
|
||||
const u8 *cmd;
|
||||
|
||||
switch (wacom->dev->id.version) {
|
||||
case MODEL_CINTIQ: /* UNTESTED */
|
||||
cmd = COMMAND_ORIGIN_IN_UPPER_LEFT
|
||||
COMMAND_TRANSMIT_AT_MAX_RATE
|
||||
COMMAND_ENABLE_CONTINUOUS_MODE
|
||||
COMMAND_START_SENDING_PACKETS;
|
||||
break;
|
||||
|
||||
case MODEL_PENPARTNER:
|
||||
cmd = COMMAND_ENABLE_PRESSURE_MODE
|
||||
COMMAND_START_SENDING_PACKETS;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmd = COMMAND_MULTI_MODE_INPUT
|
||||
COMMAND_ORIGIN_IN_UPPER_LEFT
|
||||
COMMAND_ENABLE_ALL_MACRO_BUTTONS
|
||||
COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS
|
||||
COMMAND_TRANSMIT_AT_MAX_RATE
|
||||
COMMAND_DISABLE_INCREMENTAL_MODE
|
||||
COMMAND_ENABLE_CONTINUOUS_MODE
|
||||
COMMAND_Z_FILTER
|
||||
COMMAND_START_SENDING_PACKETS;
|
||||
break;
|
||||
}
|
||||
|
||||
return wacom_send(serio, cmd);
|
||||
}
|
||||
|
||||
static int wacom_send_and_wait(struct wacom *wacom, struct serio *serio,
|
||||
const u8 *cmd, const char *desc)
|
||||
{
|
||||
int err;
|
||||
unsigned long u;
|
||||
|
||||
wacom->expect = cmd[1];
|
||||
init_completion(&wacom->cmd_done);
|
||||
|
||||
err = wacom_send(serio, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
u = wait_for_completion_timeout(&wacom->cmd_done, HZ);
|
||||
if (u == 0) {
|
||||
/* Timeout, process what we've received. */
|
||||
wacom_handle_response(wacom);
|
||||
}
|
||||
|
||||
wacom->expect = 0;
|
||||
return wacom->result;
|
||||
}
|
||||
|
||||
static int wacom_setup(struct wacom *wacom, struct serio *serio)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Note that setting the link speed is the job of inputattach.
|
||||
* We assume that reset negotiation has already happened,
|
||||
* here. */
|
||||
err = wacom_send_and_wait(wacom, serio, REQUEST_MODEL_AND_ROM_VERSION,
|
||||
"model and version");
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(wacom->res_x && wacom->res_y)) {
|
||||
err = wacom_send_and_wait(wacom, serio,
|
||||
REQUEST_CONFIGURATION_STRING,
|
||||
"configuration string");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!(wacom->max_x && wacom->max_y)) {
|
||||
err = wacom_send_and_wait(wacom, serio,
|
||||
REQUEST_MAX_COORDINATES,
|
||||
"coordinates string");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return wacom_send_setup_string(wacom, serio);
|
||||
}
|
||||
|
||||
static int wacom_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct wacom *wacom;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!wacom || !input_dev)
|
||||
goto free_device;
|
||||
|
||||
wacom->dev = input_dev;
|
||||
wacom->extra_z_bits = 1;
|
||||
wacom->eraser_mask = 0x04;
|
||||
wacom->tool = wacom->idx = 0;
|
||||
snprintf(wacom->phys, sizeof(wacom->phys), "%s/input0", serio->phys);
|
||||
input_dev->phys = wacom->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_WACOM_IV;
|
||||
input_dev->id.product = serio->id.extra;
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] =
|
||||
BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL);
|
||||
set_bit(ABS_MISC, input_dev->absbit);
|
||||
set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
|
||||
set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
set_bit(BTN_LEFT, input_dev->keybit);
|
||||
set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
set_bit(BTN_MIDDLE, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, wacom);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto free_device;
|
||||
|
||||
err = wacom_setup(wacom, serio);
|
||||
if (err)
|
||||
goto close_serio;
|
||||
|
||||
set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
|
||||
if (!(wacom->flags & F_COVERS_SCREEN))
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
if (wacom->flags & F_HAS_STYLUS2)
|
||||
__set_bit(BTN_STYLUS2, input_dev->keybit);
|
||||
|
||||
if (wacom->flags & F_HAS_SCROLLWHEEL)
|
||||
__set_bit(REL_WHEEL, input_dev->relbit);
|
||||
|
||||
input_abs_set_res(wacom->dev, ABS_X, wacom->res_x);
|
||||
input_abs_set_res(wacom->dev, ABS_Y, wacom->res_y);
|
||||
input_set_abs_params(wacom->dev, ABS_X, 0, wacom->max_x, 0, 0);
|
||||
input_set_abs_params(wacom->dev, ABS_Y, 0, wacom->max_y, 0, 0);
|
||||
input_set_abs_params(wacom->dev, ABS_PRESSURE, -1,
|
||||
(1 << (7 + wacom->extra_z_bits)) - 1, 0, 0);
|
||||
|
||||
err = input_register_device(wacom->dev);
|
||||
if (err)
|
||||
goto close_serio;
|
||||
|
||||
return 0;
|
||||
|
||||
close_serio:
|
||||
serio_close(serio);
|
||||
free_device:
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_free_device(input_dev);
|
||||
kfree(wacom);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct serio_device_id wacom_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_WACOM_IV,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, wacom_serio_ids);
|
||||
|
||||
static struct serio_driver wacom_drv = {
|
||||
.driver = {
|
||||
.name = "wacom_serial4",
|
||||
},
|
||||
.description = "Wacom protocol 4 serial tablet driver",
|
||||
.id_table = wacom_serio_ids,
|
||||
.interrupt = wacom_interrupt,
|
||||
.connect = wacom_connect,
|
||||
.disconnect = wacom_disconnect,
|
||||
};
|
||||
|
||||
module_serio_driver(wacom_drv);
|
@ -471,6 +471,18 @@ config TOUCHSCREEN_HP7XX
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called jornada720_ts.
|
||||
|
||||
config TOUCHSCREEN_IPAQ_MICRO
|
||||
tristate "HP iPAQ Atmel Micro ASIC touchscreen"
|
||||
depends on MFD_IPAQ_MICRO
|
||||
help
|
||||
Say Y here to enable support for the touchscreen attached to
|
||||
the Atmel Micro peripheral controller on iPAQ h3100/h3600/h3700
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ipaq-micro-ts.
|
||||
|
||||
config TOUCHSCREEN_HTCPEN
|
||||
tristate "HTC Shift X9500 touchscreen"
|
||||
depends on ISA
|
||||
|
@ -46,6 +46,7 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||
|
@ -1302,8 +1302,10 @@ static int ads7846_probe(struct spi_device *spi)
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
if (!pdata) {
|
||||
pdata = ads7846_probe_dt(&spi->dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
if (IS_ERR(pdata)) {
|
||||
err = PTR_ERR(pdata);
|
||||
goto err_free_mem;
|
||||
}
|
||||
}
|
||||
|
||||
ts->model = pdata->model ? : 7846;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -733,8 +733,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
|
||||
static void
|
||||
edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
|
||||
{
|
||||
if (tsdata->debug_dir)
|
||||
debugfs_remove_recursive(tsdata->debug_dir);
|
||||
debugfs_remove_recursive(tsdata->debug_dir);
|
||||
kfree(tsdata->raw_buffer);
|
||||
}
|
||||
|
||||
|
166
drivers/input/touchscreen/ipaq-micro-ts.c
Normal file
166
drivers/input/touchscreen/ipaq-micro-ts.c
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* h3600 atmel micro companion support, touchscreen subdevice
|
||||
* Author : Alessandro Gardich <gremlin@gremlin.it>
|
||||
* Author : Dmitry Artamonow <mad_soft@inbox.ru>
|
||||
* Author : Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/ipaq-micro.h>
|
||||
|
||||
struct touchscreen_data {
|
||||
struct input_dev *input;
|
||||
struct ipaq_micro *micro;
|
||||
};
|
||||
|
||||
static void micro_ts_receive(void *data, int len, unsigned char *msg)
|
||||
{
|
||||
struct touchscreen_data *ts = data;
|
||||
|
||||
if (len == 4) {
|
||||
input_report_abs(ts->input, ABS_X,
|
||||
be16_to_cpup((__be16 *) &msg[2]));
|
||||
input_report_abs(ts->input, ABS_Y,
|
||||
be16_to_cpup((__be16 *) &msg[0]));
|
||||
input_report_key(ts->input, BTN_TOUCH, 1);
|
||||
input_sync(ts->input);
|
||||
} else if (len == 0) {
|
||||
input_report_abs(ts->input, ABS_X, 0);
|
||||
input_report_abs(ts->input, ABS_Y, 0);
|
||||
input_report_key(ts->input, BTN_TOUCH, 0);
|
||||
input_sync(ts->input);
|
||||
}
|
||||
}
|
||||
|
||||
static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable)
|
||||
{
|
||||
struct ipaq_micro *micro = ts->micro;
|
||||
|
||||
spin_lock_irq(µ->lock);
|
||||
|
||||
if (enable) {
|
||||
micro->ts = micro_ts_receive;
|
||||
micro->ts_data = ts;
|
||||
} else {
|
||||
micro->ts = NULL;
|
||||
micro->ts_data = NULL;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&ts->micro->lock);
|
||||
}
|
||||
|
||||
static int micro_ts_open(struct input_dev *input)
|
||||
{
|
||||
struct touchscreen_data *ts = input_get_drvdata(input);
|
||||
|
||||
micro_ts_toggle_receive(ts, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void micro_ts_close(struct input_dev *input)
|
||||
{
|
||||
struct touchscreen_data *ts = input_get_drvdata(input);
|
||||
|
||||
micro_ts_toggle_receive(ts, false);
|
||||
}
|
||||
|
||||
static int micro_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent);
|
||||
struct touchscreen_data *ts;
|
||||
int error;
|
||||
|
||||
ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->micro = micro;
|
||||
|
||||
ts->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!ts->input) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ts->input->name = "ipaq micro ts";
|
||||
ts->input->open = micro_ts_open;
|
||||
ts->input->close = micro_ts_close;
|
||||
|
||||
input_set_drvdata(ts->input, ts);
|
||||
|
||||
input_set_capability(ts->input, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(ts->input, EV_ABS, ABS_X);
|
||||
input_set_capability(ts->input, EV_ABS, ABS_Y);
|
||||
input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0);
|
||||
input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0);
|
||||
|
||||
error = input_register_device(ts->input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "error registering touch input\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
|
||||
dev_info(&pdev->dev, "iPAQ micro touchscreen\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int micro_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct touchscreen_data *ts = dev_get_drvdata(dev);
|
||||
|
||||
micro_ts_toggle_receive(ts, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int micro_ts_resume(struct device *dev)
|
||||
{
|
||||
struct touchscreen_data *ts = dev_get_drvdata(dev);
|
||||
struct input_dev *input = ts->input;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users)
|
||||
micro_ts_toggle_receive(ts, true);
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops micro_ts_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver micro_ts_device_driver = {
|
||||
.driver = {
|
||||
.name = "ipaq-micro-ts",
|
||||
.pm = µ_ts_dev_pm_ops,
|
||||
},
|
||||
.probe = micro_ts_probe,
|
||||
};
|
||||
module_platform_driver(micro_ts_device_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen");
|
||||
MODULE_ALIAS("platform:ipaq-micro-ts");
|
@ -36,22 +36,21 @@ struct jornada_ts {
|
||||
|
||||
static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
|
||||
{
|
||||
/* 3 low word X samples */
|
||||
jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* 3 low word X samples */
|
||||
jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
/* 3 low word Y samples */
|
||||
jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* 3 low word Y samples */
|
||||
jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
/* combined x samples bits */
|
||||
jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* combined x samples bits */
|
||||
jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* combined y samples bits */
|
||||
jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
/* combined y samples bits */
|
||||
jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
}
|
||||
|
||||
static int jornada720_ts_average(int coords[4])
|
||||
@ -104,13 +103,13 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
|
||||
if (!jornada_ts)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!jornada_ts || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, jornada_ts);
|
||||
|
||||
@ -126,36 +125,18 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
||||
input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
|
||||
|
||||
error = request_irq(IRQ_GPIO9,
|
||||
jornada720_ts_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"HP7XX Touchscreen driver", pdev);
|
||||
error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
|
||||
jornada720_ts_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"HP7XX Touchscreen driver", pdev);
|
||||
if (error) {
|
||||
printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
|
||||
goto fail1;
|
||||
dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(jornada_ts->dev);
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
free_irq(IRQ_GPIO9, pdev);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(jornada_ts);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int jornada720_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(IRQ_GPIO9, pdev);
|
||||
input_unregister_device(jornada_ts->dev);
|
||||
kfree(jornada_ts);
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -165,7 +146,6 @@ MODULE_ALIAS("platform:jornada_ts");
|
||||
|
||||
static struct platform_driver jornada720_ts_driver = {
|
||||
.probe = jornada720_ts_probe,
|
||||
.remove = jornada720_ts_remove,
|
||||
.driver = {
|
||||
.name = "jornada_ts",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -248,8 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mcs5000_ts_suspend(struct device *dev)
|
||||
static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
@ -259,7 +258,7 @@ static int mcs5000_ts_suspend(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs5000_ts_resume(struct device *dev)
|
||||
static int __maybe_unused mcs5000_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct mcs5000_ts_data *data = i2c_get_clientdata(client);
|
||||
@ -269,7 +268,6 @@ static int mcs5000_ts_resume(struct device *dev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
|
||||
|
||||
|
@ -23,22 +23,51 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/pixcir_ts.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */
|
||||
|
||||
struct pixcir_i2c_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
const struct pixcir_ts_platform_data *chip;
|
||||
const struct pixcir_ts_platform_data *pdata;
|
||||
bool running;
|
||||
int max_fingers; /* Max fingers supported in this instance */
|
||||
};
|
||||
|
||||
static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
||||
struct pixcir_touch {
|
||||
int x;
|
||||
int y;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct pixcir_report_data {
|
||||
int num_touches;
|
||||
struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
|
||||
};
|
||||
|
||||
static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
|
||||
struct pixcir_report_data *report)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = data;
|
||||
u8 rdbuf[10], wrbuf[1] = { 0 };
|
||||
u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5];
|
||||
u8 wrbuf[1] = { 0 };
|
||||
u8 *bufptr;
|
||||
u8 touch;
|
||||
int ret;
|
||||
int ret, i;
|
||||
int readsize;
|
||||
const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip;
|
||||
|
||||
memset(report, 0, sizeof(struct pixcir_report_data));
|
||||
|
||||
i = chip->has_hw_ids ? 1 : 0;
|
||||
readsize = 2 + tsdata->max_fingers * (4 + i);
|
||||
if (readsize > sizeof(rdbuf))
|
||||
readsize = sizeof(rdbuf);
|
||||
|
||||
ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
|
||||
if (ret != sizeof(wrbuf)) {
|
||||
@ -48,7 +77,7 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
||||
return;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
|
||||
ret = i2c_master_recv(tsdata->client, rdbuf, readsize);
|
||||
if (ret != sizeof(rdbuf)) {
|
||||
dev_err(&tsdata->client->dev,
|
||||
"%s: i2c_master_recv failed(), ret=%d\n",
|
||||
@ -56,45 +85,103 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
||||
return;
|
||||
}
|
||||
|
||||
touch = rdbuf[0];
|
||||
if (touch) {
|
||||
u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
|
||||
u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
|
||||
u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
|
||||
u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
|
||||
touch = rdbuf[0] & 0x7;
|
||||
if (touch > tsdata->max_fingers)
|
||||
touch = tsdata->max_fingers;
|
||||
|
||||
input_report_key(tsdata->input, BTN_TOUCH, 1);
|
||||
input_report_abs(tsdata->input, ABS_X, posx1);
|
||||
input_report_abs(tsdata->input, ABS_Y, posy1);
|
||||
report->num_touches = touch;
|
||||
bufptr = &rdbuf[2];
|
||||
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1);
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1);
|
||||
input_mt_sync(tsdata->input);
|
||||
for (i = 0; i < touch; i++) {
|
||||
report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
|
||||
report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
|
||||
|
||||
if (touch == 2) {
|
||||
input_report_abs(tsdata->input,
|
||||
ABS_MT_POSITION_X, posx2);
|
||||
input_report_abs(tsdata->input,
|
||||
ABS_MT_POSITION_Y, posy2);
|
||||
input_mt_sync(tsdata->input);
|
||||
if (chip->has_hw_ids) {
|
||||
report->touches[i].id = bufptr[4];
|
||||
bufptr = bufptr + 5;
|
||||
} else {
|
||||
bufptr = bufptr + 4;
|
||||
}
|
||||
} else {
|
||||
input_report_key(tsdata->input, BTN_TOUCH, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
struct pixcir_report_data *report)
|
||||
{
|
||||
struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
|
||||
int slots[PIXCIR_MAX_SLOTS];
|
||||
struct pixcir_touch *touch;
|
||||
int n, i, slot;
|
||||
struct device *dev = &ts->client->dev;
|
||||
const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip;
|
||||
|
||||
n = report->num_touches;
|
||||
if (n > PIXCIR_MAX_SLOTS)
|
||||
n = PIXCIR_MAX_SLOTS;
|
||||
|
||||
if (!chip->has_hw_ids) {
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
pos[i].x = touch->x;
|
||||
pos[i].y = touch->y;
|
||||
}
|
||||
|
||||
input_mt_assign_slots(ts->input, slots, pos, n);
|
||||
}
|
||||
|
||||
input_sync(tsdata->input);
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
|
||||
if (chip->has_hw_ids) {
|
||||
slot = input_mt_get_slot_by_key(ts->input, touch->id);
|
||||
if (slot < 0) {
|
||||
dev_dbg(dev, "no free slot for id 0x%x\n",
|
||||
touch->id);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
slot = slots[i];
|
||||
}
|
||||
|
||||
input_mt_slot(ts->input, slot);
|
||||
input_mt_report_slot_state(ts->input,
|
||||
MT_TOOL_FINGER, true);
|
||||
|
||||
input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
|
||||
input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
|
||||
|
||||
dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
|
||||
i, slot, touch->x, touch->y);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(ts->input);
|
||||
input_sync(ts->input);
|
||||
}
|
||||
|
||||
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = dev_id;
|
||||
const struct pixcir_ts_platform_data *pdata = tsdata->chip;
|
||||
const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
|
||||
struct pixcir_report_data report;
|
||||
|
||||
while (tsdata->running) {
|
||||
pixcir_ts_poscheck(tsdata);
|
||||
/* parse packet */
|
||||
pixcir_ts_parse(tsdata, &report);
|
||||
|
||||
if (gpio_get_value(pdata->gpio_attb))
|
||||
/* report it */
|
||||
pixcir_ts_report(tsdata, &report);
|
||||
|
||||
if (gpio_get_value(pdata->gpio_attb)) {
|
||||
if (report.num_touches) {
|
||||
/*
|
||||
* Last report with no finger up?
|
||||
* Do it now then.
|
||||
*/
|
||||
input_mt_sync_frame(tsdata->input);
|
||||
input_sync(tsdata->input);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
@ -323,16 +410,69 @@ unlock:
|
||||
static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
|
||||
pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pixcir_of_match[];
|
||||
|
||||
static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
|
||||
{
|
||||
struct pixcir_ts_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(pixcir_of_match), dev);
|
||||
if (!match)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data;
|
||||
|
||||
pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
|
||||
/* gpio_attb validity is checked in probe */
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) {
|
||||
dev_err(dev, "Failed to get touchscreen-size-x property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
pdata->x_max -= 1;
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) {
|
||||
dev_err(dev, "Failed to get touchscreen-size-y property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
pdata->y_max -= 1;
|
||||
|
||||
dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__,
|
||||
pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct pixcir_ts_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pixcir_i2c_ts_data *tsdata;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
if (np && !pdata) {
|
||||
pdata = pixcir_parse_dt(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data not defined\n");
|
||||
return -EINVAL;
|
||||
@ -343,6 +483,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->chip.max_fingers) {
|
||||
dev_err(dev, "Invalid max_fingers in pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
|
||||
if (!tsdata)
|
||||
return -ENOMEM;
|
||||
@ -355,7 +500,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
|
||||
tsdata->client = client;
|
||||
tsdata->input = input;
|
||||
tsdata->chip = pdata;
|
||||
tsdata->pdata = pdata;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
@ -371,6 +516,20 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
|
||||
|
||||
tsdata->max_fingers = tsdata->pdata->chip.max_fingers;
|
||||
if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
|
||||
tsdata->max_fingers = PIXCIR_MAX_SLOTS;
|
||||
dev_info(dev, "Limiting maximum fingers to %d\n",
|
||||
tsdata->max_fingers);
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input, tsdata->max_fingers,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error) {
|
||||
dev_err(dev, "Error initializing Multi-Touch slots\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
input_set_drvdata(input, tsdata);
|
||||
|
||||
error = devm_gpio_request_one(dev, pdata->gpio_attb,
|
||||
@ -419,15 +578,36 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client)
|
||||
|
||||
static const struct i2c_device_id pixcir_i2c_ts_id[] = {
|
||||
{ "pixcir_ts", 0 },
|
||||
{ "pixcir_tangoc", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct pixcir_i2c_chip_data pixcir_ts_data = {
|
||||
.max_fingers = 2,
|
||||
/* no hw id support */
|
||||
};
|
||||
|
||||
static const struct pixcir_i2c_chip_data pixcir_tangoc_data = {
|
||||
.max_fingers = 5,
|
||||
.has_hw_ids = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id pixcir_of_match[] = {
|
||||
{ .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data },
|
||||
{ .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pixcir_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver pixcir_i2c_ts_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pixcir_ts",
|
||||
.pm = &pixcir_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(pixcir_of_match),
|
||||
},
|
||||
.probe = pixcir_i2c_ts_probe,
|
||||
.remove = pixcir_i2c_ts_remove,
|
||||
|
@ -264,7 +264,7 @@ static int s3c2410ts_probe(struct platform_device *pdev)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
clk_enable(ts.clock);
|
||||
clk_prepare_enable(ts.clock);
|
||||
dev_dbg(dev, "got and enabled clocks\n");
|
||||
|
||||
ts.irq_tc = ret = platform_get_irq(pdev, 0);
|
||||
@ -369,7 +369,7 @@ static int s3c2410ts_remove(struct platform_device *pdev)
|
||||
free_irq(ts.irq_tc, ts.input);
|
||||
del_timer_sync(&touch_timer);
|
||||
|
||||
clk_disable(ts.clock);
|
||||
clk_disable_unprepare(ts.clock);
|
||||
clk_put(ts.clock);
|
||||
|
||||
input_unregister_device(ts.input);
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/platform_data/zforce_ts.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
@ -117,6 +119,8 @@ struct zforce_ts {
|
||||
const struct zforce_ts_platdata *pdata;
|
||||
char phys[32];
|
||||
|
||||
struct regulator *reg_vdd;
|
||||
|
||||
bool suspending;
|
||||
bool suspended;
|
||||
bool boot_complete;
|
||||
@ -690,6 +694,11 @@ static void zforce_reset(void *data)
|
||||
struct zforce_ts *ts = data;
|
||||
|
||||
gpio_set_value(ts->pdata->gpio_rst, 0);
|
||||
|
||||
udelay(10);
|
||||
|
||||
if (!IS_ERR(ts->reg_vdd))
|
||||
regulator_disable(ts->reg_vdd);
|
||||
}
|
||||
|
||||
static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
|
||||
@ -765,10 +774,32 @@ static int zforce_probe(struct i2c_client *client,
|
||||
return ret;
|
||||
}
|
||||
|
||||
ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd");
|
||||
if (IS_ERR(ts->reg_vdd)) {
|
||||
ret = PTR_ERR(ts->reg_vdd);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
} else {
|
||||
ret = regulator_enable(ts->reg_vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* according to datasheet add 100us grace time after regular
|
||||
* regulator enable delay.
|
||||
*/
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
ret = devm_add_action(&client->dev, zforce_reset, ts);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register reset action, %d\n",
|
||||
ret);
|
||||
|
||||
/* hereafter the regulator will be disabled by the action */
|
||||
if (!IS_ERR(ts->reg_vdd))
|
||||
regulator_disable(ts->reg_vdd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -97,8 +97,6 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = {
|
||||
.irqflags = IRQF_TRIGGER_FALLING,
|
||||
.t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
|
||||
.t19_keymap = mxt_t19_keys,
|
||||
.config = NULL,
|
||||
.config_length = 0,
|
||||
};
|
||||
|
||||
static struct i2c_board_info atmel_224s_tp_device = {
|
||||
@ -109,8 +107,6 @@ static struct i2c_board_info atmel_224s_tp_device = {
|
||||
|
||||
static struct mxt_platform_data atmel_1664s_platform_data = {
|
||||
.irqflags = IRQF_TRIGGER_FALLING,
|
||||
.config = NULL,
|
||||
.config_length = 0,
|
||||
};
|
||||
|
||||
static struct i2c_board_info atmel_1664s_device = {
|
||||
|
@ -311,6 +311,11 @@ struct hid_item {
|
||||
*/
|
||||
#define HID_GROUP_RMI 0x0100
|
||||
|
||||
/*
|
||||
* Vendor specific HID device groups
|
||||
*/
|
||||
#define HID_GROUP_WACOM 0x0101
|
||||
|
||||
/*
|
||||
* This is the global environment of the parser. This information is
|
||||
* persistent for main-items. The global environment can be saved and
|
||||
|
@ -17,9 +17,6 @@
|
||||
|
||||
/* The platform data for the Atmel maXTouch touchscreen driver */
|
||||
struct mxt_platform_data {
|
||||
const u8 *config;
|
||||
size_t config_length;
|
||||
u32 config_crc;
|
||||
unsigned long irqflags;
|
||||
u8 t19_num_keys;
|
||||
const unsigned int *t19_keymap;
|
||||
|
@ -43,10 +43,22 @@ enum pixcir_int_mode {
|
||||
#define PIXCIR_INT_ENABLE (1UL << 3)
|
||||
#define PIXCIR_INT_POL_HIGH (1UL << 2)
|
||||
|
||||
/**
|
||||
* struct pixcir_irc_chip_data - chip related data
|
||||
* @max_fingers: Max number of fingers reported simultaneously by h/w
|
||||
* @has_hw_ids: Hardware supports finger tracking IDs
|
||||
*
|
||||
*/
|
||||
struct pixcir_i2c_chip_data {
|
||||
u8 max_fingers;
|
||||
bool has_hw_ids;
|
||||
};
|
||||
|
||||
struct pixcir_ts_platform_data {
|
||||
int x_max;
|
||||
int y_max;
|
||||
int gpio_attb; /* GPIO connected to ATTB line */
|
||||
struct pixcir_i2c_chip_data chip;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -76,5 +76,6 @@
|
||||
#define SERIO_HAMPSHIRE 0x3b
|
||||
#define SERIO_PS2MULT 0x3c
|
||||
#define SERIO_TSC40 0x3d
|
||||
#define SERIO_WACOM_IV 0x3e
|
||||
|
||||
#endif /* _UAPI_SERIO_H */
|
||||
|
@ -84,6 +84,15 @@ struct uinput_ff_erase {
|
||||
*/
|
||||
#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len)
|
||||
|
||||
/**
|
||||
* UI_GET_VERSION - Return version of uinput protocol
|
||||
*
|
||||
* This writes uinput protocol version implemented by the kernel into
|
||||
* the integer pointed to by the ioctl argument. The protocol version
|
||||
* is hard-coded in the kernel and is independent of the uinput device.
|
||||
*/
|
||||
#define UI_GET_VERSION _IOR(UINPUT_IOCTL_BASE, 301, unsigned int)
|
||||
|
||||
/*
|
||||
* To write a force-feedback-capable driver, the upload_effect
|
||||
* and erase_effect callbacks in input_dev must be implemented.
|
||||
|
Loading…
Reference in New Issue
Block a user