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 | |
| 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>
| -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) |
