Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
- a new driver to ChipOne icn8505 based touchscreens
- on certain systems with Elan touch controllers they will be switched
away form PS/2 emulation and over to native SMbus mode
- assorted driver fixups and improvements
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (24 commits)
Input: elan_i2c - add ELAN0612 (Lenovo v330 14IKB) ACPI ID
Input: goodix - add new ACPI id for GPD Win 2 touch screen
Input: xpad - add GPD Win 2 Controller USB IDs
Input: ti_am335x_tsc - prevent system suspend when TSC is in use
Input: ti_am335x_tsc - ack pending IRQs at probe and before suspend
Input: cros_ec_keyb - mark cros_ec_keyb driver as wake enabled device.
Input: mk712 - update documentation web link
Input: atmel_mxt_ts - fix reset-gpio for level based irqs
Input: atmel_mxt_ts - require device properties present when probing
Input: psmouse-smbus - allow to control psmouse_deactivate
Input: elantech - detect new ICs and setup Host Notify for them
Input: elantech - add support for SMBus devices
Input: elantech - query the resolution in query_info
Input: elantech - split device info into a separate structure
Input: elan_i2c - add trackstick report
Input: usbtouchscreen - add sysfs attribute for 3M MTouch firmware rev
Input: ati_remote2 - fix typo 'can by' to 'can be'
Input: replace hard coded string with __func__ in pr_err()
Input: add support for ChipOne icn8505 based touchscreens
Input: gamecon - avoid using __set_bit() for capabilities
...
diff --git a/Documentation/devicetree/bindings/input/elan_i2c.txt b/Documentation/devicetree/bindings/input/elan_i2c.txt
index ee3242c..d80a835 100644
--- a/Documentation/devicetree/bindings/input/elan_i2c.txt
+++ b/Documentation/devicetree/bindings/input/elan_i2c.txt
@@ -14,6 +14,7 @@
- pinctrl-0: a phandle pointing to the pin settings for the device (see
pinctrl binding [1]).
- vcc-supply: a phandle for the regulator supplying 3.3V power.
+- elan,trackpoint: touchpad can support a trackpoint (boolean)
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
diff --git a/MAINTAINERS b/MAINTAINERS
index a2254ac..df68898 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3431,6 +3431,12 @@
F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
F: drivers/input/touchscreen/chipone_icn8318.c
+CHIPONE ICN8505 I2C TOUCHSCREEN DRIVER
+M: Hans de Goede <hdegoede@redhat.com>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/input/touchscreen/chipone_icn8505.c
+
CHROME HARDWARE PLATFORM SUPPORT
M: Benson Leung <bleung@chromium.org>
M: Olof Johansson <olof@lixom.net>
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 9785546..6365c19 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -1943,8 +1943,7 @@ void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int
break;
default:
- pr_err("input_set_capability: unknown type %u (code %u)\n",
- type, code);
+ pr_err("%s: unknown type %u (code %u)\n", __func__, type, code);
dump_stack();
return;
}
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
index 005d852..f051993 100644
--- a/drivers/input/joystick/as5011.c
+++ b/drivers/input/joystick/as5011.c
@@ -269,9 +269,7 @@ static int as5011_probe(struct i2c_client *client,
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(BTN_JOYSTICK, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_JOYSTICK);
input_set_abs_params(input_dev, ABS_X,
AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index 2ffb2e8..4e10ffd 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -862,7 +862,7 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
case GC_N64:
for (i = 0; i < 10; i++)
- __set_bit(gc_n64_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, gc_n64_btn[i]);
for (i = 0; i < 2; i++) {
input_set_abs_params(input_dev, ABS_X + i, -127, 126, 0, 2);
@@ -879,26 +879,27 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
break;
case GC_SNESMOUSE:
- __set_bit(BTN_LEFT, input_dev->keybit);
- __set_bit(BTN_RIGHT, input_dev->keybit);
- __set_bit(REL_X, input_dev->relbit);
- __set_bit(REL_Y, input_dev->relbit);
+ input_set_capability(input_dev, EV_KEY, BTN_LEFT);
+ input_set_capability(input_dev, EV_KEY, BTN_RIGHT);
+ input_set_capability(input_dev, EV_REL, REL_X);
+ input_set_capability(input_dev, EV_REL, REL_Y);
break;
case GC_SNES:
for (i = 4; i < 8; i++)
- __set_bit(gc_snes_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
/* fall through */
case GC_NES:
for (i = 0; i < 4; i++)
- __set_bit(gc_snes_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, gc_snes_btn[i]);
break;
case GC_MULTI2:
- __set_bit(BTN_THUMB, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_THUMB);
/* fall through */
case GC_MULTI:
- __set_bit(BTN_TRIGGER, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TRIGGER);
+ /* fall through */
break;
case GC_PSX:
@@ -906,15 +907,17 @@ static int gc_setup_pad(struct gc *gc, int idx, int pad_type)
input_set_abs_params(input_dev,
gc_psx_abs[i], 4, 252, 0, 2);
for (i = 0; i < 12; i++)
- __set_bit(gc_psx_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
+ break;
break;
case GC_DDR:
for (i = 0; i < 4; i++)
- __set_bit(gc_psx_ddr_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY,
+ gc_psx_ddr_btn[i]);
for (i = 0; i < 12; i++)
- __set_bit(gc_psx_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, gc_psx_btn[i]);
break;
}
diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index 06e9650..48e36ac 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -86,8 +86,10 @@
#define XPAD_PKT_LEN 64
-/* xbox d-pads should map to buttons, as is required for DDR pads
- but we map them to axes when possible to simplify things */
+/*
+ * xbox d-pads should map to buttons, as is required for DDR pads
+ * but we map them to axes when possible to simplify things
+ */
#define MAP_DPAD_TO_BUTTONS (1 << 0)
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
#define MAP_STICKS_TO_NULL (1 << 2)
@@ -123,6 +125,7 @@ static const struct xpad_device {
u8 mapping;
u8 xtype;
} xpad_device[] = {
+ { 0x0079, 0x18d4, "GPD Win 2 Controller", 0, XTYPE_XBOX360 },
{ 0x044f, 0x0f00, "Thrustmaster Wheel", 0, XTYPE_XBOX },
{ 0x044f, 0x0f03, "Thrustmaster Wheel", 0, XTYPE_XBOX },
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
@@ -387,15 +390,15 @@ static const signed short xpad_abs_triggers[] = {
* match against vendor id as well. Wired Xbox 360 devices have protocol 1,
* wireless controllers have protocol 129.
*/
-#define XPAD_XBOX360_VENDOR_PROTOCOL(vend,pr) \
+#define XPAD_XBOX360_VENDOR_PROTOCOL(vend, pr) \
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO, \
.idVendor = (vend), \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC, \
.bInterfaceSubClass = 93, \
.bInterfaceProtocol = (pr)
#define XPAD_XBOX360_VENDOR(vend) \
- { XPAD_XBOX360_VENDOR_PROTOCOL(vend,1) }, \
- { XPAD_XBOX360_VENDOR_PROTOCOL(vend,129) }
+ { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 1) }, \
+ { XPAD_XBOX360_VENDOR_PROTOCOL((vend), 129) }
/* The Xbox One controller uses subclass 71 and protocol 208. */
#define XPAD_XBOXONE_VENDOR_PROTOCOL(vend, pr) \
@@ -405,10 +408,11 @@ static const signed short xpad_abs_triggers[] = {
.bInterfaceSubClass = 71, \
.bInterfaceProtocol = (pr)
#define XPAD_XBOXONE_VENDOR(vend) \
- { XPAD_XBOXONE_VENDOR_PROTOCOL(vend, 208) }
+ { XPAD_XBOXONE_VENDOR_PROTOCOL((vend), 208) }
static const struct usb_device_id xpad_table[] = {
{ USB_INTERFACE_INFO('X', 'B', 0) }, /* X-Box USB-IF not approved class */
+ XPAD_XBOX360_VENDOR(0x0079), /* GPD Win 2 Controller */
XPAD_XBOX360_VENDOR(0x044f), /* Thrustmaster X-Box 360 controllers */
XPAD_XBOX360_VENDOR(0x045e), /* Microsoft X-Box 360 controllers */
XPAD_XBOXONE_VENDOR(0x045e), /* Microsoft X-Box One controllers */
@@ -1573,7 +1577,6 @@ static void xpad_close(struct input_dev *dev)
static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
{
struct usb_xpad *xpad = input_get_drvdata(input_dev);
- set_bit(abs, input_dev->absbit);
switch (abs) {
case ABS_X:
@@ -1593,6 +1596,9 @@ static void xpad_set_up_abs(struct input_dev *input_dev, signed short abs)
case ABS_HAT0Y: /* the d-pad (only if dpad is mapped to axes */
input_set_abs_params(input_dev, abs, -1, 1, 0, 0);
break;
+ default:
+ input_set_abs_params(input_dev, abs, 0, 0, 0, 0);
+ break;
}
}
@@ -1633,10 +1639,7 @@ static int xpad_init_input(struct usb_xpad *xpad)
input_dev->close = xpad_close;
}
- __set_bit(EV_KEY, input_dev->evbit);
-
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
- __set_bit(EV_ABS, input_dev->evbit);
/* set up axes */
for (i = 0; xpad_abs[i] >= 0; i++)
xpad_set_up_abs(input_dev, xpad_abs[i]);
@@ -1644,21 +1647,22 @@ static int xpad_init_input(struct usb_xpad *xpad)
/* set up standard buttons */
for (i = 0; xpad_common_btn[i] >= 0; i++)
- __set_bit(xpad_common_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, xpad_common_btn[i]);
/* set up model-specific ones */
if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W ||
xpad->xtype == XTYPE_XBOXONE) {
for (i = 0; xpad360_btn[i] >= 0; i++)
- __set_bit(xpad360_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, xpad360_btn[i]);
} else {
for (i = 0; xpad_btn[i] >= 0; i++)
- __set_bit(xpad_btn[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, xpad_btn[i]);
}
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
for (i = 0; xpad_btn_pad[i] >= 0; i++)
- __set_bit(xpad_btn_pad[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY,
+ xpad_btn_pad[i]);
}
/*
@@ -1675,7 +1679,8 @@ static int xpad_init_input(struct usb_xpad *xpad)
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
for (i = 0; xpad_btn_triggers[i] >= 0; i++)
- __set_bit(xpad_btn_triggers[i], input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY,
+ xpad_btn_triggers[i]);
} else {
for (i = 0; xpad_abs_triggers[i] >= 0; i++)
xpad_set_up_abs(input_dev, xpad_abs_triggers[i]);
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 79eb295..489ddd3 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -244,24 +244,35 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
switch (ckdev->ec->event_data.event_type) {
case EC_MKBP_EVENT_KEY_MATRIX:
- /*
- * If EC is not the wake source, discard key state changes
- * during suspend.
- */
- if (queued_during_suspend)
- return NOTIFY_OK;
+ if (device_may_wakeup(ckdev->dev)) {
+ pm_wakeup_event(ckdev->dev, 0);
+ } else {
+ /*
+ * If keyboard is not wake enabled, discard key state
+ * changes during suspend. Switches will be re-checked
+ * in cros_ec_keyb_resume() to be sure nothing is lost.
+ */
+ if (queued_during_suspend)
+ return NOTIFY_OK;
+ }
if (ckdev->ec->event_size != ckdev->cols) {
dev_err(ckdev->dev,
"Discarded incomplete key matrix event.\n");
return NOTIFY_OK;
}
+
cros_ec_keyb_process(ckdev,
ckdev->ec->event_data.data.key_matrix,
ckdev->ec->event_size);
break;
case EC_MKBP_EVENT_SYSRQ:
+ if (device_may_wakeup(ckdev->dev))
+ pm_wakeup_event(ckdev->dev, 0);
+ else if (queued_during_suspend)
+ return NOTIFY_OK;
+
val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq);
dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val);
handle_sysrq(val);
@@ -269,12 +280,9 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
case EC_MKBP_EVENT_BUTTON:
case EC_MKBP_EVENT_SWITCH:
- /*
- * If EC is not the wake source, discard key state
- * changes during suspend. Switches will be re-checked in
- * cros_ec_keyb_resume() to be sure nothing is lost.
- */
- if (queued_during_suspend)
+ if (device_may_wakeup(ckdev->dev))
+ pm_wakeup_event(ckdev->dev, 0);
+ else if (queued_during_suspend)
return NOTIFY_OK;
if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
@@ -639,6 +647,7 @@ static int cros_ec_keyb_probe(struct platform_device *pdev)
return err;
}
+ device_init_wakeup(ckdev->dev, true);
return 0;
}
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
index ded5b84..d8fd58f 100644
--- a/drivers/input/misc/ati_remote2.c
+++ b/drivers/input/misc/ati_remote2.c
@@ -22,7 +22,7 @@ MODULE_LICENSE("GPL");
/*
* ATI Remote Wonder II Channel Configuration
*
- * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * The remote control can be assigned one of sixteen "channels" in order to facilitate
* the use of multiple remote controls within range of each other.
* A remote's "channel" may be altered by pressing and holding the "PC" button for
* approximately 3 seconds, after which the button will slowly flash the count of the
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 89ebb8f..f27f23f 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -133,6 +133,18 @@
If unsure, say N.
+config MOUSE_PS2_ELANTECH_SMBUS
+ bool "Elantech PS/2 SMbus companion" if EXPERT
+ default y
+ depends on MOUSE_PS2 && MOUSE_PS2_ELANTECH
+ depends on I2C=y || I2C=MOUSE_PS2
+ select MOUSE_PS2_SMBUS
+ help
+ Say Y here if you have a Elantech touchpad connected to
+ to an SMBus, but enumerated through PS/2.
+
+ If unsure, say Y.
+
config MOUSE_PS2_SENTELIC
bool "Sentelic Finger Sensing Pad PS/2 protocol extension"
depends on MOUSE_PS2
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 38f9501..cb55797 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -2049,14 +2049,11 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse)
return 0;
}
-static int alps_hw_init_v6(struct psmouse *psmouse)
+/* Must be in passthrough mode when calling this function */
+static int alps_trackstick_enter_extended_mode_v3_v6(struct psmouse *psmouse)
{
unsigned char param[2] = {0xC8, 0x14};
- /* Enter passthrough mode to let trackpoint enter 6byte raw mode */
- if (alps_passthrough_mode_v2(psmouse, true))
- return -1;
-
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
@@ -2064,9 +2061,25 @@ static int alps_hw_init_v6(struct psmouse *psmouse)
ps2_command(&psmouse->ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE))
return -1;
+ return 0;
+}
+
+static int alps_hw_init_v6(struct psmouse *psmouse)
+{
+ int ret;
+
+ /* Enter passthrough mode to let trackpoint enter 6byte raw mode */
+ if (alps_passthrough_mode_v2(psmouse, true))
+ return -1;
+
+ ret = alps_trackstick_enter_extended_mode_v3_v6(psmouse);
+
if (alps_passthrough_mode_v2(psmouse, false))
return -1;
+ if (ret)
+ return ret;
+
if (alps_absolute_mode_v6(psmouse)) {
psmouse_err(psmouse, "Failed to enable absolute mode\n");
return -1;
@@ -2140,10 +2153,18 @@ static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base)
static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
{
- struct ps2dev *ps2dev = &psmouse->ps2dev;
int ret = 0;
+ int reg_val;
unsigned char param[4];
+ /*
+ * We need to configure trackstick to report data for touchpad in
+ * extended format. And also we need to tell touchpad to expect data
+ * from trackstick in extended format. Without this configuration
+ * trackstick packets sent from touchpad are in basic format which is
+ * different from what we expect.
+ */
+
if (alps_passthrough_mode_v3(psmouse, reg_base, true))
return -EIO;
@@ -2161,39 +2182,36 @@ static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base)
ret = -ENODEV;
} else {
psmouse_dbg(psmouse, "trackstick E7 report: %3ph\n", param);
-
- /*
- * Not sure what this does, but it is absolutely
- * essential. Without it, the touchpad does not
- * work at all and the trackstick just emits normal
- * PS/2 packets.
- */
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) ||
- alps_command_mode_send_nibble(psmouse, 0x9) ||
- alps_command_mode_send_nibble(psmouse, 0x4)) {
- psmouse_err(psmouse,
- "Error sending magic E6 sequence\n");
+ if (alps_trackstick_enter_extended_mode_v3_v6(psmouse)) {
+ psmouse_err(psmouse, "Failed to enter into trackstick extended mode\n");
ret = -EIO;
- goto error;
}
+ }
+ if (alps_passthrough_mode_v3(psmouse, reg_base, false))
+ return -EIO;
+
+ if (ret)
+ return ret;
+
+ if (alps_enter_command_mode(psmouse))
+ return -EIO;
+
+ reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08);
+ if (reg_val == -1) {
+ ret = -EIO;
+ } else {
/*
- * This ensures the trackstick packets are in the format
- * supported by this driver. If bit 1 isn't set the packet
- * format is different.
+ * Tell touchpad that trackstick is now in extended mode.
+ * If bit 1 isn't set the packet format is different.
*/
- if (alps_enter_command_mode(psmouse) ||
- alps_command_mode_write_reg(psmouse,
- reg_base + 0x08, 0x82) ||
- alps_exit_command_mode(psmouse))
+ reg_val |= BIT(1);
+ if (__alps_command_mode_write_reg(psmouse, reg_val))
ret = -EIO;
}
-error:
- if (alps_passthrough_mode_v3(psmouse, reg_base, false))
- ret = -EIO;
+ if (alps_exit_command_mode(psmouse))
+ return -EIO;
return ret;
}
diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c
index 75e7575..8ff7511 100644
--- a/drivers/input/mouse/elan_i2c_core.c
+++ b/drivers/input/mouse/elan_i2c_core.c
@@ -36,6 +36,7 @@
#include <linux/jiffies.h>
#include <linux/completion.h>
#include <linux/of.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <asm/unaligned.h>
@@ -51,6 +52,7 @@
#define ETP_MAX_FINGERS 5
#define ETP_FINGER_DATA_LEN 5
#define ETP_REPORT_ID 0x5D
+#define ETP_TP_REPORT_ID 0x5E
#define ETP_REPORT_ID_OFFSET 2
#define ETP_TOUCH_INFO_OFFSET 3
#define ETP_FINGER_DATA_OFFSET 4
@@ -61,6 +63,7 @@
struct elan_tp_data {
struct i2c_client *client;
struct input_dev *input;
+ struct input_dev *tp_input; /* trackpoint input node */
struct regulator *vcc;
const struct elan_transport_ops *ops;
@@ -930,6 +933,33 @@ static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
input_sync(input);
}
+static void elan_report_trackpoint(struct elan_tp_data *data, u8 *report)
+{
+ struct input_dev *input = data->tp_input;
+ u8 *packet = &report[ETP_REPORT_ID_OFFSET + 1];
+ int x, y;
+
+ if (!data->tp_input) {
+ dev_warn_once(&data->client->dev,
+ "received a trackpoint report while no trackpoint device has been created. Please report upstream.\n");
+ return;
+ }
+
+ input_report_key(input, BTN_LEFT, packet[0] & 0x01);
+ input_report_key(input, BTN_RIGHT, packet[0] & 0x02);
+ input_report_key(input, BTN_MIDDLE, packet[0] & 0x04);
+
+ if ((packet[3] & 0x0F) == 0x06) {
+ x = packet[4] - (int)((packet[1] ^ 0x80) << 1);
+ y = (int)((packet[2] ^ 0x80) << 1) - packet[5];
+
+ input_report_rel(input, REL_X, x);
+ input_report_rel(input, REL_Y, y);
+ }
+
+ input_sync(input);
+}
+
static irqreturn_t elan_isr(int irq, void *dev_id)
{
struct elan_tp_data *data = dev_id;
@@ -951,11 +981,17 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
if (error)
goto out;
- if (report[ETP_REPORT_ID_OFFSET] != ETP_REPORT_ID)
+ switch (report[ETP_REPORT_ID_OFFSET]) {
+ case ETP_REPORT_ID:
+ elan_report_absolute(data, report);
+ break;
+ case ETP_TP_REPORT_ID:
+ elan_report_trackpoint(data, report);
+ break;
+ default:
dev_err(dev, "invalid report id data (%x)\n",
report[ETP_REPORT_ID_OFFSET]);
- else
- elan_report_absolute(data, report);
+ }
out:
return IRQ_HANDLED;
@@ -966,6 +1002,36 @@ static irqreturn_t elan_isr(int irq, void *dev_id)
* Elan initialization functions
******************************************************************
*/
+
+static int elan_setup_trackpoint_input_device(struct elan_tp_data *data)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input;
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = "Elan TrackPoint";
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = ELAN_VENDOR_ID;
+ input->id.product = data->product_id;
+ input_set_drvdata(input, data);
+
+ input_set_capability(input, EV_REL, REL_X);
+ input_set_capability(input, EV_REL, REL_Y);
+ input_set_capability(input, EV_KEY, BTN_LEFT);
+ input_set_capability(input, EV_KEY, BTN_RIGHT);
+ input_set_capability(input, EV_KEY, BTN_MIDDLE);
+
+ __set_bit(INPUT_PROP_POINTER, input->propbit);
+ __set_bit(INPUT_PROP_POINTING_STICK, input->propbit);
+
+ data->tp_input = input;
+
+ return 0;
+}
+
static int elan_setup_input_device(struct elan_tp_data *data)
{
struct device *dev = &data->client->dev;
@@ -1140,6 +1206,12 @@ static int elan_probe(struct i2c_client *client,
if (error)
return error;
+ if (device_property_read_bool(&client->dev, "elan,trackpoint")) {
+ error = elan_setup_trackpoint_input_device(data);
+ if (error)
+ return error;
+ }
+
/*
* Platform code (ACPI, DTS) should normally set up interrupt
* for us, but in case it did not let's fall back to using falling
@@ -1177,6 +1249,16 @@ static int elan_probe(struct i2c_client *client,
return error;
}
+ if (data->tp_input) {
+ error = input_register_device(data->tp_input);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to register TrackPoint input device: %d\n",
+ error);
+ return error;
+ }
+ }
+
/*
* Systems using device tree should set up wakeup via DTS,
* the rest will configure device as wakeup source by default.
@@ -1262,6 +1344,7 @@ static const struct acpi_device_id elan_acpi_id[] = {
{ "ELAN060B", 0 },
{ "ELAN060C", 0 },
{ "ELAN0611", 0 },
+ { "ELAN0612", 0 },
{ "ELAN1000", 0 },
{ }
};
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index db47a5e..fb4d902 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -14,17 +14,20 @@
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
+#include <linux/platform_device.h>
#include <linux/serio.h>
#include <linux/libps2.h>
#include <asm/unaligned.h>
#include "psmouse.h"
#include "elantech.h"
+#include "elan_i2c.h"
#define elantech_debug(fmt, ...) \
do { \
- if (etd->debug) \
+ if (etd->info.debug) \
psmouse_printk(KERN_DEBUG, psmouse, \
fmt, ##__VA_ARGS__); \
} while (0)
@@ -105,7 +108,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
if (reg > 0x11 && reg < 0x20)
return -1;
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_READ) ||
ps2_sliced_command(&psmouse->ps2dev, reg) ||
@@ -137,7 +140,7 @@ static int elantech_read_reg(struct psmouse *psmouse, unsigned char reg,
if (rc)
psmouse_err(psmouse, "failed to read register 0x%02x.\n", reg);
- else if (etd->hw_version != 4)
+ else if (etd->info.hw_version != 4)
*val = param[0];
else
*val = param[1];
@@ -160,7 +163,7 @@ static int elantech_write_reg(struct psmouse *psmouse, unsigned char reg,
if (reg > 0x11 && reg < 0x20)
return -1;
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
if (ps2_sliced_command(&psmouse->ps2dev, ETP_REGISTER_WRITE) ||
ps2_sliced_command(&psmouse->ps2dev, reg) ||
@@ -237,7 +240,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
int fingers;
- if (etd->fw_version < 0x020000) {
+ if (etd->info.fw_version < 0x020000) {
/*
* byte 0: D U p1 p2 1 p3 R L
* byte 1: f 0 th tw x9 x8 y9 y8
@@ -252,7 +255,7 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
fingers = (packet[0] & 0xc0) >> 6;
}
- if (etd->jumpy_cursor) {
+ if (etd->info.jumpy_cursor) {
if (fingers != 1) {
etd->single_finger_reports = 0;
} else if (etd->single_finger_reports < 2) {
@@ -282,8 +285,8 @@ static void elantech_report_absolute_v1(struct psmouse *psmouse)
psmouse_report_standard_buttons(dev, packet[0]);
- if (etd->fw_version < 0x020000 &&
- (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+ if (etd->info.fw_version < 0x020000 &&
+ (etd->info.capabilities[0] & ETP_CAP_HAS_ROCKER)) {
/* rocker up */
input_report_key(dev, BTN_FORWARD, packet[0] & 0x40);
/* rocker down */
@@ -391,7 +394,7 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
input_report_key(dev, BTN_TOOL_QUADTAP, fingers == 4);
psmouse_report_standard_buttons(dev, packet[0]);
- if (etd->reports_pressure) {
+ if (etd->info.reports_pressure) {
input_report_abs(dev, ABS_PRESSURE, pres);
input_report_abs(dev, ABS_TOOL_WIDTH, width);
}
@@ -444,7 +447,7 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
default:
/* Dump unexpected packet sequences if debug=1 (default) */
- if (etd->debug == 1)
+ if (etd->info.debug == 1)
elantech_packet_dump(psmouse);
break;
@@ -523,7 +526,7 @@ static void elantech_report_absolute_v3(struct psmouse *psmouse,
input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3);
/* For clickpads map both buttons to BTN_LEFT */
- if (etd->fw_version & 0x001000)
+ if (etd->info.fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
else
psmouse_report_standard_buttons(dev, packet[0]);
@@ -541,7 +544,7 @@ static void elantech_input_sync_v4(struct psmouse *psmouse)
unsigned char *packet = psmouse->packet;
/* For clickpads map both buttons to BTN_LEFT */
- if (etd->fw_version & 0x001000)
+ if (etd->info.fw_version & 0x001000)
input_report_key(dev, BTN_LEFT, packet[0] & 0x03);
else
psmouse_report_standard_buttons(dev, packet[0]);
@@ -669,7 +672,7 @@ static int elantech_packet_check_v1(struct psmouse *psmouse)
unsigned char p1, p2, p3;
/* Parity bits are placed differently */
- if (etd->fw_version < 0x020000) {
+ if (etd->info.fw_version < 0x020000) {
/* byte 0: D U p1 p2 1 p3 R L */
p1 = (packet[0] & 0x20) >> 5;
p2 = (packet[0] & 0x10) >> 4;
@@ -714,7 +717,7 @@ static int elantech_packet_check_v2(struct psmouse *psmouse)
* With all three cases, if the constant bits are not exactly what I
* expected, I consider them invalid.
*/
- if (etd->reports_pressure)
+ if (etd->info.reports_pressure)
return (packet[0] & 0x0c) == 0x04 &&
(packet[3] & 0x0f) == 0x02;
@@ -751,7 +754,7 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
* If the hardware flag 'crc_enabled' is set the packets have
* different signatures.
*/
- if (etd->crc_enabled) {
+ if (etd->info.crc_enabled) {
if ((packet[3] & 0x09) == 0x08)
return PACKET_V3_HEAD;
@@ -782,7 +785,7 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
return PACKET_TRACKPOINT;
/* This represents the version of IC body. */
- ic_version = (etd->fw_version & 0x0f0000) >> 16;
+ ic_version = (etd->info.fw_version & 0x0f0000) >> 16;
/*
* Sanity check based on the constant bits of a packet.
@@ -791,9 +794,9 @@ static int elantech_packet_check_v4(struct psmouse *psmouse)
* the IC body, but are the same for every packet,
* regardless of the type.
*/
- if (etd->crc_enabled)
+ if (etd->info.crc_enabled)
sanity_check = ((packet[3] & 0x08) == 0x00);
- else if (ic_version == 7 && etd->samples[1] == 0x2A)
+ else if (ic_version == 7 && etd->info.samples[1] == 0x2A)
sanity_check = ((packet[3] & 0x1c) == 0x10);
else
sanity_check = ((packet[0] & 0x0c) == 0x04 &&
@@ -827,12 +830,12 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
if (psmouse->pktcnt < psmouse->pktsize)
return PSMOUSE_GOOD_DATA;
- if (etd->debug > 1)
+ if (etd->info.debug > 1)
elantech_packet_dump(psmouse);
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
- if (etd->paritycheck && !elantech_packet_check_v1(psmouse))
+ if (etd->info.paritycheck && !elantech_packet_check_v1(psmouse))
return PSMOUSE_BAD_DATA;
elantech_report_absolute_v1(psmouse);
@@ -843,7 +846,7 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
if (elantech_debounce_check_v2(psmouse))
return PSMOUSE_FULL_PACKET;
- if (etd->paritycheck && !elantech_packet_check_v2(psmouse))
+ if (etd->info.paritycheck && !elantech_packet_check_v2(psmouse))
return PSMOUSE_BAD_DATA;
elantech_report_absolute_v2(psmouse);
@@ -916,7 +919,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
int tries = ETP_READ_BACK_TRIES;
int rc = 0;
- switch (etd->hw_version) {
+ switch (etd->info.hw_version) {
case 1:
etd->reg_10 = 0x16;
etd->reg_11 = 0x8f;
@@ -939,7 +942,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
break;
case 3:
- if (etd->set_hw_resolution)
+ if (etd->info.set_hw_resolution)
etd->reg_10 = 0x0b;
else
etd->reg_10 = 0x01;
@@ -976,7 +979,7 @@ static int elantech_set_absolute_mode(struct psmouse *psmouse)
if (rc) {
psmouse_err(psmouse,
"failed to read back register 0x10.\n");
- } else if (etd->hw_version == 1 &&
+ } else if (etd->info.hw_version == 1 &&
!(val & ETP_R10_ABSOLUTE_MODE)) {
psmouse_err(psmouse,
"touchpad refuses to switch to absolute mode.\n");
@@ -997,10 +1000,11 @@ static int elantech_set_range(struct psmouse *psmouse,
unsigned int *width)
{
struct elantech_data *etd = psmouse->private;
+ struct elantech_device_info *info = &etd->info;
unsigned char param[3];
unsigned char traces;
- switch (etd->hw_version) {
+ switch (info->hw_version) {
case 1:
*x_min = ETP_XMIN_V1;
*y_min = ETP_YMIN_V1;
@@ -1009,9 +1013,9 @@ static int elantech_set_range(struct psmouse *psmouse,
break;
case 2:
- if (etd->fw_version == 0x020800 ||
- etd->fw_version == 0x020b00 ||
- etd->fw_version == 0x020030) {
+ if (info->fw_version == 0x020800 ||
+ info->fw_version == 0x020b00 ||
+ info->fw_version == 0x020030) {
*x_min = ETP_XMIN_V2;
*y_min = ETP_YMIN_V2;
*x_max = ETP_XMAX_V2;
@@ -1020,35 +1024,35 @@ static int elantech_set_range(struct psmouse *psmouse,
int i;
int fixed_dpi;
- i = (etd->fw_version > 0x020800 &&
- etd->fw_version < 0x020900) ? 1 : 2;
+ i = (info->fw_version > 0x020800 &&
+ info->fw_version < 0x020900) ? 1 : 2;
- if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
return -1;
fixed_dpi = param[1] & 0x10;
- if (((etd->fw_version >> 16) == 0x14) && fixed_dpi) {
- if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
+ if (((info->fw_version >> 16) == 0x14) && fixed_dpi) {
+ if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param))
return -1;
- *x_max = (etd->capabilities[1] - i) * param[1] / 2;
- *y_max = (etd->capabilities[2] - i) * param[2] / 2;
- } else if (etd->fw_version == 0x040216) {
+ *x_max = (info->capabilities[1] - i) * param[1] / 2;
+ *y_max = (info->capabilities[2] - i) * param[2] / 2;
+ } else if (info->fw_version == 0x040216) {
*x_max = 819;
*y_max = 405;
- } else if (etd->fw_version == 0x040219 || etd->fw_version == 0x040215) {
+ } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) {
*x_max = 900;
*y_max = 500;
} else {
- *x_max = (etd->capabilities[1] - i) * 64;
- *y_max = (etd->capabilities[2] - i) * 64;
+ *x_max = (info->capabilities[1] - i) * 64;
+ *y_max = (info->capabilities[2] - i) * 64;
}
}
break;
case 3:
- if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
return -1;
*x_max = (0x0f & param[0]) << 8 | param[1];
@@ -1056,12 +1060,12 @@ static int elantech_set_range(struct psmouse *psmouse,
break;
case 4:
- if (etd->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
+ if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param))
return -1;
*x_max = (0x0f & param[0]) << 8 | param[1];
*y_max = (0xf0 & param[0]) << 4 | param[2];
- traces = etd->capabilities[1];
+ traces = info->capabilities[1];
if ((traces < 2) || (traces > *x_max))
return -1;
@@ -1083,7 +1087,8 @@ static unsigned int elantech_convert_res(unsigned int val)
static int elantech_get_resolution_v4(struct psmouse *psmouse,
unsigned int *x_res,
- unsigned int *y_res)
+ unsigned int *y_res,
+ unsigned int *bus)
{
unsigned char param[3];
@@ -1092,6 +1097,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
*x_res = elantech_convert_res(param[1] & 0x0f);
*y_res = elantech_convert_res((param[1] & 0xf0) >> 4);
+ *bus = param[2];
return 0;
}
@@ -1140,7 +1146,7 @@ static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private;
- if (etd->fw_version & 0x001000) {
+ if (etd->info.fw_version & 0x001000) {
__set_bit(INPUT_PROP_BUTTONPAD, dev->propbit);
__clear_bit(BTN_RIGHT, dev->keybit);
}
@@ -1176,8 +1182,8 @@ static int elantech_set_input_params(struct psmouse *psmouse)
{
struct input_dev *dev = psmouse->dev;
struct elantech_data *etd = psmouse->private;
+ struct elantech_device_info *info = &etd->info;
unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0;
- unsigned int x_res = 31, y_res = 31;
if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width))
return -1;
@@ -1197,11 +1203,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
__set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
- switch (etd->hw_version) {
+ switch (info->hw_version) {
case 1:
/* Rocker button */
- if (etd->fw_version < 0x020000 &&
- (etd->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
+ if (info->fw_version < 0x020000 &&
+ (info->capabilities[0] & ETP_CAP_HAS_ROCKER)) {
__set_bit(BTN_FORWARD, dev->keybit);
__set_bit(BTN_BACK, dev->keybit);
}
@@ -1214,11 +1220,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
/* fall through */
case 3:
- if (etd->hw_version == 3)
+ if (info->hw_version == 3)
elantech_set_buttonpad_prop(psmouse);
input_set_abs_params(dev, ABS_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_Y, y_min, y_max, 0, 0);
- if (etd->reports_pressure) {
+ if (info->reports_pressure) {
input_set_abs_params(dev, ABS_PRESSURE, ETP_PMIN_V2,
ETP_PMAX_V2, 0, 0);
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
@@ -1230,13 +1236,6 @@ static int elantech_set_input_params(struct psmouse *psmouse)
break;
case 4:
- if (elantech_get_resolution_v4(psmouse, &x_res, &y_res)) {
- /*
- * if query failed, print a warning and leave the values
- * zero to resemble synaptics.c behavior.
- */
- psmouse_warn(psmouse, "couldn't query resolution data.\n");
- }
elantech_set_buttonpad_prop(psmouse);
__set_bit(BTN_TOOL_QUADTAP, dev->keybit);
/* For X to recognize me as touchpad. */
@@ -1265,11 +1264,11 @@ static int elantech_set_input_params(struct psmouse *psmouse)
break;
}
- input_abs_set_res(dev, ABS_X, x_res);
- input_abs_set_res(dev, ABS_Y, y_res);
- if (etd->hw_version > 1) {
- input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
- input_abs_set_res(dev, ABS_MT_POSITION_Y, y_res);
+ input_abs_set_res(dev, ABS_X, info->x_res);
+ input_abs_set_res(dev, ABS_Y, info->y_res);
+ if (info->hw_version > 1) {
+ input_abs_set_res(dev, ABS_MT_POSITION_X, info->x_res);
+ input_abs_set_res(dev, ABS_MT_POSITION_Y, info->y_res);
}
etd->y_max = y_max;
@@ -1317,7 +1316,7 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
return err;
/* Do we need to preserve some bits for version 2 hardware too? */
- if (etd->hw_version == 1) {
+ if (etd->info.hw_version == 1) {
if (attr->reg == 0x10)
/* Force absolute mode always on */
value |= ETP_R10_ABSOLUTE_MODE;
@@ -1337,11 +1336,22 @@ static ssize_t elantech_set_int_attr(struct psmouse *psmouse,
.field_offset = offsetof(struct elantech_data, _name), \
.reg = _register, \
}; \
- PSMOUSE_DEFINE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ PSMOUSE_DEFINE_ATTR(_name, 0644, \
&elantech_attr_##_name, \
elantech_show_int_attr, \
elantech_set_int_attr)
+#define ELANTECH_INFO_ATTR(_name) \
+ static struct elantech_attr_data elantech_attr_##_name = { \
+ .field_offset = offsetof(struct elantech_data, info) + \
+ offsetof(struct elantech_device_info, _name), \
+ .reg = 0, \
+ }; \
+ PSMOUSE_DEFINE_ATTR(_name, 0644, \
+ &elantech_attr_##_name, \
+ elantech_show_int_attr, \
+ elantech_set_int_attr)
+
ELANTECH_INT_ATTR(reg_07, 0x07);
ELANTECH_INT_ATTR(reg_10, 0x10);
ELANTECH_INT_ATTR(reg_11, 0x11);
@@ -1352,9 +1362,9 @@ ELANTECH_INT_ATTR(reg_23, 0x23);
ELANTECH_INT_ATTR(reg_24, 0x24);
ELANTECH_INT_ATTR(reg_25, 0x25);
ELANTECH_INT_ATTR(reg_26, 0x26);
-ELANTECH_INT_ATTR(debug, 0);
-ELANTECH_INT_ATTR(paritycheck, 0);
-ELANTECH_INT_ATTR(crc_enabled, 0);
+ELANTECH_INFO_ATTR(debug);
+ELANTECH_INFO_ATTR(paritycheck);
+ELANTECH_INFO_ATTR(crc_enabled);
static struct attribute *elantech_attrs[] = {
&psmouse_attr_reg_07.dattr.attr,
@@ -1469,6 +1479,12 @@ static void elantech_disconnect(struct psmouse *psmouse)
{
struct elantech_data *etd = psmouse->private;
+ /*
+ * We might have left a breadcrumb when trying to
+ * set up SMbus companion.
+ */
+ psmouse_smbus_cleanup(psmouse);
+
if (etd->tp_dev)
input_unregister_device(etd->tp_dev);
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
@@ -1588,25 +1604,25 @@ static const struct dmi_system_id no_hw_res_dmi_table[] = {
/*
* determine hardware version and set some properties according to it.
*/
-static int elantech_set_properties(struct elantech_data *etd)
+static int elantech_set_properties(struct elantech_device_info *info)
{
/* This represents the version of IC body. */
- int ver = (etd->fw_version & 0x0f0000) >> 16;
+ int ver = (info->fw_version & 0x0f0000) >> 16;
/* Early version of Elan touchpads doesn't obey the rule. */
- if (etd->fw_version < 0x020030 || etd->fw_version == 0x020600)
- etd->hw_version = 1;
+ if (info->fw_version < 0x020030 || info->fw_version == 0x020600)
+ info->hw_version = 1;
else {
switch (ver) {
case 2:
case 4:
- etd->hw_version = 2;
+ info->hw_version = 2;
break;
case 5:
- etd->hw_version = 3;
+ info->hw_version = 3;
break;
case 6 ... 15:
- etd->hw_version = 4;
+ info->hw_version = 4;
break;
default:
return -1;
@@ -1614,100 +1630,88 @@ static int elantech_set_properties(struct elantech_data *etd)
}
/* decide which send_cmd we're gonna use early */
- etd->send_cmd = etd->hw_version >= 3 ? elantech_send_cmd :
- synaptics_send_cmd;
+ info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd :
+ synaptics_send_cmd;
/* Turn on packet checking by default */
- etd->paritycheck = 1;
+ info->paritycheck = 1;
/*
* This firmware suffers from misreporting coordinates when
* a touch action starts causing the mouse cursor or scrolled page
* to jump. Enable a workaround.
*/
- etd->jumpy_cursor =
- (etd->fw_version == 0x020022 || etd->fw_version == 0x020600);
+ info->jumpy_cursor =
+ (info->fw_version == 0x020022 || info->fw_version == 0x020600);
- if (etd->hw_version > 1) {
+ if (info->hw_version > 1) {
/* For now show extra debug information */
- etd->debug = 1;
+ info->debug = 1;
- if (etd->fw_version >= 0x020800)
- etd->reports_pressure = true;
+ if (info->fw_version >= 0x020800)
+ info->reports_pressure = true;
}
/*
* The signatures of v3 and v4 packets change depending on the
* value of this hardware flag.
*/
- etd->crc_enabled = (etd->fw_version & 0x4000) == 0x4000 ||
- dmi_check_system(elantech_dmi_force_crc_enabled);
+ info->crc_enabled = (info->fw_version & 0x4000) == 0x4000 ||
+ dmi_check_system(elantech_dmi_force_crc_enabled);
/* Enable real hardware resolution on hw_version 3 ? */
- etd->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
+ info->set_hw_resolution = !dmi_check_system(no_hw_res_dmi_table);
return 0;
}
-/*
- * Initialize the touchpad and create sysfs entries
- */
-int elantech_init(struct psmouse *psmouse)
+static int elantech_query_info(struct psmouse *psmouse,
+ struct elantech_device_info *info)
{
- struct elantech_data *etd;
- int i;
- int error = -EINVAL;
unsigned char param[3];
- struct input_dev *tp_dev;
- psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
- if (!etd)
- return -ENOMEM;
-
- psmouse_reset(psmouse);
-
- etd->parity[0] = 1;
- for (i = 1; i < 256; i++)
- etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+ memset(info, 0, sizeof(*info));
/*
* Do the version query again so we can store the result
*/
if (synaptics_send_cmd(psmouse, ETP_FW_VERSION_QUERY, param)) {
psmouse_err(psmouse, "failed to query firmware version.\n");
- goto init_fail;
+ return -EINVAL;
}
- etd->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
+ info->fw_version = (param[0] << 16) | (param[1] << 8) | param[2];
- if (elantech_set_properties(etd)) {
+ if (elantech_set_properties(info)) {
psmouse_err(psmouse, "unknown hardware version, aborting...\n");
- goto init_fail;
+ return -EINVAL;
}
psmouse_info(psmouse,
"assuming hardware version %d (with firmware version 0x%02x%02x%02x)\n",
- etd->hw_version, param[0], param[1], param[2]);
+ info->hw_version, param[0], param[1], param[2]);
- if (etd->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
- etd->capabilities)) {
+ if (info->send_cmd(psmouse, ETP_CAPABILITIES_QUERY,
+ info->capabilities)) {
psmouse_err(psmouse, "failed to query capabilities.\n");
- goto init_fail;
+ return -EINVAL;
}
psmouse_info(psmouse,
"Synaptics capabilities query result 0x%02x, 0x%02x, 0x%02x.\n",
- etd->capabilities[0], etd->capabilities[1],
- etd->capabilities[2]);
+ info->capabilities[0], info->capabilities[1],
+ info->capabilities[2]);
- if (etd->hw_version != 1) {
- if (etd->send_cmd(psmouse, ETP_SAMPLE_QUERY, etd->samples)) {
+ if (info->hw_version != 1) {
+ if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, info->samples)) {
psmouse_err(psmouse, "failed to query sample data\n");
- goto init_fail;
+ return -EINVAL;
}
psmouse_info(psmouse,
"Elan sample query result %02x, %02x, %02x\n",
- etd->samples[0], etd->samples[1], etd->samples[2]);
+ info->samples[0],
+ info->samples[1],
+ info->samples[2]);
}
- if (etd->samples[1] == 0x74 && etd->hw_version == 0x03) {
+ if (info->samples[1] == 0x74 && info->hw_version == 0x03) {
/*
* This module has a bug which makes absolute mode
* unusable, so let's abort so we'll be using standard
@@ -1715,16 +1719,181 @@ int elantech_init(struct psmouse *psmouse)
*/
psmouse_info(psmouse,
"absolute mode broken, forcing standard PS/2 protocol\n");
+ return -ENODEV;
+ }
+
+ /* The MSB indicates the presence of the trackpoint */
+ info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80;
+
+ info->x_res = 31;
+ info->y_res = 31;
+ if (info->hw_version == 4) {
+ if (elantech_get_resolution_v4(psmouse,
+ &info->x_res,
+ &info->y_res,
+ &info->bus)) {
+ psmouse_warn(psmouse,
+ "failed to query resolution data.\n");
+ }
+ }
+
+ return 0;
+}
+
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+
+/*
+ * The newest Elantech device can use a secondary bus (over SMBus) which
+ * provides a better bandwidth and allow a better control of the touchpads.
+ * This is used to decide if we need to use this bus or not.
+ */
+enum {
+ ELANTECH_SMBUS_NOT_SET = -1,
+ ELANTECH_SMBUS_OFF,
+ ELANTECH_SMBUS_ON,
+};
+
+static int elantech_smbus = IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ?
+ ELANTECH_SMBUS_NOT_SET : ELANTECH_SMBUS_OFF;
+module_param_named(elantech_smbus, elantech_smbus, int, 0644);
+MODULE_PARM_DESC(elantech_smbus, "Use a secondary bus for the Elantech device.");
+
+static int elantech_create_smbus(struct psmouse *psmouse,
+ struct elantech_device_info *info,
+ bool leave_breadcrumbs)
+{
+ const struct property_entry i2c_properties[] = {
+ PROPERTY_ENTRY_BOOL("elan,trackpoint"),
+ { },
+ };
+ struct i2c_board_info smbus_board = {
+ I2C_BOARD_INFO("elan_i2c", 0x15),
+ .flags = I2C_CLIENT_HOST_NOTIFY,
+ };
+
+ if (info->has_trackpoint)
+ smbus_board.properties = i2c_properties;
+
+ return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false,
+ leave_breadcrumbs);
+}
+
+/**
+ * elantech_setup_smbus - called once the PS/2 devices are enumerated
+ * and decides to instantiate a SMBus InterTouch device.
+ */
+static int elantech_setup_smbus(struct psmouse *psmouse,
+ struct elantech_device_info *info,
+ bool leave_breadcrumbs)
+{
+ int error;
+
+ if (elantech_smbus == ELANTECH_SMBUS_OFF)
+ return -ENXIO;
+
+ if (elantech_smbus == ELANTECH_SMBUS_NOT_SET) {
+ /*
+ * New ICs are enabled by default.
+ * Old ICs are up to the user to decide.
+ */
+ if (!ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
+ return -ENXIO;
+ }
+
+ psmouse_info(psmouse, "Trying to set up SMBus access\n");
+
+ error = elantech_create_smbus(psmouse, info, leave_breadcrumbs);
+ if (error) {
+ if (error == -EAGAIN)
+ psmouse_info(psmouse, "SMbus companion is not ready yet\n");
+ else
+ psmouse_err(psmouse, "unable to create intertouch device\n");
+
+ return error;
+ }
+
+ return 0;
+}
+
+static bool elantech_use_host_notify(struct psmouse *psmouse,
+ struct elantech_device_info *info)
+{
+ if (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version))
+ return true;
+
+ switch (info->bus) {
+ case ETP_BUS_PS2_ONLY:
+ /* expected case */
+ break;
+ case ETP_BUS_SMB_ALERT_ONLY:
+ /* fall-through */
+ case ETP_BUS_PS2_SMB_ALERT:
+ psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n");
+ break;
+ case ETP_BUS_SMB_HST_NTFY_ONLY:
+ /* fall-through */
+ case ETP_BUS_PS2_SMB_HST_NTFY:
+ return true;
+ default:
+ psmouse_dbg(psmouse,
+ "Ignoring SMBus bus provider %d.\n",
+ info->bus);
+ }
+
+ return false;
+}
+
+int elantech_init_smbus(struct psmouse *psmouse)
+{
+ struct elantech_device_info info;
+ int error = -EINVAL;
+
+ psmouse_reset(psmouse);
+
+ error = elantech_query_info(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+ if (info.hw_version < 4) {
+ error = -ENXIO;
goto init_fail;
}
+ return elantech_create_smbus(psmouse, &info, false);
+ init_fail:
+ psmouse_reset(psmouse);
+ return error;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
+/*
+ * Initialize the touchpad and create sysfs entries
+ */
+static int elantech_setup_ps2(struct psmouse *psmouse,
+ struct elantech_device_info *info)
+{
+ struct elantech_data *etd;
+ int i;
+ int error = -EINVAL;
+ struct input_dev *tp_dev;
+
+ psmouse->private = etd = kzalloc(sizeof(*etd), GFP_KERNEL);
+ if (!etd)
+ return -ENOMEM;
+
+ etd->info = *info;
+
+ etd->parity[0] = 1;
+ for (i = 1; i < 256; i++)
+ etd->parity[i] = etd->parity[i & (i - 1)] ^ 1;
+
if (elantech_set_absolute_mode(psmouse)) {
psmouse_err(psmouse,
"failed to put touchpad into absolute mode.\n");
goto init_fail;
}
- if (etd->fw_version == 0x381f17) {
+ if (info->fw_version == 0x381f17) {
etd->original_set_rate = psmouse->set_rate;
psmouse->set_rate = elantech_set_rate_restore_reg_07;
}
@@ -1743,8 +1912,7 @@ int elantech_init(struct psmouse *psmouse)
goto init_fail;
}
- /* The MSB indicates the presence of the trackpoint */
- if ((etd->capabilities[0] & 0x80) == 0x80) {
+ if (info->has_trackpoint) {
tp_dev = input_allocate_device();
if (!tp_dev) {
@@ -1780,7 +1948,7 @@ int elantech_init(struct psmouse *psmouse)
psmouse->protocol_handler = elantech_process_byte;
psmouse->disconnect = elantech_disconnect;
psmouse->reconnect = elantech_reconnect;
- psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
+ psmouse->pktsize = info->hw_version > 1 ? 6 : 4;
return 0;
init_fail_tp_reg:
@@ -1789,7 +1957,70 @@ int elantech_init(struct psmouse *psmouse)
sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
&elantech_attr_group);
init_fail:
- psmouse_reset(psmouse);
kfree(etd);
return error;
}
+
+int elantech_init_ps2(struct psmouse *psmouse)
+{
+ struct elantech_device_info info;
+ int error = -EINVAL;
+
+ psmouse_reset(psmouse);
+
+ error = elantech_query_info(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+ error = elantech_setup_ps2(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+ return 0;
+ init_fail:
+ psmouse_reset(psmouse);
+ return error;
+}
+
+int elantech_init(struct psmouse *psmouse)
+{
+ struct elantech_device_info info;
+ int error = -EINVAL;
+
+ psmouse_reset(psmouse);
+
+ error = elantech_query_info(psmouse, &info);
+ if (error)
+ goto init_fail;
+
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+
+ if (elantech_use_host_notify(psmouse, &info)) {
+ if (!IS_ENABLED(CONFIG_MOUSE_ELAN_I2C_SMBUS) ||
+ !IS_ENABLED(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)) {
+ psmouse_warn(psmouse,
+ "The touchpad can support a better bus than the too old PS/2 protocol. "
+ "Make sure MOUSE_PS2_ELANTECH_SMBUS and MOUSE_ELAN_I2C_SMBUS are enabled to get a better touchpad experience.\n");
+ }
+ error = elantech_setup_smbus(psmouse, &info, true);
+ if (!error)
+ return PSMOUSE_ELANTECH_SMBUS;
+ }
+
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
+ error = elantech_setup_ps2(psmouse, &info);
+ if (error < 0) {
+ /*
+ * Not using any flavor of Elantech support, so clean up
+ * SMbus breadcrumbs, if any.
+ */
+ psmouse_smbus_cleanup(psmouse);
+ goto init_fail;
+ }
+
+ return PSMOUSE_ELANTECH;
+ init_fail:
+ psmouse_reset(psmouse);
+ return error;
+}
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index e1cbf40..1197270 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -107,6 +107,30 @@
#define ETP_WEIGHT_VALUE 5
/*
+ * Bus information on 3rd byte of query ETP_RESOLUTION_QUERY(0x04)
+ */
+#define ETP_BUS_PS2_ONLY 0
+#define ETP_BUS_SMB_ALERT_ONLY 1
+#define ETP_BUS_SMB_HST_NTFY_ONLY 2
+#define ETP_BUS_PS2_SMB_ALERT 3
+#define ETP_BUS_PS2_SMB_HST_NTFY 4
+
+/*
+ * New ICs are either using SMBus Host Notify or just plain PS2.
+ *
+ * ETP_FW_VERSION_QUERY is:
+ * Byte 1:
+ * - bit 0..3: IC BODY
+ * Byte 2:
+ * - bit 4: HiddenButton
+ * - bit 5: PS2_SMBUS_NOTIFY
+ * - bit 6: PS2CRCCheck
+ */
+#define ETP_NEW_IC_SMBUS_HOST_NOTIFY(fw_version) \
+ ((((fw_version) & 0x0f2000) == 0x0f2000) && \
+ ((fw_version) & 0x0000ff) > 0)
+
+/*
* The base position for one finger, v4 hardware
*/
struct finger_pos {
@@ -114,6 +138,25 @@ struct finger_pos {
unsigned int y;
};
+struct elantech_device_info {
+ unsigned char capabilities[3];
+ unsigned char samples[3];
+ unsigned char debug;
+ unsigned char hw_version;
+ unsigned int fw_version;
+ unsigned int x_res;
+ unsigned int y_res;
+ unsigned int bus;
+ bool paritycheck;
+ bool jumpy_cursor;
+ bool reports_pressure;
+ bool crc_enabled;
+ bool set_hw_resolution;
+ bool has_trackpoint;
+ int (*send_cmd)(struct psmouse *psmouse, unsigned char c,
+ unsigned char *param);
+};
+
struct elantech_data {
struct input_dev *tp_dev; /* Relative device for trackpoint */
char tp_phys[32];
@@ -127,27 +170,18 @@ struct elantech_data {
unsigned char reg_24;
unsigned char reg_25;
unsigned char reg_26;
- unsigned char debug;
- unsigned char capabilities[3];
- unsigned char samples[3];
- bool paritycheck;
- bool jumpy_cursor;
- bool reports_pressure;
- bool crc_enabled;
- bool set_hw_resolution;
- unsigned char hw_version;
- unsigned int fw_version;
unsigned int single_finger_reports;
unsigned int y_max;
unsigned int width;
struct finger_pos mt[ETP_MAX_FINGERS];
unsigned char parity[256];
- int (*send_cmd)(struct psmouse *psmouse, unsigned char c, unsigned char *param);
+ struct elantech_device_info info;
void (*original_set_rate)(struct psmouse *psmouse, unsigned int rate);
};
#ifdef CONFIG_MOUSE_PS2_ELANTECH
int elantech_detect(struct psmouse *psmouse, bool set_properties);
+int elantech_init_ps2(struct psmouse *psmouse);
int elantech_init(struct psmouse *psmouse);
#else
static inline int elantech_detect(struct psmouse *psmouse, bool set_properties)
@@ -158,6 +192,19 @@ static inline int elantech_init(struct psmouse *psmouse)
{
return -ENOSYS;
}
+static inline int elantech_init_ps2(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
#endif /* CONFIG_MOUSE_PS2_ELANTECH */
+#if defined(CONFIG_MOUSE_PS2_ELANTECH_SMBUS)
+int elantech_init_smbus(struct psmouse *psmouse);
+#else
+static inline int elantech_init_smbus(struct psmouse *psmouse)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_MOUSE_PS2_ELANTECH_SMBUS */
+
#endif
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 8900c31..5ff5b19 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -856,7 +856,17 @@ static const struct psmouse_protocol psmouse_protocols[] = {
.name = "ETPS/2",
.alias = "elantech",
.detect = elantech_detect,
- .init = elantech_init,
+ .init = elantech_init_ps2,
+ },
+#endif
+#ifdef CONFIG_MOUSE_PS2_ELANTECH_SMBUS
+ {
+ .type = PSMOUSE_ELANTECH_SMBUS,
+ .name = "ETSMBus",
+ .alias = "elantech-smbus",
+ .detect = elantech_detect,
+ .init = elantech_init_smbus,
+ .smbus_companion = true,
},
#endif
#ifdef CONFIG_MOUSE_PS2_SENTELIC
@@ -1158,8 +1168,13 @@ static int psmouse_extensions(struct psmouse *psmouse,
/* Try Elantech touchpad */
if (max_proto > PSMOUSE_IMEX &&
psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
- &max_proto, set_properties, true)) {
- return PSMOUSE_ELANTECH;
+ &max_proto, set_properties, false)) {
+ if (!set_properties)
+ return PSMOUSE_ELANTECH;
+
+ ret = elantech_init(psmouse);
+ if (ret >= 0)
+ return ret;
}
if (max_proto > PSMOUSE_IMEX) {
diff --git a/drivers/input/mouse/psmouse-smbus.c b/drivers/input/mouse/psmouse-smbus.c
index c7ac24d..852d4b4 100644
--- a/drivers/input/mouse/psmouse-smbus.c
+++ b/drivers/input/mouse/psmouse-smbus.c
@@ -23,6 +23,7 @@ struct psmouse_smbus_dev {
struct i2c_client *client;
struct list_head node;
bool dead;
+ bool need_deactivate;
};
static LIST_HEAD(psmouse_smbus_list);
@@ -118,7 +119,10 @@ static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse)
static int psmouse_smbus_reconnect(struct psmouse *psmouse)
{
- psmouse_deactivate(psmouse);
+ struct psmouse_smbus_dev *smbdev = psmouse->private;
+
+ if (smbdev->need_deactivate)
+ psmouse_deactivate(psmouse);
return 0;
}
@@ -225,6 +229,7 @@ void psmouse_smbus_cleanup(struct psmouse *psmouse)
int psmouse_smbus_init(struct psmouse *psmouse,
const struct i2c_board_info *board,
const void *pdata, size_t pdata_size,
+ bool need_deactivate,
bool leave_breadcrumbs)
{
struct psmouse_smbus_dev *smbdev;
@@ -236,13 +241,20 @@ int psmouse_smbus_init(struct psmouse *psmouse,
smbdev->psmouse = psmouse;
smbdev->board = *board;
+ smbdev->need_deactivate = need_deactivate;
- smbdev->board.platform_data = kmemdup(pdata, pdata_size, GFP_KERNEL);
- if (!smbdev->board.platform_data) {
- kfree(smbdev);
- return -ENOMEM;
+ if (pdata) {
+ smbdev->board.platform_data = kmemdup(pdata, pdata_size,
+ GFP_KERNEL);
+ if (!smbdev->board.platform_data) {
+ kfree(smbdev);
+ return -ENOMEM;
+ }
}
+ if (need_deactivate)
+ psmouse_deactivate(psmouse);
+
psmouse->private = smbdev;
psmouse->protocol_handler = psmouse_smbus_process_byte;
psmouse->reconnect = psmouse_smbus_reconnect;
@@ -250,8 +262,6 @@ int psmouse_smbus_init(struct psmouse *psmouse,
psmouse->disconnect = psmouse_smbus_disconnect;
psmouse->resync_time = 0;
- psmouse_deactivate(psmouse);
-
mutex_lock(&psmouse_smbus_mutex);
list_add_tail(&smbdev->node, &psmouse_smbus_list);
mutex_unlock(&psmouse_smbus_mutex);
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 71ac500..64c3a5d 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -68,6 +68,7 @@ enum psmouse_type {
PSMOUSE_VMMOUSE,
PSMOUSE_BYD,
PSMOUSE_SYNAPTICS_SMBUS,
+ PSMOUSE_ELANTECH_SMBUS,
PSMOUSE_AUTO /* This one should always be last */
};
@@ -224,6 +225,7 @@ struct i2c_board_info;
int psmouse_smbus_init(struct psmouse *psmouse,
const struct i2c_board_info *board,
const void *pdata, size_t pdata_size,
+ bool need_deactivate,
bool leave_breadcrumbs);
void psmouse_smbus_cleanup(struct psmouse *psmouse);
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index a9591d2..55d3350 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -1754,7 +1754,7 @@ static int synaptics_create_intertouch(struct psmouse *psmouse,
};
return psmouse_smbus_init(psmouse, &intertouch_board,
- &pdata, sizeof(pdata),
+ &pdata, sizeof(pdata), true,
leave_breadcrumbs);
}
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 3e613af..32267c1 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -164,6 +164,17 @@
To compile this driver as a module, choose M here: the
module will be called chipone_icn8318.
+config TOUCHSCREEN_CHIPONE_ICN8505
+ tristate "chipone icn8505 touchscreen controller"
+ depends on I2C && ACPI
+ help
+ Say Y here if you have a ChipOne icn8505 based I2C touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called chipone_icn8505.
+
config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index dddae79..fd4fd32 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -19,6 +19,7 @@
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 0919472..54fe190 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -194,6 +194,8 @@ enum t100_type {
/* Delay times */
#define MXT_BACKUP_TIME 50 /* msec */
+#define MXT_RESET_GPIO_TIME 20 /* msec */
+#define MXT_RESET_INVALID_CHG 100 /* msec */
#define MXT_RESET_TIME 200 /* msec */
#define MXT_RESET_TIMEOUT 3000 /* msec */
#define MXT_CRC_TIMEOUT 1000 /* msec */
@@ -1208,7 +1210,7 @@ static int mxt_soft_reset(struct mxt_data *data)
return ret;
/* Ignore CHG line for 100ms after reset */
- msleep(100);
+ msleep(MXT_RESET_INVALID_CHG);
mxt_acquire_irq(data);
@@ -2999,142 +3001,6 @@ static int mxt_parse_device_properties(struct mxt_data *data)
return 0;
}
-#ifdef CONFIG_ACPI
-
-struct mxt_acpi_platform_data {
- const char *hid;
- const struct property_entry *props;
-};
-
-static unsigned int samus_touchpad_buttons[] = {
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- BTN_LEFT
-};
-
-static const struct property_entry samus_touchpad_props[] = {
- PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", samus_touchpad_buttons),
- { }
-};
-
-static struct mxt_acpi_platform_data samus_platform_data[] = {
- {
- /* Touchpad */
- .hid = "ATML0000",
- .props = samus_touchpad_props,
- },
- {
- /* Touchscreen */
- .hid = "ATML0001",
- },
- { }
-};
-
-static unsigned int chromebook_tp_buttons[] = {
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- KEY_RESERVED,
- BTN_LEFT
-};
-
-static const struct property_entry chromebook_tp_props[] = {
- PROPERTY_ENTRY_U32_ARRAY("linux,gpio-keymap", chromebook_tp_buttons),
- { }
-};
-
-static struct mxt_acpi_platform_data chromebook_platform_data[] = {
- {
- /* Touchpad */
- .hid = "ATML0000",
- .props = chromebook_tp_props,
- },
- {
- /* Touchscreen */
- .hid = "ATML0001",
- },
- { }
-};
-
-static const struct dmi_system_id mxt_dmi_table[] = {
- {
- /* 2015 Google Pixel */
- .ident = "Chromebook Pixel 2",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
- },
- .driver_data = samus_platform_data,
- },
- {
- /* Samsung Chromebook Pro */
- .ident = "Samsung Chromebook Pro",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Google"),
- DMI_MATCH(DMI_PRODUCT_NAME, "Caroline"),
- },
- .driver_data = samus_platform_data,
- },
- {
- /* Other Google Chromebooks */
- .ident = "Chromebook",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
- },
- .driver_data = chromebook_platform_data,
- },
- { }
-};
-
-static int mxt_prepare_acpi_properties(struct i2c_client *client)
-{
- struct acpi_device *adev;
- const struct dmi_system_id *system_id;
- const struct mxt_acpi_platform_data *acpi_pdata;
-
- adev = ACPI_COMPANION(&client->dev);
- if (!adev)
- return -ENOENT;
-
- system_id = dmi_first_match(mxt_dmi_table);
- if (!system_id)
- return -ENOENT;
-
- acpi_pdata = system_id->driver_data;
- if (!acpi_pdata)
- return -ENOENT;
-
- while (acpi_pdata->hid) {
- if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid)) {
- /*
- * Remove previously installed properties if we
- * are probing this device not for the very first
- * time.
- */
- device_remove_properties(&client->dev);
-
- /*
- * Now install the platform-specific properties
- * that are missing from ACPI.
- */
- device_add_properties(&client->dev, acpi_pdata->props);
- break;
- }
-
- acpi_pdata++;
- }
-
- return 0;
-}
-#else
-static int mxt_prepare_acpi_properties(struct i2c_client *client)
-{
- return -ENOENT;
-}
-#endif
-
static const struct dmi_system_id chromebook_T9_suspend_dmi[] = {
{
.matches = {
@@ -3156,6 +3022,18 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
int error;
/*
+ * Ignore devices that do not have device properties attached to
+ * them, as we need help determining whether we are dealing with
+ * touch screen or touchpad.
+ *
+ * So far on x86 the only users of Atmel touch controllers are
+ * Chromebooks, and chromeos_laptop driver will ensure that
+ * necessary properties are provided (if firmware does not do that).
+ */
+ if (!device_property_present(&client->dev, "compatible"))
+ return -ENXIO;
+
+ /*
* Ignore ACPI devices representing bootloader mode.
*
* This is a bit of a hack: Google Chromebook BIOS creates ACPI
@@ -3186,10 +3064,6 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
data->suspend_mode = dmi_check_system(chromebook_T9_suspend_dmi) ?
MXT_SUSPEND_T9_CTRL : MXT_SUSPEND_DEEP_SLEEP;
- error = mxt_prepare_acpi_properties(client);
- if (error && error != -ENOENT)
- return error;
-
error = mxt_parse_device_properties(data);
if (error)
return error;
@@ -3210,20 +3084,14 @@ static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
return error;
}
- if (data->reset_gpio) {
- data->in_bootloader = true;
- msleep(MXT_RESET_TIME);
- reinit_completion(&data->bl_completion);
- gpiod_set_value(data->reset_gpio, 1);
- error = mxt_wait_for_completion(data, &data->bl_completion,
- MXT_RESET_TIMEOUT);
- if (error)
- return error;
- data->in_bootloader = false;
- }
-
disable_irq(client->irq);
+ if (data->reset_gpio) {
+ msleep(MXT_RESET_GPIO_TIME);
+ gpiod_set_value(data->reset_gpio, 1);
+ msleep(MXT_RESET_INVALID_CHG);
+ }
+
error = mxt_initialize(data);
if (error)
return error;
diff --git a/drivers/input/touchscreen/chipone_icn8505.c b/drivers/input/touchscreen/chipone_icn8505.c
new file mode 100644
index 0000000..c768186
--- /dev/null
+++ b/drivers/input/touchscreen/chipone_icn8505.c
@@ -0,0 +1,520 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Driver for ChipOne icn8505 i2c touchscreen controller
+ *
+ * Copyright (c) 2015-2018 Red Hat Inc.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
+#include <linux/module.h>
+
+/* Normal operation mode defines */
+#define ICN8505_REG_ADDR_WIDTH 16
+
+#define ICN8505_REG_POWER 0x0004
+#define ICN8505_REG_TOUCHDATA 0x1000
+#define ICN8505_REG_CONFIGDATA 0x8000
+
+/* ICN8505_REG_POWER commands */
+#define ICN8505_POWER_ACTIVE 0x00
+#define ICN8505_POWER_MONITOR 0x01
+#define ICN8505_POWER_HIBERNATE 0x02
+/*
+ * The Android driver uses these to turn on/off the charger filter, but the
+ * filter is way too aggressive making e.g. onscreen keyboards unusable.
+ */
+#define ICN8505_POWER_ENA_CHARGER_MODE 0x55
+#define ICN8505_POWER_DIS_CHARGER_MODE 0x66
+
+#define ICN8505_MAX_TOUCHES 10
+
+/* Programming mode defines */
+#define ICN8505_PROG_I2C_ADDR 0x30
+#define ICN8505_PROG_REG_ADDR_WIDTH 24
+
+#define MAX_FW_UPLOAD_TRIES 3
+
+struct icn8505_touch {
+ u8 slot;
+ u8 x[2];
+ u8 y[2];
+ u8 pressure; /* Seems more like finger width then pressure really */
+ u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8505_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
+#define ICN8505_EVENT_UPDATE1 2 /* New or updated coordinates */
+#define ICN8505_EVENT_UPDATE2 3 /* New or updated coordinates */
+#define ICN8505_EVENT_END 4 /* Finger lifted */
+} __packed;
+
+struct icn8505_touch_data {
+ u8 softbutton;
+ u8 touch_count;
+ struct icn8505_touch touches[ICN8505_MAX_TOUCHES];
+} __packed;
+
+struct icn8505_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *wake_gpio;
+ struct touchscreen_properties prop;
+ char firmware_name[32];
+};
+
+static int icn8505_read_xfer(struct i2c_client *client, u16 i2c_addr,
+ int reg_addr, int reg_addr_width,
+ void *data, int len, bool silent)
+{
+ u8 buf[3];
+ int i, ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = i2c_addr,
+ .buf = buf,
+ .len = reg_addr_width / 8,
+ },
+ {
+ .addr = i2c_addr,
+ .flags = I2C_M_RD,
+ .buf = data,
+ .len = len,
+ }
+ };
+
+ for (i = 0; i < (reg_addr_width / 8); i++)
+ buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff;
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret != ARRAY_SIZE(msg)) {
+ if (ret >= 0)
+ ret = -EIO;
+ if (!silent)
+ dev_err(&client->dev,
+ "Error reading addr %#x reg %#x: %d\n",
+ i2c_addr, reg_addr, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int icn8505_write_xfer(struct i2c_client *client, u16 i2c_addr,
+ int reg_addr, int reg_addr_width,
+ const void *data, int len, bool silent)
+{
+ u8 buf[3 + 32]; /* 3 bytes for 24 bit reg-addr + 32 bytes max len */
+ int i, ret;
+ struct i2c_msg msg = {
+ .addr = i2c_addr,
+ .buf = buf,
+ .len = reg_addr_width / 8 + len,
+ };
+
+ if (WARN_ON(len > 32))
+ return -EINVAL;
+
+ for (i = 0; i < (reg_addr_width / 8); i++)
+ buf[i] = (reg_addr >> (reg_addr_width - (i + 1) * 8)) & 0xff;
+
+ memcpy(buf + reg_addr_width / 8, data, len);
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret != 1) {
+ if (ret >= 0)
+ ret = -EIO;
+ if (!silent)
+ dev_err(&client->dev,
+ "Error writing addr %#x reg %#x: %d\n",
+ i2c_addr, reg_addr, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int icn8505_read_data(struct icn8505_data *icn8505, int reg,
+ void *buf, int len)
+{
+ return icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg,
+ ICN8505_REG_ADDR_WIDTH, buf, len, false);
+}
+
+static int icn8505_read_reg_silent(struct icn8505_data *icn8505, int reg)
+{
+ u8 buf;
+ int error;
+
+ error = icn8505_read_xfer(icn8505->client, icn8505->client->addr, reg,
+ ICN8505_REG_ADDR_WIDTH, &buf, 1, true);
+ if (error)
+ return error;
+
+ return buf;
+}
+
+static int icn8505_write_reg(struct icn8505_data *icn8505, int reg, u8 val)
+{
+ return icn8505_write_xfer(icn8505->client, icn8505->client->addr, reg,
+ ICN8505_REG_ADDR_WIDTH, &val, 1, false);
+}
+
+static int icn8505_read_prog_data(struct icn8505_data *icn8505, int reg,
+ void *buf, int len)
+{
+ return icn8505_read_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
+ ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false);
+}
+
+static int icn8505_write_prog_data(struct icn8505_data *icn8505, int reg,
+ const void *buf, int len)
+{
+ return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
+ ICN8505_PROG_REG_ADDR_WIDTH, buf, len, false);
+}
+
+static int icn8505_write_prog_reg(struct icn8505_data *icn8505, int reg, u8 val)
+{
+ return icn8505_write_xfer(icn8505->client, ICN8505_PROG_I2C_ADDR, reg,
+ ICN8505_PROG_REG_ADDR_WIDTH, &val, 1, false);
+}
+
+/*
+ * Note this function uses a number of magic register addresses and values,
+ * there are deliberately no defines for these because the algorithm is taken
+ * from the icn85xx Android driver and I do not want to make up possibly wrong
+ * names for the addresses and/or values.
+ */
+static int icn8505_try_fw_upload(struct icn8505_data *icn8505,
+ const struct firmware *fw)
+{
+ struct device *dev = &icn8505->client->dev;
+ size_t offset, count;
+ int error;
+ u8 buf[4];
+ u32 crc;
+
+ /* Put the controller in programming mode */
+ error = icn8505_write_prog_reg(icn8505, 0xcc3355, 0x5a);
+ if (error)
+ return error;
+
+ usleep_range(2000, 5000);
+
+ error = icn8505_write_prog_reg(icn8505, 0x040400, 0x01);
+ if (error)
+ return error;
+
+ usleep_range(2000, 5000);
+
+ error = icn8505_read_prog_data(icn8505, 0x040002, buf, 1);
+ if (error)
+ return error;
+
+ if (buf[0] != 0x85) {
+ dev_err(dev, "Failed to enter programming mode\n");
+ return -ENODEV;
+ }
+
+ usleep_range(1000, 5000);
+
+ /* Enable CRC mode */
+ error = icn8505_write_prog_reg(icn8505, 0x40028, 1);
+ if (error)
+ return error;
+
+ /* Send the firmware to SRAM */
+ for (offset = 0; offset < fw->size; offset += count) {
+ count = min_t(size_t, fw->size - offset, 32);
+ error = icn8505_write_prog_data(icn8505, offset,
+ fw->data + offset, count);
+ if (error)
+ return error;
+ }
+
+ /* Disable CRC mode */
+ error = icn8505_write_prog_reg(icn8505, 0x40028, 0);
+ if (error)
+ return error;
+
+ /* Get and check length and CRC */
+ error = icn8505_read_prog_data(icn8505, 0x40034, buf, 2);
+ if (error)
+ return error;
+
+ if (get_unaligned_le16(buf) != fw->size) {
+ dev_warn(dev, "Length mismatch after uploading fw\n");
+ return -EIO;
+ }
+
+ error = icn8505_read_prog_data(icn8505, 0x4002c, buf, 4);
+ if (error)
+ return error;
+
+ crc = crc32_be(0, fw->data, fw->size);
+ if (get_unaligned_le32(buf) != crc) {
+ dev_warn(dev, "CRC mismatch after uploading fw\n");
+ return -EIO;
+ }
+
+ /* Boot controller from SRAM */
+ error = icn8505_write_prog_reg(icn8505, 0x40400, 0x03);
+ if (error)
+ return error;
+
+ usleep_range(2000, 5000);
+ return 0;
+}
+
+static int icn8505_upload_fw(struct icn8505_data *icn8505)
+{
+ struct device *dev = &icn8505->client->dev;
+ const struct firmware *fw;
+ int i, error;
+
+ /*
+ * Always load the firmware, even if we don't need it at boot, we
+ * we may need it at resume. Having loaded it once will make the
+ * firmware class code cache it at suspend/resume.
+ */
+ error = request_firmware(&fw, icn8505->firmware_name, dev);
+ if (error) {
+ dev_err(dev, "Firmware request error %d\n", error);
+ return error;
+ }
+
+ /* Check if the controller is not already up and running */
+ if (icn8505_read_reg_silent(icn8505, 0x000a) == 0x85)
+ goto success;
+
+ for (i = 1; i <= MAX_FW_UPLOAD_TRIES; i++) {
+ error = icn8505_try_fw_upload(icn8505, fw);
+ if (!error)
+ goto success;
+
+ dev_err(dev, "Failed to upload firmware: %d (attempt %d/%d)\n",
+ error, i, MAX_FW_UPLOAD_TRIES);
+ usleep_range(2000, 5000);
+ }
+
+success:
+ release_firmware(fw);
+ return error;
+}
+
+static bool icn8505_touch_active(u8 event)
+{
+ return event == ICN8505_EVENT_UPDATE1 ||
+ event == ICN8505_EVENT_UPDATE2;
+}
+
+static irqreturn_t icn8505_irq(int irq, void *dev_id)
+{
+ struct icn8505_data *icn8505 = dev_id;
+ struct device *dev = &icn8505->client->dev;
+ struct icn8505_touch_data touch_data;
+ int i, error;
+
+ error = icn8505_read_data(icn8505, ICN8505_REG_TOUCHDATA,
+ &touch_data, sizeof(touch_data));
+ if (error) {
+ dev_err(dev, "Error reading touch data: %d\n", error);
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.touch_count > ICN8505_MAX_TOUCHES) {
+ dev_warn(dev, "Too many touches %d > %d\n",
+ touch_data.touch_count, ICN8505_MAX_TOUCHES);
+ touch_data.touch_count = ICN8505_MAX_TOUCHES;
+ }
+
+ for (i = 0; i < touch_data.touch_count; i++) {
+ struct icn8505_touch *touch = &touch_data.touches[i];
+ bool act = icn8505_touch_active(touch->event);
+
+ input_mt_slot(icn8505->input, touch->slot);
+ input_mt_report_slot_state(icn8505->input, MT_TOOL_FINGER, act);
+ if (!act)
+ continue;
+
+ touchscreen_report_pos(icn8505->input, &icn8505->prop,
+ get_unaligned_le16(touch->x),
+ get_unaligned_le16(touch->y),
+ true);
+ }
+
+ input_mt_sync_frame(icn8505->input);
+ input_report_key(icn8505->input, KEY_LEFTMETA,
+ touch_data.softbutton == 1);
+ input_sync(icn8505->input);
+
+ return IRQ_HANDLED;
+}
+
+static int icn8505_probe_acpi(struct icn8505_data *icn8505, struct device *dev)
+{
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ const char *subsys = "unknown";
+ struct acpi_device *adev;
+ union acpi_object *obj;
+ acpi_status status;
+
+ adev = ACPI_COMPANION(dev);
+ if (!adev)
+ return -ENODEV;
+
+ status = acpi_evaluate_object(adev->handle, "_SUB", NULL, &buffer);
+ if (ACPI_SUCCESS(status)) {
+ obj = buffer.pointer;
+ if (obj->type == ACPI_TYPE_STRING)
+ subsys = obj->string.pointer;
+ else
+ dev_warn(dev, "Warning ACPI _SUB did not return a string\n");
+ } else {
+ dev_warn(dev, "Warning ACPI _SUB failed: %#x\n", status);
+ buffer.pointer = NULL;
+ }
+
+ snprintf(icn8505->firmware_name, sizeof(icn8505->firmware_name),
+ "chipone/icn8505-%s.fw", subsys);
+
+ kfree(buffer.pointer);
+ return 0;
+}
+
+static int icn8505_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct icn8505_data *icn8505;
+ struct input_dev *input;
+ __le16 resolution[2];
+ int error;
+
+ if (!client->irq) {
+ dev_err(dev, "No irq specified\n");
+ return -EINVAL;
+ }
+
+ icn8505 = devm_kzalloc(dev, sizeof(*icn8505), GFP_KERNEL);
+ if (!icn8505)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+
+ input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
+ input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
+ input_set_capability(input, EV_KEY, KEY_LEFTMETA);
+
+ icn8505->client = client;
+ icn8505->input = input;
+ input_set_drvdata(input, icn8505);
+
+ error = icn8505_probe_acpi(icn8505, dev);
+ if (error)
+ return error;
+
+ error = icn8505_upload_fw(icn8505);
+ if (error)
+ return error;
+
+ error = icn8505_read_data(icn8505, ICN8505_REG_CONFIGDATA,
+ resolution, sizeof(resolution));
+ if (error) {
+ dev_err(dev, "Error reading resolution: %d\n", error);
+ return error;
+ }
+
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ le16_to_cpu(resolution[0]) - 1, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ le16_to_cpu(resolution[1]) - 1, 0, 0);
+
+ touchscreen_parse_properties(input, true, &icn8505->prop);
+ if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
+ !input_abs_get_max(input, ABS_MT_POSITION_Y)) {
+ dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+ return -EINVAL;
+ }
+
+ error = input_mt_init_slots(input, ICN8505_MAX_TOUCHES,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error)
+ return error;
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL, icn8505_irq,
+ IRQF_ONESHOT, client->name, icn8505);
+ if (error) {
+ dev_err(dev, "Error requesting irq: %d\n", error);
+ return error;
+ }
+
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, icn8505);
+ return 0;
+}
+
+static int __maybe_unused icn8505_suspend(struct device *dev)
+{
+ struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev));
+
+ disable_irq(icn8505->client->irq);
+
+ icn8505_write_reg(icn8505, ICN8505_REG_POWER, ICN8505_POWER_HIBERNATE);
+
+ return 0;
+}
+
+static int __maybe_unused icn8505_resume(struct device *dev)
+{
+ struct icn8505_data *icn8505 = i2c_get_clientdata(to_i2c_client(dev));
+ int error;
+
+ error = icn8505_upload_fw(icn8505);
+ if (error)
+ return error;
+
+ enable_irq(icn8505->client->irq);
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(icn8505_pm_ops, icn8505_suspend, icn8505_resume);
+
+static const struct acpi_device_id icn8505_acpi_match[] = {
+ { "CHPN0001" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, icn8505_acpi_match);
+
+static struct i2c_driver icn8505_driver = {
+ .driver = {
+ .name = "chipone_icn8505",
+ .pm = &icn8505_pm_ops,
+ .acpi_match_table = icn8505_acpi_match,
+ },
+ .probe_new = icn8505_probe,
+};
+
+module_i2c_driver(icn8505_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8505 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 9736c83..f2d9c2c 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -933,6 +933,7 @@ MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
#ifdef CONFIG_ACPI
static const struct acpi_device_id goodix_acpi_match[] = {
{ "GDIX1001", 0 },
+ { "GDIX1002", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
diff --git a/drivers/input/touchscreen/mk712.c b/drivers/input/touchscreen/mk712.c
index bd535282..c179060 100644
--- a/drivers/input/touchscreen/mk712.c
+++ b/drivers/input/touchscreen/mk712.c
@@ -17,7 +17,7 @@
* found in Gateway AOL Connected Touchpad computers.
*
* Documentation for ICS MK712 can be found at:
- * http://www.idt.com/products/getDoc.cfm?docID=18713923
+ * https://www.idt.com/general-parts/mk712-touch-screen-controller
*/
/*
diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
index f1043ae..b86c1e5 100644
--- a/drivers/input/touchscreen/ti_am335x_tsc.c
+++ b/drivers/input/touchscreen/ti_am335x_tsc.c
@@ -34,6 +34,8 @@
#define SEQ_SETTLE 275
#define MAX_12BIT ((1 << 12) - 1)
+#define TSC_IRQENB_MASK (IRQENB_FIFO0THRES | IRQENB_EOS | IRQENB_HW_PEN)
+
static const int config_pins[] = {
STEPCONFIG_XPP,
STEPCONFIG_XNN,
@@ -274,6 +276,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
if (status & IRQENB_HW_PEN) {
ts_dev->pen_down = true;
irqclr |= IRQENB_HW_PEN;
+ pm_stay_awake(ts_dev->mfd_tscadc->dev);
}
if (status & IRQENB_PENUP) {
@@ -283,6 +286,7 @@ static irqreturn_t titsc_irq(int irq, void *dev)
input_report_key(input_dev, BTN_TOUCH, 0);
input_report_abs(input_dev, ABS_PRESSURE, 0);
input_sync(input_dev);
+ pm_relax(ts_dev->mfd_tscadc->dev);
} else {
ts_dev->pen_down = true;
}
@@ -432,6 +436,7 @@ static int titsc_probe(struct platform_device *pdev)
goto err_free_mem;
}
+ titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES);
titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_EOS);
err = titsc_config_wires(ts_dev);
@@ -495,6 +500,7 @@ static int __maybe_unused titsc_suspend(struct device *dev)
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
if (device_may_wakeup(tscadc_dev->dev)) {
+ titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
idle = titsc_readl(ts_dev, REG_IRQENABLE);
titsc_writel(ts_dev, REG_IRQENABLE,
(idle | IRQENB_HW_PEN));
@@ -513,6 +519,7 @@ static int __maybe_unused titsc_resume(struct device *dev)
titsc_writel(ts_dev, REG_IRQWAKEUP,
0x00);
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
+ pm_relax(ts_dev->mfd_tscadc->dev);
}
titsc_step_config(ts_dev);
titsc_writel(ts_dev, REG_FIFO0THR,
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index c6cf908..d61570d 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -440,6 +440,8 @@ static int panjit_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
#define MTOUCHUSB_RESET 7
#define MTOUCHUSB_REQ_CTRLLR_ID 10
+#define MTOUCHUSB_REQ_CTRLLR_ID_LEN 16
+
static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if (hwcalib_xy) {
@@ -454,11 +456,93 @@ static int mtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
return 1;
}
+struct mtouch_priv {
+ u8 fw_rev_major;
+ u8 fw_rev_minor;
+};
+
+static ssize_t mtouch_firmware_rev_show(struct device *dev,
+ struct device_attribute *attr, char *output)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usbtouch_usb *usbtouch = usb_get_intfdata(intf);
+ struct mtouch_priv *priv = usbtouch->priv;
+
+ return scnprintf(output, PAGE_SIZE, "%1x.%1x\n",
+ priv->fw_rev_major, priv->fw_rev_minor);
+}
+static DEVICE_ATTR(firmware_rev, 0444, mtouch_firmware_rev_show, NULL);
+
+static struct attribute *mtouch_attrs[] = {
+ &dev_attr_firmware_rev.attr,
+ NULL
+};
+
+static const struct attribute_group mtouch_attr_group = {
+ .attrs = mtouch_attrs,
+};
+
+static int mtouch_get_fw_revision(struct usbtouch_usb *usbtouch)
+{
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+ struct mtouch_priv *priv = usbtouch->priv;
+ u8 *buf;
+ int ret;
+
+ buf = kzalloc(MTOUCHUSB_REQ_CTRLLR_ID_LEN, GFP_NOIO);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ MTOUCHUSB_REQ_CTRLLR_ID,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, MTOUCHUSB_REQ_CTRLLR_ID_LEN,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret != MTOUCHUSB_REQ_CTRLLR_ID_LEN) {
+ dev_warn(&usbtouch->interface->dev,
+ "Failed to read FW rev: %d\n", ret);
+ ret = ret < 0 ? ret : -EIO;
+ goto free;
+ }
+
+ priv->fw_rev_major = buf[3];
+ priv->fw_rev_minor = buf[4];
+
+ ret = 0;
+
+free:
+ kfree(buf);
+ return ret;
+}
+
+static int mtouch_alloc(struct usbtouch_usb *usbtouch)
+{
+ int ret;
+
+ usbtouch->priv = kmalloc(sizeof(struct mtouch_priv), GFP_KERNEL);
+ if (!usbtouch->priv)
+ return -ENOMEM;
+
+ ret = sysfs_create_group(&usbtouch->interface->dev.kobj,
+ &mtouch_attr_group);
+ if (ret) {
+ kfree(usbtouch->priv);
+ usbtouch->priv = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
static int mtouch_init(struct usbtouch_usb *usbtouch)
{
int ret, i;
struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+ ret = mtouch_get_fw_revision(usbtouch);
+ if (ret)
+ return ret;
+
ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
MTOUCHUSB_RESET,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
@@ -492,6 +576,14 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)
return 0;
}
+
+static void mtouch_exit(struct usbtouch_usb *usbtouch)
+{
+ struct mtouch_priv *priv = usbtouch->priv;
+
+ sysfs_remove_group(&usbtouch->interface->dev.kobj, &mtouch_attr_group);
+ kfree(priv);
+}
#endif
@@ -1119,7 +1211,9 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.max_yc = 0x4000,
.rept_size = 11,
.read_data = mtouch_read_data,
+ .alloc = mtouch_alloc,
.init = mtouch_init,
+ .exit = mtouch_exit,
},
#endif
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index d610241..36156a4 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -229,7 +229,7 @@ int cros_ec_suspend(struct cros_ec_device *ec_dev)
}
EXPORT_SYMBOL(cros_ec_suspend);
-static void cros_ec_drain_events(struct cros_ec_device *ec_dev)
+static void cros_ec_report_events_during_suspend(struct cros_ec_device *ec_dev)
{
while (cros_ec_get_next_event(ec_dev, NULL) > 0)
blocking_notifier_call_chain(&ec_dev->event_notifier,
@@ -253,21 +253,16 @@ int cros_ec_resume(struct cros_ec_device *ec_dev)
dev_dbg(ec_dev->dev, "Error %d sending resume event to ec",
ret);
- /*
- * In some cases, we need to distinguish between events that occur
- * during suspend if the EC is not a wake source. For example,
- * keypresses during suspend should be discarded if it does not wake
- * the system.
- *
- * If the EC is not a wake source, drain the event queue and mark them
- * as "queued during suspend".
- */
if (ec_dev->wake_enabled) {
disable_irq_wake(ec_dev->irq);
ec_dev->wake_enabled = 0;
- } else {
- cros_ec_drain_events(ec_dev);
}
+ /*
+ * Let the mfd devices know about events that occur during
+ * suspend. This way the clients know what to do with them.
+ */
+ cros_ec_report_events_during_suspend(ec_dev);
+
return 0;
}