Commit 689f891c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull some more input subsystem updates from Dmitry Torokhov:
 "An update to the ALPS driver to support the V8 protocol with
  touchstick, a change for i8042 to skip selftest on many Asus laptops
  which helps to keep their touchpads working after resume, and a couple
  other driver fixes"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
  Input: i8042 - skip selftest on ASUS laptops
  Input: melfas_mip4 - add ic_name sysfs attribute
  Input: melfas_mip4 - add maintainer information
  Input: melfas_mip4 - add devicetree binding documentations
  Input: elantech - add Fujitsu Lifebook E556 to force crc_enabled
  Input: synaptics-rmi4 - fix error handling in I2C transport driver
  Input: synaptics-rmi4 - fix error handling in SPI transport driver
  Input: ALPS - add V8 protocol documentation
  Input: ALPS - set DualPoint flag for 74 03 28 devices
  Input: ALPS - allow touchsticks to report pressure
  Input: ALPS - handle 0-pressure 1F events
  Input: ALPS - add touchstick support for SS5 hardware
  Input: elantech - force needed quirks on Fujitsu H760
  Input: elantech - fix Lenovo version typo
parents 8b70f716 1134ca26
* MELFAS MIP4 Touchscreen
Required properties:
- compatible: must be "melfas,mip4_ts"
- reg: I2C slave address of the chip (0x48 or 0x34)
- interrupt-parent: interrupt controller to which the chip is connected
- interrupts: interrupt to which the chip is connected
Optional properties:
- ce-gpios: GPIO connected to the CE (chip enable) pin of the chip
Example:
i2c@00000000 {
touchscreen: melfas_mip4@48 {
compatible = "melfas,mip4_ts";
reg = <0x48>;
interrupt-parent = <&gpio>;
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
ce-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
};
};
...@@ -163,6 +163,7 @@ maxim Maxim Integrated Products ...@@ -163,6 +163,7 @@ maxim Maxim Integrated Products
meas Measurement Specialties meas Measurement Specialties
mediatek MediaTek Inc. mediatek MediaTek Inc.
melexis Melexis N.V. melexis Melexis N.V.
melfas MELFAS Inc.
merrii Merrii Technology Co., Ltd. merrii Merrii Technology Co., Ltd.
micrel Micrel Inc. micrel Micrel Inc.
microchip Microchip Technology Inc. microchip Microchip Technology Inc.
......
...@@ -319,3 +319,60 @@ For touchpad packet, the format is: ...@@ -319,3 +319,60 @@ For touchpad packet, the format is:
otherwise byte 0 bit 4 must be set and byte 0/4/5 are otherwise byte 0 bit 4 must be set and byte 0/4/5 are
in NEW fmt in NEW fmt
F: Number of fingers - 3, 0 means 3 fingers, 1 means 4 ... F: Number of fingers - 3, 0 means 3 fingers, 1 means 4 ...
ALPS Absolute Mode - Protocol Version 8
---------------------------------------
Spoken by SS4 (73 03 14) and SS5 (73 03 28) hardware.
The packet type is given by the APD field, bits 4-5 of byte 3.
Touchpad packet (APD = 0x2):
b7 b6 b5 b4 b3 b2 b1 b0
byte 0: SWM SWR SWL 1 1 0 0 X7
byte 1: 0 X6 X5 X4 X3 X2 X1 X0
byte 2: 0 Y6 Y5 Y4 Y3 Y2 Y1 Y0
byte 3: 0 T&P 1 0 1 0 0 Y7
byte 4: 0 Z6 Z5 Z4 Z3 Z2 Z1 Z0
byte 5: 0 0 0 0 0 0 0 0
SWM, SWR, SWL: Middle, Right, and Left button states
Touchpad 1 Finger packet (APD = 0x0):
b7 b6 b5 b4 b3 b2 b1 b0
byte 0: SWM SWR SWL 1 1 X2 X1 X0
byte 1: X9 X8 X7 1 X6 X5 X4 X3
byte 2: 0 X11 X10 LFB Y3 Y2 Y1 Y0
byte 3: Y5 Y4 0 0 1 TAPF2 TAPF1 TAPF0
byte 4: Zv7 Y11 Y10 1 Y9 Y8 Y7 Y6
byte 5: Zv6 Zv5 Zv4 0 Zv3 Zv2 Zv1 Zv0
TAPF: ???
LFB: ???
Touchpad 2 Finger packet (APD = 0x1):
b7 b6 b5 b4 b3 b2 b1 b0
byte 0: SWM SWR SWL 1 1 AX6 AX5 AX4
byte 1: AX11 AX10 AX9 AX8 AX7 AZ1 AY4 AZ0
byte 2: AY11 AY10 AY9 CONT AY8 AY7 AY6 AY5
byte 3: 0 0 0 1 1 BX6 BX5 BX4
byte 4: BX11 BX10 BX9 BX8 BX7 BZ1 BY4 BZ0
byte 5: BY11 BY10 BY9 0 BY8 BY7 BY5 BY5
CONT: A 3-or-4 Finger packet is to follow
Touchpad 3-or-4 Finger packet (APD = 0x3):
b7 b6 b5 b4 b3 b2 b1 b0
byte 0: SWM SWR SWL 1 1 AX6 AX5 AX4
byte 1: AX11 AX10 AX9 AX8 AX7 AZ1 AY4 AZ0
byte 2: AY11 AY10 AY9 OVF AY8 AY7 AY6 AY5
byte 3: 0 0 1 1 1 BX6 BX5 BX4
byte 4: BX11 BX10 BX9 BX8 BX7 BZ1 BY4 BZ0
byte 5: BY11 BY10 BY9 0 BY8 BY7 BY5 BY5
OVF: 5th finger detected
...@@ -1511,7 +1511,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ...@@ -1511,7 +1511,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
controllers controllers
i8042.notimeout [HW] Ignore timeout condition signalled by controller i8042.notimeout [HW] Ignore timeout condition signalled by controller
i8042.reset [HW] Reset the controller during init and cleanup i8042.reset [HW] Reset the controller during init, cleanup and
suspend-to-ram transitions, only during s2r
transitions, or never reset
Format: { 1 | Y | y | 0 | N | n }
1, Y, y: always reset controller
0, N, n: don't ever reset controller
Default: only on s2r transitions on x86; most other
architectures force reset to be always executed
i8042.unlock [HW] Unlock (ignore) the keylock i8042.unlock [HW] Unlock (ignore) the keylock
i8042.kbdreset [HW] Reset device connected to KBD port i8042.kbdreset [HW] Reset device connected to KBD port
......
...@@ -7829,6 +7829,13 @@ F: Documentation/scsi/megaraid.txt ...@@ -7829,6 +7829,13 @@ F: Documentation/scsi/megaraid.txt
F: drivers/scsi/megaraid.* F: drivers/scsi/megaraid.*
F: drivers/scsi/megaraid/ F: drivers/scsi/megaraid/
MELFAS MIP4 TOUCHSCREEN DRIVER
M: Sangwon Jee <jeesw@melfas.com>
W: http://www.melfas.com
S: Supported
F: drivers/input/touchscreen/melfas_mip4.c
F: Documentation/devicetree/bindings/input/touchscreen/melfas_mip4.txt
MELLANOX ETHERNET DRIVER (mlx4_en) MELLANOX ETHERNET DRIVER (mlx4_en)
M: Tariq Toukan <tariqt@mellanox.com> M: Tariq Toukan <tariqt@mellanox.com>
L: netdev@vger.kernel.org L: netdev@vger.kernel.org
......
...@@ -103,6 +103,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = { ...@@ -103,6 +103,7 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
6-byte ALPS packet */ 6-byte ALPS packet */
#define ALPS_STICK_BITS 0x100 /* separate stick button bits */ #define ALPS_STICK_BITS 0x100 /* separate stick button bits */
#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */ #define ALPS_BUTTONPAD 0x200 /* device is a clickpad */
#define ALPS_DUALPOINT_WITH_PRESSURE 0x400 /* device can report trackpoint pressure */
static const struct alps_model_info alps_model_data[] = { static const struct alps_model_info alps_model_data[] = {
{ { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */ { { 0x32, 0x02, 0x14 }, 0x00, { ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT } }, /* Toshiba Salellite Pro M10 */
...@@ -1156,15 +1157,28 @@ static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte) ...@@ -1156,15 +1157,28 @@ static unsigned char alps_get_pkt_id_ss4_v2(unsigned char *byte)
{ {
unsigned char pkt_id = SS4_PACKET_ID_IDLE; unsigned char pkt_id = SS4_PACKET_ID_IDLE;
if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 && switch (byte[3] & 0x30) {
(byte[3] & 0x88) == 0x08 && byte[4] == 0x10 && byte[5] == 0x00) { case 0x00:
pkt_id = SS4_PACKET_ID_IDLE; if (byte[0] == 0x18 && byte[1] == 0x10 && byte[2] == 0x00 &&
} else if (!(byte[3] & 0x10)) { (byte[3] & 0x88) == 0x08 && byte[4] == 0x10 &&
pkt_id = SS4_PACKET_ID_ONE; byte[5] == 0x00) {
} else if (!(byte[3] & 0x20)) { pkt_id = SS4_PACKET_ID_IDLE;
} else {
pkt_id = SS4_PACKET_ID_ONE;
}
break;
case 0x10:
/* two-finger finger positions */
pkt_id = SS4_PACKET_ID_TWO; pkt_id = SS4_PACKET_ID_TWO;
} else { break;
case 0x20:
/* stick pointer */
pkt_id = SS4_PACKET_ID_STICK;
break;
case 0x30:
/* third and fourth finger positions */
pkt_id = SS4_PACKET_ID_MULTI; pkt_id = SS4_PACKET_ID_MULTI;
break;
} }
return pkt_id; return pkt_id;
...@@ -1185,7 +1199,13 @@ static int alps_decode_ss4_v2(struct alps_fields *f, ...@@ -1185,7 +1199,13 @@ static int alps_decode_ss4_v2(struct alps_fields *f,
f->mt[0].x = SS4_1F_X_V2(p); f->mt[0].x = SS4_1F_X_V2(p);
f->mt[0].y = SS4_1F_Y_V2(p); f->mt[0].y = SS4_1F_Y_V2(p);
f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f; f->pressure = ((SS4_1F_Z_V2(p)) * 2) & 0x7f;
f->fingers = 1; /*
* When a button is held the device will give us events
* with x, y, and pressure of 0. This causes annoying jumps
* if a touch is released while the button is held.
* Handle this by claiming zero contacts.
*/
f->fingers = f->pressure > 0 ? 1 : 0;
f->first_mp = 0; f->first_mp = 0;
f->is_mp = 0; f->is_mp = 0;
break; break;
...@@ -1246,16 +1266,40 @@ static int alps_decode_ss4_v2(struct alps_fields *f, ...@@ -1246,16 +1266,40 @@ static int alps_decode_ss4_v2(struct alps_fields *f,
} }
break; break;
case SS4_PACKET_ID_STICK:
if (!(priv->flags & ALPS_DUALPOINT)) {
psmouse_warn(psmouse,
"Rejected trackstick packet from non DualPoint device");
} else {
int x = (s8)(((p[0] & 1) << 7) | (p[1] & 0x7f));
int y = (s8)(((p[3] & 1) << 7) | (p[2] & 0x7f));
int pressure = (s8)(p[4] & 0x7f);
input_report_rel(priv->dev2, REL_X, x);
input_report_rel(priv->dev2, REL_Y, -y);
input_report_abs(priv->dev2, ABS_PRESSURE, pressure);
}
break;
case SS4_PACKET_ID_IDLE: case SS4_PACKET_ID_IDLE:
default: default:
memset(f, 0, sizeof(struct alps_fields)); memset(f, 0, sizeof(struct alps_fields));
break; break;
} }
f->left = !!(SS4_BTN_V2(p) & 0x01); /* handle buttons */
if (!(priv->flags & ALPS_BUTTONPAD)) { if (pkt_id == SS4_PACKET_ID_STICK) {
f->right = !!(SS4_BTN_V2(p) & 0x02); f->ts_left = !!(SS4_BTN_V2(p) & 0x01);
f->middle = !!(SS4_BTN_V2(p) & 0x04); if (!(priv->flags & ALPS_BUTTONPAD)) {
f->ts_right = !!(SS4_BTN_V2(p) & 0x02);
f->ts_middle = !!(SS4_BTN_V2(p) & 0x04);
}
} else {
f->left = !!(SS4_BTN_V2(p) & 0x01);
if (!(priv->flags & ALPS_BUTTONPAD)) {
f->right = !!(SS4_BTN_V2(p) & 0x02);
f->middle = !!(SS4_BTN_V2(p) & 0x04);
}
} }
return 0; return 0;
...@@ -1266,6 +1310,7 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) ...@@ -1266,6 +1310,7 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
struct alps_data *priv = psmouse->private; struct alps_data *priv = psmouse->private;
unsigned char *packet = psmouse->packet; unsigned char *packet = psmouse->packet;
struct input_dev *dev = psmouse->dev; struct input_dev *dev = psmouse->dev;
struct input_dev *dev2 = priv->dev2;
struct alps_fields *f = &priv->f; struct alps_fields *f = &priv->f;
memset(f, 0, sizeof(struct alps_fields)); memset(f, 0, sizeof(struct alps_fields));
...@@ -1311,6 +1356,13 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse) ...@@ -1311,6 +1356,13 @@ static void alps_process_packet_ss4_v2(struct psmouse *psmouse)
input_report_abs(dev, ABS_PRESSURE, f->pressure); input_report_abs(dev, ABS_PRESSURE, f->pressure);
input_sync(dev); input_sync(dev);
if (priv->flags & ALPS_DUALPOINT) {
input_report_key(dev2, BTN_LEFT, f->ts_left);
input_report_key(dev2, BTN_RIGHT, f->ts_right);
input_report_key(dev2, BTN_MIDDLE, f->ts_middle);
input_sync(dev2);
}
} }
static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse) static bool alps_is_valid_package_ss4_v2(struct psmouse *psmouse)
...@@ -2695,6 +2747,10 @@ static int alps_set_protocol(struct psmouse *psmouse, ...@@ -2695,6 +2747,10 @@ static int alps_set_protocol(struct psmouse *psmouse,
if (alps_set_defaults_ss4_v2(psmouse, priv)) if (alps_set_defaults_ss4_v2(psmouse, priv))
return -EIO; return -EIO;
if (priv->fw_ver[1] == 0x1)
priv->flags |= ALPS_DUALPOINT |
ALPS_DUALPOINT_WITH_PRESSURE;
break; break;
} }
...@@ -2767,6 +2823,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) ...@@ -2767,6 +2823,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
} else if (e7[0] == 0x73 && e7[1] == 0x03 && } else if (e7[0] == 0x73 && e7[1] == 0x03 &&
e7[2] == 0x14 && ec[1] == 0x02) { e7[2] == 0x14 && ec[1] == 0x02) {
protocol = &alps_v8_protocol_data; protocol = &alps_v8_protocol_data;
} else if (e7[0] == 0x73 && e7[1] == 0x03 &&
e7[2] == 0x28 && ec[1] == 0x01) {
protocol = &alps_v8_protocol_data;
} else { } else {
psmouse_dbg(psmouse, psmouse_dbg(psmouse,
"Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec); "Likely not an ALPS touchpad: E7=%3ph, EC=%3ph\n", e7, ec);
...@@ -2949,6 +3008,10 @@ int alps_init(struct psmouse *psmouse) ...@@ -2949,6 +3008,10 @@ int alps_init(struct psmouse *psmouse)
input_set_capability(dev2, EV_REL, REL_X); input_set_capability(dev2, EV_REL, REL_X);
input_set_capability(dev2, EV_REL, REL_Y); input_set_capability(dev2, EV_REL, REL_Y);
if (priv->flags & ALPS_DUALPOINT_WITH_PRESSURE) {
input_set_capability(dev2, EV_ABS, ABS_PRESSURE);
input_set_abs_params(dev2, ABS_PRESSURE, 0, 127, 0, 0);
}
input_set_capability(dev2, EV_KEY, BTN_LEFT); input_set_capability(dev2, EV_KEY, BTN_LEFT);
input_set_capability(dev2, EV_KEY, BTN_RIGHT); input_set_capability(dev2, EV_KEY, BTN_RIGHT);
input_set_capability(dev2, EV_KEY, BTN_MIDDLE); input_set_capability(dev2, EV_KEY, BTN_MIDDLE);
......
...@@ -37,12 +37,14 @@ ...@@ -37,12 +37,14 @@
* or there's button activities. * or there's button activities.
* SS4_PACKET_ID_TWO: There's two or more fingers on touchpad * SS4_PACKET_ID_TWO: There's two or more fingers on touchpad
* SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad * SS4_PACKET_ID_MULTI: There's three or more fingers on touchpad
* SS4_PACKET_ID_STICK: A stick pointer packet
*/ */
enum SS4_PACKET_ID { enum SS4_PACKET_ID {
SS4_PACKET_ID_IDLE = 0, SS4_PACKET_ID_IDLE = 0,
SS4_PACKET_ID_ONE, SS4_PACKET_ID_ONE,
SS4_PACKET_ID_TWO, SS4_PACKET_ID_TWO,
SS4_PACKET_ID_MULTI, SS4_PACKET_ID_MULTI,
SS4_PACKET_ID_STICK,
}; };
#define SS4_COUNT_PER_ELECTRODE 256 #define SS4_COUNT_PER_ELECTRODE 256
......
...@@ -1134,7 +1134,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse, ...@@ -1134,7 +1134,7 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
* System76 Pangolin 0x250f01 ? 2 hw buttons * System76 Pangolin 0x250f01 ? 2 hw buttons
* (*) + 3 trackpoint buttons * (*) + 3 trackpoint buttons
* (**) + 0 trackpoint buttons * (**) + 0 trackpoint buttons
* Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps
*/ */
static void elantech_set_buttonpad_prop(struct psmouse *psmouse) static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
{ {
...@@ -1159,6 +1159,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = { ...@@ -1159,6 +1159,13 @@ static const struct dmi_system_id elantech_dmi_has_middle_button[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"), DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H730"),
}, },
}, },
{
/* Fujitsu H760 also has a middle button */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
},
},
#endif #endif
{ } { }
}; };
...@@ -1503,10 +1510,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { ...@@ -1503,10 +1510,10 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
}, },
}, },
{ {
/* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */ /* Fujitsu H760 does not work with crc_enabled == 0 */
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"), DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS H760"),
}, },
}, },
{ {
...@@ -1516,6 +1523,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = { ...@@ -1516,6 +1523,20 @@ static const struct dmi_system_id elantech_dmi_force_crc_enabled[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"), DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E544"),
}, },
}, },
{
/* Fujitsu LIFEBOOK E554 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E554"),
},
},
{
/* Fujitsu LIFEBOOK E556 does not work with crc_enabled == 0 */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E556"),
},
},
{ {
/* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */ /* Fujitsu LIFEBOOK U745 does not work with crc_enabled == 0 */
.matches = { .matches = {
......
...@@ -221,6 +221,21 @@ static const struct of_device_id rmi_i2c_of_match[] = { ...@@ -221,6 +221,21 @@ static const struct of_device_id rmi_i2c_of_match[] = {
MODULE_DEVICE_TABLE(of, rmi_i2c_of_match); MODULE_DEVICE_TABLE(of, rmi_i2c_of_match);
#endif #endif
static void rmi_i2c_regulator_bulk_disable(void *data)
{
struct rmi_i2c_xport *rmi_i2c = data;
regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
rmi_i2c->supplies);
}
static void rmi_i2c_unregister_transport(void *data)
{
struct rmi_i2c_xport *rmi_i2c = data;
rmi_unregister_transport_device(&rmi_i2c->xport);
}
static int rmi_i2c_probe(struct i2c_client *client, static int rmi_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id) const struct i2c_device_id *id)
{ {
...@@ -264,6 +279,12 @@ static int rmi_i2c_probe(struct i2c_client *client, ...@@ -264,6 +279,12 @@ static int rmi_i2c_probe(struct i2c_client *client,
if (retval < 0) if (retval < 0)
return retval; return retval;
retval = devm_add_action_or_reset(&client->dev,
rmi_i2c_regulator_bulk_disable,
rmi_i2c);
if (retval)
return retval;
of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms", of_property_read_u32(client->dev.of_node, "syna,startup-delay-ms",
&rmi_i2c->startup_delay); &rmi_i2c->startup_delay);
...@@ -294,6 +315,11 @@ static int rmi_i2c_probe(struct i2c_client *client, ...@@ -294,6 +315,11 @@ static int rmi_i2c_probe(struct i2c_client *client,
client->addr); client->addr);
return retval; return retval;
} }
retval = devm_add_action_or_reset(&client->dev,
rmi_i2c_unregister_transport,
rmi_i2c);
if (retval)
return retval;
retval = rmi_i2c_init_irq(client); retval = rmi_i2c_init_irq(client);
if (retval < 0) if (retval < 0)
...@@ -304,17 +330,6 @@ static int rmi_i2c_probe(struct i2c_client *client, ...@@ -304,17 +330,6 @@ static int rmi_i2c_probe(struct i2c_client *client,
return 0; return 0;
} }
static int rmi_i2c_remove(struct i2c_client *client)
{
struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
rmi_unregister_transport_device(&rmi_i2c->xport);
regulator_bulk_disable(ARRAY_SIZE(rmi_i2c->supplies),
rmi_i2c->supplies);
return 0;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int rmi_i2c_suspend(struct device *dev) static int rmi_i2c_suspend(struct device *dev)
{ {
...@@ -431,7 +446,6 @@ static struct i2c_driver rmi_i2c_driver = { ...@@ -431,7 +446,6 @@ static struct i2c_driver rmi_i2c_driver = {
}, },
.id_table = rmi_id, .id_table = rmi_id,
.probe = rmi_i2c_probe, .probe = rmi_i2c_probe,
.remove = rmi_i2c_remove,
}; };
module_i2c_driver(rmi_i2c_driver); module_i2c_driver(rmi_i2c_driver);
......
...@@ -396,6 +396,13 @@ static inline int rmi_spi_of_probe(struct spi_device *spi, ...@@ -396,6 +396,13 @@ static inline int rmi_spi_of_probe(struct spi_device *spi,
} }
#endif #endif
static void rmi_spi_unregister_transport(void *data)
{
struct rmi_spi_xport *rmi_spi = data;
rmi_unregister_transport_device(&rmi_spi->xport);
}
static int rmi_spi_probe(struct spi_device *spi) static int rmi_spi_probe(struct spi_device *spi)
{ {
struct rmi_spi_xport *rmi_spi; struct rmi_spi_xport *rmi_spi;
...@@ -464,6 +471,11 @@ static int rmi_spi_probe(struct spi_device *spi) ...@@ -464,6 +471,11 @@ static int rmi_spi_probe(struct spi_device *spi)
dev_err(&spi->dev, "failed to register transport.\n"); dev_err(&spi->dev, "failed to register transport.\n");
return retval; return retval;
} }
retval = devm_add_action_or_reset(&spi->dev,
rmi_spi_unregister_transport,
rmi_spi);
if (retval)
return retval;
retval = rmi_spi_init_irq(spi); retval = rmi_spi_init_irq(spi);
if (retval < 0) if (retval < 0)
...@@ -473,15 +485,6 @@ static int rmi_spi_probe(struct spi_device *spi) ...@@ -473,15 +485,6 @@ static int rmi_spi_probe(struct spi_device *spi)
return 0; return 0;
} }
static int rmi_spi_remove(struct spi_device *spi)
{
struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi);
rmi_unregister_transport_device(&rmi_spi->xport);
return 0;
}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static int rmi_spi_suspend(struct device *dev) static int rmi_spi_suspend(struct device *dev)
{ {
...@@ -577,7 +580,6 @@ static struct spi_driver rmi_spi_driver = { ...@@ -577,7 +580,6 @@ static struct spi_driver rmi_spi_driver = {
}, },
.id_table = rmi_id, .id_table = rmi_id,
.probe = rmi_spi_probe, .probe = rmi_spi_probe,
.remove = rmi_spi_remove,
}; };
module_spi_driver(rmi_spi_driver); module_spi_driver(rmi_spi_driver);
......
...@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void) ...@@ -81,7 +81,7 @@ static inline int i8042_platform_init(void)
return -EBUSY; return -EBUSY;
#endif #endif
i8042_reset = 1; i8042_reset = I8042_RESET_ALWAYS;
return 0; return 0;
} }
......
...@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) ...@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
return -EBUSY; return -EBUSY;
#endif #endif
i8042_reset = 1; i8042_reset = I8042_RESET_ALWAYS;
return 0; return 0;
} }
......
...@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val) ...@@ -44,7 +44,7 @@ static inline void i8042_write_command(int val)
static inline int i8042_platform_init(void) static inline int i8042_platform_init(void)
{ {
i8042_reset = 1; i8042_reset = I8042_RESET_ALWAYS;
return 0; return 0;
} }
......
...@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void) ...@@ -130,7 +130,7 @@ static int __init i8042_platform_init(void)
} }
} }
i8042_reset = 1; i8042_reset = I8042_RESET_ALWAYS;
return 0; return 0;
} }
......
...@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void) ...@@ -61,7 +61,7 @@ static inline int i8042_platform_init(void)
if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042")) if (!request_mem_region(I8042_REGION_START, I8042_REGION_SIZE, "i8042"))
return -EBUSY; return -EBUSY;
i8042_reset = 1; i8042_reset = I8042_RESET_ALWAYS;
return 0; return 0;
} }
......
...@@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = { ...@@ -510,6 +510,90 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
{ } { }
}; };
/*
* On some Asus laptops, just running self tests cause problems.
*/
static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "A455LD"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "K401LB"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "K501LB"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "K501LX"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "R409L"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "V502LX"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X302LA"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X450LD"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X455LAB"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X455LDB"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "X455LF"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "Z450LA"),
},
},
{ }
};
static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = { static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
{ {
/* MSI Wind U-100 */ /* MSI Wind U-100 */
...@@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void) ...@@ -1072,12 +1156,18 @@ static int __init i8042_platform_init(void)
return retval; return retval;
#if defined(__ia64__) #if defined(__ia64__)
i8042_reset = true; i8042_reset = I8042_RESET_ALWAYS;
#endif #endif
#ifdef CONFIG_X86 #ifdef CONFIG_X86
if (dmi_check_system(i8042_dmi_reset_table)) /* Honor module parameter when value is not default */
i8042_reset = true; if (i8042_reset == I8042_RESET_DEFAULT) {
if (dmi_check_system(i8042_dmi_reset_table))
i8042_reset = I8042_RESET_ALWAYS;
if (dmi_check_system(i8042_dmi_noselftest_table))
i8042_reset = I8042_RESET_NEVER;
}
if (dmi_check_system(i8042_dmi_noloop_table)) if (dmi_check_system(i8042_dmi_noloop_table))
i8042_noloop = true; i8042_noloop = true;
......
...@@ -48,9 +48,39 @@ static bool i8042_unlock; ...@@ -48,9 +48,39 @@ static bool i8042_unlock;
module_param_named(unlock, i8042_unlock, bool, 0); module_param_named(unlock, i8042_unlock, bool, 0);
MODULE_PARM_DESC(unlock, "Ignore keyboard lock."); MODULE_PARM_DESC(unlock, "Ignore keyboard lock.");
static bool i8042_reset; enum i8042_controller_reset_mode {
module_param_named(reset, i8042_reset, bool, 0); I8042_RESET_NEVER,
MODULE_PARM_DESC(reset, "Reset controller during init and cleanup."); I8042_RESET_ALWAYS,
I8042_RESET_ON_S2RAM,
#define I8042_RESET_DEFAULT I8042_RESET_ON_S2RAM
};
static enum i8042_controller_reset_mode i8042_reset = I8042_RESET_DEFAULT;
static int i8042_set_reset(const char *val, const struct kernel_param *kp)
{
enum i8042_controller_reset_mode *arg = kp->arg;
int error;
bool reset;
if (val) {
error = kstrtobool(val, &reset);
if (error)
return error;
} else {
reset = true;
}
*arg = reset ? I8042_RESET_ALWAYS : I8042_RESET_NEVER;
return 0;
}
static const struct kernel_param_ops param_ops_reset_param = {
.flags = KERNEL_PARAM_OPS_FL_NOARG,
.set = i8042_set_reset,
};
#define param_check_reset_param(name, p) \
__param_check(name, p, enum i8042_controller_reset_mode)
module_param_named(reset, i8042_reset, reset_param, 0);
MODULE_PARM_DESC(reset, "Reset controller on resume, cleanup or both");
static bool i8042_direct; static bool i8042_direct;
module_param_named(direct, i8042_direct, bool, 0); module_param_named(direct, i8042_direct, bool, 0);
...@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void) ...@@ -1019,7 +1049,7 @@ static int i8042_controller_init(void)
* Reset the controller and reset CRT to the original value set by BIOS. * Reset the controller and reset CRT to the original value set by BIOS.
*/ */
static void i8042_controller_reset(bool force_reset) static void i8042_controller_reset(bool s2r_wants_reset)
{ {
i8042_flush(); i8042_flush();
...@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset) ...@@ -1044,8 +1074,10 @@ static void i8042_controller_reset(bool force_reset)
* Reset the controller if requested. * Reset the controller if requested.
*/ */
if (i8042_reset || force_reset) if (i8042_reset == I8042_RESET_ALWAYS ||
(i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
i8042_controller_selftest(); i8042_controller_selftest();
}
/* /*
* Restore the original control register setting. * Restore the original control register setting.
...@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void) ...@@ -1110,7 +1142,7 @@ static void i8042_dritek_enable(void)
* before suspending. * before suspending.
*/ */
static int i8042_controller_resume(bool force_reset) static int i8042_controller_resume(bool s2r_wants_reset)
{ {
int error; int error;
...@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset) ...@@ -1118,7 +1150,8 @@ static int i8042_controller_resume(bool force_reset)
if (error) if (error)
return error; return error;
if (i8042_reset || force_reset) { if (i8042_reset == I8042_RESET_ALWAYS ||
(i8042_reset == I8042_RESET_ON_S2RAM && s2r_wants_reset)) {
error = i8042_controller_selftest(); error = i8042_controller_selftest();
if (error) if (error)
return error; return error;
...@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev) ...@@ -1195,7 +1228,7 @@ static int i8042_pm_resume_noirq(struct device *dev)
static int i8042_pm_resume(struct device *dev) static int i8042_pm_resume(struct device *dev)
{ {
bool force_reset; bool want_reset;
int i; int i;
for (i = 0; i < I8042_NUM_PORTS; i++) { for (i = 0; i < I8042_NUM_PORTS; i++) {
...@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev) ...@@ -1218,9 +1251,9 @@ static int i8042_pm_resume(struct device *dev)
* off control to the platform firmware, otherwise we can simply restore * off control to the platform firmware, otherwise we can simply restore
* the mode. * the mode.
*/ */
force_reset = pm_resume_via_firmware(); want_reset = pm_resume_via_firmware();
return i8042_controller_resume(force_reset); return i8042_controller_resume(want_reset);
} }
static int i8042_pm_thaw(struct device *dev) static int i8042_pm_thaw(struct device *dev)
...@@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev) ...@@ -1482,7 +1515,7 @@ static int __init i8042_probe(struct platform_device *dev)
i8042_platform_device = dev; i8042_platform_device = dev;
if (i8042_reset) { if (i8042_reset == I8042_RESET_ALWAYS) {
error = i8042_controller_selftest(); error = i8042_controller_selftest();
if (error) if (error)
return error; return error;
......
...@@ -157,6 +157,7 @@ struct mip4_ts { ...@@ -157,6 +157,7 @@ struct mip4_ts {
char phys[32]; char phys[32];
char product_name[16]; char product_name[16];
char ic_name[4];
unsigned int max_x; unsigned int max_x;
unsigned int max_y; unsigned int max_y;
...@@ -263,6 +264,18 @@ static int mip4_query_device(struct mip4_ts *ts) ...@@ -263,6 +264,18 @@ static int mip4_query_device(struct mip4_ts *ts)
dev_dbg(&ts->client->dev, "product name: %.*s\n", dev_dbg(&ts->client->dev, "product name: %.*s\n",
(int)sizeof(ts->product_name), ts->product_name); (int)sizeof(ts->product_name), ts->product_name);
/* IC name */
cmd[0] = MIP4_R0_INFO;
cmd[1] = MIP4_R1_INFO_IC_NAME;
error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
ts->ic_name, sizeof(ts->ic_name));
if (error)
dev_warn(&ts->client->dev,
"Failed to retrieve IC name: %d\n", error);
else
dev_dbg(&ts->client->dev, "IC name: %.*s\n",
(int)sizeof(ts->ic_name), ts->ic_name);
/* Firmware version */ /* Firmware version */
error = mip4_get_fw_version(ts); error = mip4_get_fw_version(ts);
if (error) if (error)
...@@ -1326,7 +1339,7 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, ...@@ -1326,7 +1339,7 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev,
* paired with current firmware in the chip. * paired with current firmware in the chip.
*/ */
count = snprintf(buf, PAGE_SIZE, "%.*s\n", count = snprintf(buf, PAGE_SIZE, "%.*s\n",
(int)sizeof(ts->product_name), ts->product_name); (int)sizeof(ts->product_name), ts->product_name);
mutex_unlock(&ts->input->mutex); mutex_unlock(&ts->input->mutex);
...@@ -1335,9 +1348,30 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev, ...@@ -1335,9 +1348,30 @@ static ssize_t mip4_sysfs_read_hw_version(struct device *dev,
static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL); static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL);
static ssize_t mip4_sysfs_read_ic_name(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct mip4_ts *ts = i2c_get_clientdata(client);
size_t count;
mutex_lock(&ts->input->mutex);
count = snprintf(buf, PAGE_SIZE, "%.*s\n",
(int)sizeof(ts->ic_name), ts->ic_name);
mutex_unlock(&ts->input->mutex);
return count;
}
static DEVICE_ATTR(ic_name, S_IRUGO, mip4_sysfs_read_ic_name, NULL);
static struct attribute *mip4_attrs[] = { static struct attribute *mip4_attrs[] = {
&dev_attr_fw_version.attr, &dev_attr_fw_version.attr,
&dev_attr_hw_version.attr, &dev_attr_hw_version.attr,
&dev_attr_ic_name.attr,
&dev_attr_update_fw.attr, &dev_attr_update_fw.attr,
NULL, NULL,
}; };
...@@ -1538,6 +1572,6 @@ static struct i2c_driver mip4_driver = { ...@@ -1538,6 +1572,6 @@ static struct i2c_driver mip4_driver = {
module_i2c_driver(mip4_driver); module_i2c_driver(mip4_driver);
MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen");
MODULE_VERSION("2016.03.12"); MODULE_VERSION("2016.09.28");
MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>"); MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment