diff options
author | Dmitry Torokhov | 2018-06-04 13:33:12 -0700 |
---|---|---|
committer | Dmitry Torokhov | 2018-06-04 13:33:12 -0700 |
commit | c13aca79ff3c4af5fd31a5b2743a90eba6e36a26 (patch) | |
tree | 8f77894f61822d1ae5285c07c801af7c62f71afd /drivers/input/mouse | |
parent | 40f7090bb1b4ec327ea1e1402ff5783af5b35195 (diff) | |
parent | 5ca4d1ae9bad0f59bd6f851c39b19f5366953666 (diff) |
Merge branch 'next' into for-linus
Prepare input updates for 4.18 merge window.
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/Kconfig | 12 | ||||
-rw-r--r-- | drivers/input/mouse/alps.c | 80 | ||||
-rw-r--r-- | drivers/input/mouse/elan_i2c_core.c | 88 | ||||
-rw-r--r-- | drivers/input/mouse/elantech.c | 479 | ||||
-rw-r--r-- | drivers/input/mouse/elantech.h | 69 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-base.c | 21 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse-smbus.c | 24 | ||||
-rw-r--r-- | drivers/input/mouse/psmouse.h | 2 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.c | 2 |
9 files changed, 597 insertions, 180 deletions
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 89ebb8f39fee..f27f23f2d99a 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -133,6 +133,18 @@ config MOUSE_PS2_ELANTECH 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 38f9501acdf0..cb5579716dba 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 @@ error: 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 75e757520ef0..2690a4b7ed53 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 @@ out: * 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. diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c index db47a5e1d114..fb4d902c4403 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 e1cbf409d9c8..119727085a60 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 8900c3166ebf..5ff5b1952be0 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 c7ac24d119c1..852d4b486ddb 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 71ac50082c8b..64c3a5d3fb3e 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 a9591d278145..55d33500d55e 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); } |