@@ -304,7 +304,8 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse)
static unsigned char param = 0xc8;
struct synaptics_data *priv = psmouse->private;
- if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c))
+ if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) ||
+ SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)))
return 0;
if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL))
@@ -463,7 +464,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[],
hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0;
}
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) {
+ if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)
+ || SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))
+ && hw->w == 2) {
synaptics_parse_agm(buf, priv);
return 1;
}
@@ -543,6 +546,87 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev,
}
}
+static void synaptics_report_slot_empty(struct input_dev *dev, int slot)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, false);
+}
+
+static void synaptics_report_slot_sgm(struct input_dev *dev, int slot,
+ const struct synaptics_hw_state *sgm)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, sgm->x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, invert_y(sgm->y));
+ input_report_abs(dev, ABS_MT_PRESSURE, sgm->z);
+ /* SGM can sometimes contain valid width */
+ if (sgm->w >= 4)
+ input_report_abs(dev, ABS_MT_TOUCH_MAJOR, sgm->w);
+}
+
+static void synaptics_report_slot_agm(struct input_dev *dev, int slot,
+ const struct synaptics_hw_state *agm)
+{
+ input_mt_slot(dev, slot);
+ input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
+ input_report_abs(dev, ABS_MT_POSITION_X, agm->x);
+ input_report_abs(dev, ABS_MT_POSITION_Y, invert_y(agm->y));
+ input_report_abs(dev, ABS_MT_PRESSURE, agm->z);
+}
+
+static void synaptics_report_mt(struct psmouse *psmouse,
+ int count,
+ const struct synaptics_hw_state *sgm)
+{
+ struct input_dev *dev = psmouse->dev;
+ struct synaptics_data *priv = psmouse->private;
+ struct synaptics_hw_state *agm = &priv->agm;
+
+ switch (count) {
+ case 0:
+ synaptics_report_slot_empty(dev, 0);
+ synaptics_report_slot_empty(dev, 1);
+ break;
+ case 1:
+ synaptics_report_slot_sgm(dev, 0, sgm);
+ synaptics_report_slot_empty(dev, 1);
+ break;
+ case 2:
+ case 3: /* Fall-through case */
+ synaptics_report_slot_sgm(dev, 0, sgm);
+ synaptics_report_slot_agm(dev, 1, agm);
+ break;
+ }
+
+ /* Don't use active slot count to generate BTN_TOOL events. */
+ input_mt_report_pointer_emulation(dev, false);
+
+ /* Send the number of fingers reported by touchpad itself. */
+ input_mt_report_finger_count(dev, count);
+
+ input_report_key(dev, BTN_LEFT, sgm->left);
+ input_sync(dev);
+}
+
+static void synaptics_image_sensor_process(struct psmouse *psmouse,
+ struct synaptics_hw_state *sgm)
+{
+ int count;
+
+ if (sgm->z == 0)
+ count = 0;
+ else if (sgm->w >= 4)
+ count = 1;
+ else if (sgm->w == 0)
+ count = 2;
+ else
+ count = 3;
+
+ /* Send resulting input events to user space */
+ synaptics_report_mt(psmouse, count, sgm);
+}
+
/*
* called for each full received packet from the touchpad
*/
@@ -558,6 +642,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
if (synaptics_parse_hw_state(psmouse->packet, priv, &hw))
return;
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ synaptics_image_sensor_process(psmouse, &hw);
+ return;
+ }
+
if (hw.scroll) {
priv->scroll += hw.scroll;
@@ -739,7 +828,16 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
set_abs_position_params(dev, priv, ABS_X, ABS_Y);
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
- if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
+ input_mt_init_slots(dev, 2);
+ set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
+ ABS_MT_POSITION_Y);
+ /* Image sensors can report per-contact pressure */
+ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
+ /* Image sensors can sometimes report per-contact width */
+ input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 4, 15, 0, 0);
+ } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
+ /* Non-image sensors with AGM use semi-mt */
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
input_mt_init_slots(dev, 2);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
@@ -74,6 +74,8 @@
* 2 0x04 reduced filtering firmware does less filtering on
* position data, driver should watch
* for noise.
+ * 2 0x08 image sensor image sensor tracks 5 fingers, but only
+ * reports 2.
* 2 0x20 report min query 0x0f gives min coord reported
*/
#define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */
@@ -82,6 +84,7 @@
#define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x000200)
#define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000)
#define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400)
+#define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800)
/* synaptics modes query bits */
#define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7))