diff options
author | Henrik Rydberg <rydberg@euromail.se> | 2014-07-14 13:26:56 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2014-08-08 12:17:01 -0400 |
commit | e08d9afa9345f9a0e13cfff1d116b3a9b10d9dcb (patch) | |
tree | c8d00350261b69e444bf5ff17e1da247b045b19c /drivers/input/mouse/synaptics.c | |
parent | ae84197f8a5e29d9b949fb515493d05bf7723f5c (diff) |
Input: synaptics - use firmware data for Cr-48
The profile sensor clickpad in a Cr-48 Chromebook does a reasonable job
of tracking individual fingers. This tracking isn't perfect, but,
experiments show that it works better than just passing "semi-mt" data
to userspace, and making userspace try to deduce where the fingers are
given a bounding box.
This patch tries to report correct two-finger positions instead of the
{(min_x, min_y), (max_x, max_y)} for profile sensor clickpads on Cr-48
chromebooks. Note that this device's firmware always reports the higher
(smaller y) finger in the "sgm" packet, and the lower (larger y) finger
in the "agm" packet. Thus, when a new finger arrives on the pad, the
kernel driver uses input core's contact tracking facilities to match
contacts with slots.
Inspired by patch by Daniel Kurtz <djkurtz@chromium.org> and Chung-yih
Wang <cywang@chromium.org>
Signed-off-by: Henrik Rydberg <rydberg@euromail.se>
Reviewed-by: Benson Leung <bleung@chromium.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/mouse/synaptics.c')
-rw-r--r-- | drivers/input/mouse/synaptics.c | 70 |
1 files changed, 68 insertions, 2 deletions
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index f0142f215475..35ade3b80389 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
@@ -117,6 +117,9 @@ void synaptics_reset(struct psmouse *psmouse) | |||
117 | } | 117 | } |
118 | 118 | ||
119 | #ifdef CONFIG_MOUSE_PS2_SYNAPTICS | 119 | #ifdef CONFIG_MOUSE_PS2_SYNAPTICS |
120 | |||
121 | static bool cr48_profile_sensor; | ||
122 | |||
120 | struct min_max_quirk { | 123 | struct min_max_quirk { |
121 | const char * const *pnp_ids; | 124 | const char * const *pnp_ids; |
122 | int x_min, x_max, y_min, y_max; | 125 | int x_min, x_max, y_min, y_max; |
@@ -1151,6 +1154,42 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse, | |||
1151 | priv->agm_pending = false; | 1154 | priv->agm_pending = false; |
1152 | } | 1155 | } |
1153 | 1156 | ||
1157 | static void synaptics_profile_sensor_process(struct psmouse *psmouse, | ||
1158 | struct synaptics_hw_state *sgm, | ||
1159 | int num_fingers) | ||
1160 | { | ||
1161 | struct input_dev *dev = psmouse->dev; | ||
1162 | struct synaptics_data *priv = psmouse->private; | ||
1163 | struct synaptics_hw_state *hw[2] = { sgm, &priv->agm }; | ||
1164 | struct input_mt_pos pos[2]; | ||
1165 | int slot[2], nsemi, i; | ||
1166 | |||
1167 | nsemi = clamp_val(num_fingers, 0, 2); | ||
1168 | |||
1169 | for (i = 0; i < nsemi; i++) { | ||
1170 | pos[i].x = hw[i]->x; | ||
1171 | pos[i].y = synaptics_invert_y(hw[i]->y); | ||
1172 | } | ||
1173 | |||
1174 | input_mt_assign_slots(dev, slot, pos, nsemi); | ||
1175 | |||
1176 | for (i = 0; i < nsemi; i++) { | ||
1177 | input_mt_slot(dev, slot[i]); | ||
1178 | input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); | ||
1179 | input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x); | ||
1180 | input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y); | ||
1181 | input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z); | ||
1182 | } | ||
1183 | |||
1184 | input_mt_drop_unused(dev); | ||
1185 | input_mt_report_pointer_emulation(dev, false); | ||
1186 | input_mt_report_finger_count(dev, num_fingers); | ||
1187 | |||
1188 | synaptics_report_buttons(psmouse, sgm); | ||
1189 | |||
1190 | input_sync(dev); | ||
1191 | } | ||
1192 | |||
1154 | /* | 1193 | /* |
1155 | * called for each full received packet from the touchpad | 1194 | * called for each full received packet from the touchpad |
1156 | */ | 1195 | */ |
@@ -1214,6 +1253,11 @@ static void synaptics_process_packet(struct psmouse *psmouse) | |||
1214 | finger_width = 0; | 1253 | finger_width = 0; |
1215 | } | 1254 | } |
1216 | 1255 | ||
1256 | if (cr48_profile_sensor) { | ||
1257 | synaptics_profile_sensor_process(psmouse, &hw, num_fingers); | ||
1258 | return; | ||
1259 | } | ||
1260 | |||
1217 | if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) | 1261 | if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) |
1218 | synaptics_report_semi_mt_data(dev, &hw, &priv->agm, | 1262 | synaptics_report_semi_mt_data(dev, &hw, &priv->agm, |
1219 | num_fingers); | 1263 | num_fingers); |
@@ -1359,6 +1403,9 @@ static void set_input_params(struct psmouse *psmouse, | |||
1359 | set_abs_position_params(dev, priv, ABS_X, ABS_Y); | 1403 | set_abs_position_params(dev, priv, ABS_X, ABS_Y); |
1360 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); | 1404 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); |
1361 | 1405 | ||
1406 | if (cr48_profile_sensor) | ||
1407 | input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); | ||
1408 | |||
1362 | if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { | 1409 | if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { |
1363 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, | 1410 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, |
1364 | ABS_MT_POSITION_Y); | 1411 | ABS_MT_POSITION_Y); |
@@ -1372,9 +1419,14 @@ static void set_input_params(struct psmouse *psmouse, | |||
1372 | } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { | 1419 | } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { |
1373 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, | 1420 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, |
1374 | ABS_MT_POSITION_Y); | 1421 | ABS_MT_POSITION_Y); |
1375 | /* Non-image sensors with AGM use semi-mt */ | 1422 | /* |
1423 | * Profile sensor in CR-48 tracks contacts reasonably well, | ||
1424 | * other non-image sensors with AGM use semi-mt. | ||
1425 | */ | ||
1376 | input_mt_init_slots(dev, 2, | 1426 | input_mt_init_slots(dev, 2, |
1377 | INPUT_MT_POINTER | INPUT_MT_SEMI_MT); | 1427 | INPUT_MT_POINTER | |
1428 | (cr48_profile_sensor ? | ||
1429 | INPUT_MT_TRACK : INPUT_MT_SEMI_MT)); | ||
1378 | } | 1430 | } |
1379 | 1431 | ||
1380 | if (SYN_CAP_PALMDETECT(priv->capabilities)) | 1432 | if (SYN_CAP_PALMDETECT(priv->capabilities)) |
@@ -1576,10 +1628,24 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = { | |||
1576 | { } | 1628 | { } |
1577 | }; | 1629 | }; |
1578 | 1630 | ||
1631 | static const struct dmi_system_id __initconst cr48_dmi_table[] = { | ||
1632 | #if defined(CONFIG_DMI) && defined(CONFIG_X86) | ||
1633 | { | ||
1634 | /* Cr-48 Chromebook (Codename Mario) */ | ||
1635 | .matches = { | ||
1636 | DMI_MATCH(DMI_SYS_VENDOR, "IEC"), | ||
1637 | DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), | ||
1638 | }, | ||
1639 | }, | ||
1640 | #endif | ||
1641 | { } | ||
1642 | }; | ||
1643 | |||
1579 | void __init synaptics_module_init(void) | 1644 | void __init synaptics_module_init(void) |
1580 | { | 1645 | { |
1581 | impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); | 1646 | impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); |
1582 | broken_olpc_ec = dmi_check_system(olpc_dmi_table); | 1647 | broken_olpc_ec = dmi_check_system(olpc_dmi_table); |
1648 | cr48_profile_sensor = dmi_check_system(cr48_dmi_table); | ||
1583 | } | 1649 | } |
1584 | 1650 | ||
1585 | static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) | 1651 | static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) |