diff options
author | Daniel Kurtz <djkurtz@chromium.org> | 2011-08-24 02:02:25 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-08-24 02:08:12 -0400 |
commit | 3cdfee9ea7e98eb6e8c3d4d66f2dfcfffe6afc4d (patch) | |
tree | a545a98c243d9eb4d9254651587ddfade4fb32f5 /drivers/input/mouse | |
parent | 85615476e2039d2567075d7949a30f0c204f8103 (diff) |
Input: synaptics - add image sensor support
Synaptics makes (at least) two kinds of touchpad sensors:
* Older pads use a profile sensor that could only infer the location
of individual fingers based on the projection of their profiles
onto row and column sensors.
* Newer pads use an image sensor that can track true finger position
using a two-dimensional sensor grid.
Both sensor types support an "Advanced Gesture Mode":
When multiple fingers are detected, the touchpad sends alternating
"Advanced Gesture Mode" (AGM) and "Simple Gesture Mode" (SGM)
packets.
The AGM packets have w=2, and contain reduced resolution finger data
The SGM packets have w={0,1} and contain full resolution finger data
Profile sensors try to report the "upper" (larger y value) finger in
the SGM packet, and the lower (smaller y value) in the AGM packet.
However, due to the nature of the profile sensor, they easily get
confused when fingers cross, and can start reporting the x-coordinate
of one with the y-coordinate of the other. Thus, for profile
sensors, "semi-mt" was created, which reports a "bounding box"
created by pairing min and max coordinates of the two pairs of
reported fingers.
Image sensors can report the actual coordinates of two of the fingers
present. This patch detects if the touchpad is an image sensor and
reports finger data using the MT-B protocol.
NOTE: This patch only adds partial support for 2-finger gestures.
The proper interpretation of the slot contents when more than
two fingers are present is left to later patches. Also,
handling of 'number of fingers' transitions is incomplete.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Acked-by: Chase Douglas <chase.douglas@canonical.com>
Acked-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/mouse')
-rw-r--r-- | drivers/input/mouse/synaptics.c | 124 | ||||
-rw-r--r-- | drivers/input/mouse/synaptics.h | 3 |
2 files changed, 110 insertions, 17 deletions
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 8e9360ab4ec4..e6e59c59391d 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c | |||
@@ -304,7 +304,8 @@ static int synaptics_set_advanced_gesture_mode(struct psmouse *psmouse) | |||
304 | static unsigned char param = 0xc8; | 304 | static unsigned char param = 0xc8; |
305 | struct synaptics_data *priv = psmouse->private; | 305 | struct synaptics_data *priv = psmouse->private; |
306 | 306 | ||
307 | if (!SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) | 307 | if (!(SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || |
308 | SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c))) | ||
308 | return 0; | 309 | return 0; |
309 | 310 | ||
310 | if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) | 311 | if (psmouse_sliced_command(psmouse, SYN_QUE_MODEL)) |
@@ -463,7 +464,9 @@ static int synaptics_parse_hw_state(const unsigned char buf[], | |||
463 | hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; | 464 | hw->down = ((buf[0] ^ buf[3]) & 0x02) ? 1 : 0; |
464 | } | 465 | } |
465 | 466 | ||
466 | if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) && hw->w == 2) { | 467 | if ((SYN_CAP_ADV_GESTURE(priv->ext_cap_0c) || |
468 | SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) && | ||
469 | hw->w == 2) { | ||
467 | synaptics_parse_agm(buf, priv); | 470 | synaptics_parse_agm(buf, priv); |
468 | return 1; | 471 | return 1; |
469 | } | 472 | } |
@@ -543,6 +546,94 @@ static void synaptics_report_semi_mt_data(struct input_dev *dev, | |||
543 | } | 546 | } |
544 | } | 547 | } |
545 | 548 | ||
549 | static void synaptics_report_buttons(struct psmouse *psmouse, | ||
550 | const struct synaptics_hw_state *hw) | ||
551 | { | ||
552 | struct input_dev *dev = psmouse->dev; | ||
553 | struct synaptics_data *priv = psmouse->private; | ||
554 | int i; | ||
555 | |||
556 | input_report_key(dev, BTN_LEFT, hw->left); | ||
557 | input_report_key(dev, BTN_RIGHT, hw->right); | ||
558 | |||
559 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | ||
560 | input_report_key(dev, BTN_MIDDLE, hw->middle); | ||
561 | |||
562 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | ||
563 | input_report_key(dev, BTN_FORWARD, hw->up); | ||
564 | input_report_key(dev, BTN_BACK, hw->down); | ||
565 | } | ||
566 | |||
567 | for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | ||
568 | input_report_key(dev, BTN_0 + i, hw->ext_buttons & (1 << i)); | ||
569 | } | ||
570 | |||
571 | static void synaptics_report_slot(struct input_dev *dev, int slot, | ||
572 | const struct synaptics_hw_state *hw) | ||
573 | { | ||
574 | input_mt_slot(dev, slot); | ||
575 | input_mt_report_slot_state(dev, MT_TOOL_FINGER, (hw != NULL)); | ||
576 | if (!hw) | ||
577 | return; | ||
578 | |||
579 | input_report_abs(dev, ABS_MT_POSITION_X, hw->x); | ||
580 | input_report_abs(dev, ABS_MT_POSITION_Y, synaptics_invert_y(hw->y)); | ||
581 | input_report_abs(dev, ABS_MT_PRESSURE, hw->z); | ||
582 | } | ||
583 | |||
584 | static void synaptics_report_mt_data(struct psmouse *psmouse, | ||
585 | int count, | ||
586 | const struct synaptics_hw_state *sgm) | ||
587 | { | ||
588 | struct input_dev *dev = psmouse->dev; | ||
589 | struct synaptics_data *priv = psmouse->private; | ||
590 | struct synaptics_hw_state *agm = &priv->agm; | ||
591 | |||
592 | switch (count) { | ||
593 | case 0: | ||
594 | synaptics_report_slot(dev, 0, NULL); | ||
595 | synaptics_report_slot(dev, 1, NULL); | ||
596 | break; | ||
597 | case 1: | ||
598 | synaptics_report_slot(dev, 0, sgm); | ||
599 | synaptics_report_slot(dev, 1, NULL); | ||
600 | break; | ||
601 | case 2: | ||
602 | case 3: /* Fall-through case */ | ||
603 | synaptics_report_slot(dev, 0, sgm); | ||
604 | synaptics_report_slot(dev, 1, agm); | ||
605 | break; | ||
606 | } | ||
607 | |||
608 | /* Don't use active slot count to generate BTN_TOOL events. */ | ||
609 | input_mt_report_pointer_emulation(dev, false); | ||
610 | |||
611 | /* Send the number of fingers reported by touchpad itself. */ | ||
612 | input_mt_report_finger_count(dev, count); | ||
613 | |||
614 | synaptics_report_buttons(psmouse, sgm); | ||
615 | |||
616 | input_sync(dev); | ||
617 | } | ||
618 | |||
619 | static void synaptics_image_sensor_process(struct psmouse *psmouse, | ||
620 | struct synaptics_hw_state *sgm) | ||
621 | { | ||
622 | int count; | ||
623 | |||
624 | if (sgm->z == 0) | ||
625 | count = 0; | ||
626 | else if (sgm->w >= 4) | ||
627 | count = 1; | ||
628 | else if (sgm->w == 0) | ||
629 | count = 2; | ||
630 | else | ||
631 | count = 3; | ||
632 | |||
633 | /* Send resulting input events to user space */ | ||
634 | synaptics_report_mt_data(psmouse, count, sgm); | ||
635 | } | ||
636 | |||
546 | /* | 637 | /* |
547 | * called for each full received packet from the touchpad | 638 | * called for each full received packet from the touchpad |
548 | */ | 639 | */ |
@@ -553,11 +644,15 @@ static void synaptics_process_packet(struct psmouse *psmouse) | |||
553 | struct synaptics_hw_state hw; | 644 | struct synaptics_hw_state hw; |
554 | int num_fingers; | 645 | int num_fingers; |
555 | int finger_width; | 646 | int finger_width; |
556 | int i; | ||
557 | 647 | ||
558 | if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) | 648 | if (synaptics_parse_hw_state(psmouse->packet, priv, &hw)) |
559 | return; | 649 | return; |
560 | 650 | ||
651 | if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { | ||
652 | synaptics_image_sensor_process(psmouse, &hw); | ||
653 | return; | ||
654 | } | ||
655 | |||
561 | if (hw.scroll) { | 656 | if (hw.scroll) { |
562 | priv->scroll += hw.scroll; | 657 | priv->scroll += hw.scroll; |
563 | 658 | ||
@@ -623,24 +718,12 @@ static void synaptics_process_packet(struct psmouse *psmouse) | |||
623 | input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); | 718 | input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); |
624 | 719 | ||
625 | input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); | 720 | input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); |
626 | input_report_key(dev, BTN_LEFT, hw.left); | ||
627 | input_report_key(dev, BTN_RIGHT, hw.right); | ||
628 | |||
629 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) { | 721 | if (SYN_CAP_MULTIFINGER(priv->capabilities)) { |
630 | input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); | 722 | input_report_key(dev, BTN_TOOL_DOUBLETAP, num_fingers == 2); |
631 | input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); | 723 | input_report_key(dev, BTN_TOOL_TRIPLETAP, num_fingers == 3); |
632 | } | 724 | } |
633 | 725 | ||
634 | if (SYN_CAP_MIDDLE_BUTTON(priv->capabilities)) | 726 | synaptics_report_buttons(psmouse, &hw); |
635 | input_report_key(dev, BTN_MIDDLE, hw.middle); | ||
636 | |||
637 | if (SYN_CAP_FOUR_BUTTON(priv->capabilities)) { | ||
638 | input_report_key(dev, BTN_FORWARD, hw.up); | ||
639 | input_report_key(dev, BTN_BACK, hw.down); | ||
640 | } | ||
641 | |||
642 | for (i = 0; i < SYN_CAP_MULTI_BUTTON_NO(priv->ext_cap); i++) | ||
643 | input_report_key(dev, BTN_0 + i, hw.ext_buttons & (1 << i)); | ||
644 | 727 | ||
645 | input_sync(dev); | 728 | input_sync(dev); |
646 | } | 729 | } |
@@ -739,7 +822,14 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) | |||
739 | set_abs_position_params(dev, priv, ABS_X, ABS_Y); | 822 | set_abs_position_params(dev, priv, ABS_X, ABS_Y); |
740 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); | 823 | input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); |
741 | 824 | ||
742 | if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { | 825 | if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { |
826 | input_mt_init_slots(dev, 2); | ||
827 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, | ||
828 | ABS_MT_POSITION_Y); | ||
829 | /* Image sensors can report per-contact pressure */ | ||
830 | input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); | ||
831 | } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) { | ||
832 | /* Non-image sensors with AGM use semi-mt */ | ||
743 | __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); | 833 | __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); |
744 | input_mt_init_slots(dev, 2); | 834 | input_mt_init_slots(dev, 2); |
745 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, | 835 | set_abs_position_params(dev, priv, ABS_MT_POSITION_X, |
diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index a9efbf3e3ea1..0ea7616e8fe1 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h | |||
@@ -74,6 +74,8 @@ | |||
74 | * 2 0x04 reduced filtering firmware does less filtering on | 74 | * 2 0x04 reduced filtering firmware does less filtering on |
75 | * position data, driver should watch | 75 | * position data, driver should watch |
76 | * for noise. | 76 | * for noise. |
77 | * 2 0x08 image sensor image sensor tracks 5 fingers, but only | ||
78 | * reports 2. | ||
77 | * 2 0x20 report min query 0x0f gives min coord reported | 79 | * 2 0x20 report min query 0x0f gives min coord reported |
78 | */ | 80 | */ |
79 | #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */ | 81 | #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */ |
@@ -82,6 +84,7 @@ | |||
82 | #define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x002000) | 84 | #define SYN_CAP_MIN_DIMENSIONS(ex0c) ((ex0c) & 0x002000) |
83 | #define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000) | 85 | #define SYN_CAP_ADV_GESTURE(ex0c) ((ex0c) & 0x080000) |
84 | #define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400) | 86 | #define SYN_CAP_REDUCED_FILTERING(ex0c) ((ex0c) & 0x000400) |
87 | #define SYN_CAP_IMAGE_SENSOR(ex0c) ((ex0c) & 0x000800) | ||
85 | 88 | ||
86 | /* synaptics modes query bits */ | 89 | /* synaptics modes query bits */ |
87 | #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) | 90 | #define SYN_MODE_ABSOLUTE(m) ((m) & (1 << 7)) |