Commit e08d9afa authored by Henrik Rydberg's avatar Henrik Rydberg Committed by Dmitry Torokhov

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: default avatarHenrik Rydberg <rydberg@euromail.se>
Reviewed-by: default avatarBenson Leung <bleung@chromium.org>
Signed-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent ae84197f
...@@ -117,6 +117,9 @@ void synaptics_reset(struct psmouse *psmouse) ...@@ -117,6 +117,9 @@ void synaptics_reset(struct psmouse *psmouse)
} }
#ifdef CONFIG_MOUSE_PS2_SYNAPTICS #ifdef CONFIG_MOUSE_PS2_SYNAPTICS
static bool cr48_profile_sensor;
struct min_max_quirk { struct min_max_quirk {
const char * const *pnp_ids; const char * const *pnp_ids;
int x_min, x_max, y_min, y_max; int x_min, x_max, y_min, y_max;
...@@ -1151,6 +1154,42 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse, ...@@ -1151,6 +1154,42 @@ static void synaptics_image_sensor_process(struct psmouse *psmouse,
priv->agm_pending = false; priv->agm_pending = false;
} }
static void synaptics_profile_sensor_process(struct psmouse *psmouse,
struct synaptics_hw_state *sgm,
int num_fingers)
{
struct input_dev *dev = psmouse->dev;
struct synaptics_data *priv = psmouse->private;
struct synaptics_hw_state *hw[2] = { sgm, &priv->agm };
struct input_mt_pos pos[2];
int slot[2], nsemi, i;
nsemi = clamp_val(num_fingers, 0, 2);
for (i = 0; i < nsemi; i++) {
pos[i].x = hw[i]->x;
pos[i].y = synaptics_invert_y(hw[i]->y);
}
input_mt_assign_slots(dev, slot, pos, nsemi);
for (i = 0; i < nsemi; i++) {
input_mt_slot(dev, slot[i]);
input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
input_report_abs(dev, ABS_MT_POSITION_X, pos[i].x);
input_report_abs(dev, ABS_MT_POSITION_Y, pos[i].y);
input_report_abs(dev, ABS_MT_PRESSURE, hw[i]->z);
}
input_mt_drop_unused(dev);
input_mt_report_pointer_emulation(dev, false);
input_mt_report_finger_count(dev, num_fingers);
synaptics_report_buttons(psmouse, sgm);
input_sync(dev);
}
/* /*
* called for each full received packet from the touchpad * called for each full received packet from the touchpad
*/ */
...@@ -1214,6 +1253,11 @@ static void synaptics_process_packet(struct psmouse *psmouse) ...@@ -1214,6 +1253,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
finger_width = 0; finger_width = 0;
} }
if (cr48_profile_sensor) {
synaptics_profile_sensor_process(psmouse, &hw, num_fingers);
return;
}
if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
synaptics_report_semi_mt_data(dev, &hw, &priv->agm, synaptics_report_semi_mt_data(dev, &hw, &priv->agm,
num_fingers); num_fingers);
...@@ -1359,6 +1403,9 @@ static void set_input_params(struct psmouse *psmouse, ...@@ -1359,6 +1403,9 @@ static void set_input_params(struct psmouse *psmouse,
set_abs_position_params(dev, priv, ABS_X, ABS_Y); set_abs_position_params(dev, priv, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (cr48_profile_sensor)
input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
set_abs_position_params(dev, priv, ABS_MT_POSITION_X, set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y); ABS_MT_POSITION_Y);
...@@ -1372,9 +1419,14 @@ static void set_input_params(struct psmouse *psmouse, ...@@ -1372,9 +1419,14 @@ static void set_input_params(struct psmouse *psmouse,
} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
set_abs_position_params(dev, priv, ABS_MT_POSITION_X, set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y); ABS_MT_POSITION_Y);
/* Non-image sensors with AGM use semi-mt */ /*
* Profile sensor in CR-48 tracks contacts reasonably well,
* other non-image sensors with AGM use semi-mt.
*/
input_mt_init_slots(dev, 2, input_mt_init_slots(dev, 2,
INPUT_MT_POINTER | INPUT_MT_SEMI_MT); INPUT_MT_POINTER |
(cr48_profile_sensor ?
INPUT_MT_TRACK : INPUT_MT_SEMI_MT));
} }
if (SYN_CAP_PALMDETECT(priv->capabilities)) if (SYN_CAP_PALMDETECT(priv->capabilities))
...@@ -1576,10 +1628,24 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = { ...@@ -1576,10 +1628,24 @@ static const struct dmi_system_id olpc_dmi_table[] __initconst = {
{ } { }
}; };
static const struct dmi_system_id __initconst cr48_dmi_table[] = {
#if defined(CONFIG_DMI) && defined(CONFIG_X86)
{
/* Cr-48 Chromebook (Codename Mario) */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "IEC"),
DMI_MATCH(DMI_PRODUCT_NAME, "Mario"),
},
},
#endif
{ }
};
void __init synaptics_module_init(void) void __init synaptics_module_init(void)
{ {
impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table);
broken_olpc_ec = dmi_check_system(olpc_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table);
cr48_profile_sensor = dmi_check_system(cr48_dmi_table);
} }
static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
......
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