diff options
| author | Yunkang Tang <tommywill2011@gmail.com> | 2013-12-02 01:33:52 -0500 |
|---|---|---|
| committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2013-12-05 15:50:18 -0500 |
| commit | 95f75e91588afecfb0090988393653d21f5d1f91 (patch) | |
| tree | b65031e817f5159b1a85ac87e29e2cf293b53e11 | |
| parent | 9cb80b965eaf7af1369f6e16f48a05fbaaccc021 (diff) | |
Input: ALPS - add support for DualPoint device on Dell XT2 model
The device uses special MPU controller that necessitates the new
initialization sequence for the device. We also define a new protocol for
the trackpad that allows reporting better resolution than older V2
protocol.
Signed-off-by: Yunkang Tang <yunkang.tang@cn.alps.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
| -rw-r--r-- | drivers/input/mouse/alps.c | 206 | ||||
| -rw-r--r-- | drivers/input/mouse/alps.h | 1 |
2 files changed, 204 insertions, 3 deletions
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index ca7a26f1dce8..5cf62e315218 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c | |||
| @@ -70,6 +70,25 @@ static const struct alps_nibble_commands alps_v4_nibble_commands[] = { | |||
| 70 | { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ | 70 | { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| 73 | static const struct alps_nibble_commands alps_v6_nibble_commands[] = { | ||
| 74 | { PSMOUSE_CMD_ENABLE, 0x00 }, /* 0 */ | ||
| 75 | { PSMOUSE_CMD_SETRATE, 0x0a }, /* 1 */ | ||
| 76 | { PSMOUSE_CMD_SETRATE, 0x14 }, /* 2 */ | ||
| 77 | { PSMOUSE_CMD_SETRATE, 0x28 }, /* 3 */ | ||
| 78 | { PSMOUSE_CMD_SETRATE, 0x3c }, /* 4 */ | ||
| 79 | { PSMOUSE_CMD_SETRATE, 0x50 }, /* 5 */ | ||
| 80 | { PSMOUSE_CMD_SETRATE, 0x64 }, /* 6 */ | ||
| 81 | { PSMOUSE_CMD_SETRATE, 0xc8 }, /* 7 */ | ||
| 82 | { PSMOUSE_CMD_GETID, 0x00 }, /* 8 */ | ||
| 83 | { PSMOUSE_CMD_GETINFO, 0x00 }, /* 9 */ | ||
| 84 | { PSMOUSE_CMD_SETRES, 0x00 }, /* a */ | ||
| 85 | { PSMOUSE_CMD_SETRES, 0x01 }, /* b */ | ||
| 86 | { PSMOUSE_CMD_SETRES, 0x02 }, /* c */ | ||
| 87 | { PSMOUSE_CMD_SETRES, 0x03 }, /* d */ | ||
| 88 | { PSMOUSE_CMD_SETSCALE21, 0x00 }, /* e */ | ||
| 89 | { PSMOUSE_CMD_SETSCALE11, 0x00 }, /* f */ | ||
| 90 | }; | ||
| 91 | |||
| 73 | 92 | ||
| 74 | #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ | 93 | #define ALPS_DUALPOINT 0x02 /* touchpad has trackstick */ |
| 75 | #define ALPS_PASS 0x04 /* device has a pass-through port */ | 94 | #define ALPS_PASS 0x04 /* device has a pass-through port */ |
| @@ -103,6 +122,7 @@ static const struct alps_model_info alps_model_data[] = { | |||
| 103 | /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ | 122 | /* Dell Latitude E5500, E6400, E6500, Precision M4400 */ |
| 104 | { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, | 123 | { { 0x62, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, |
| 105 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, | 124 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, |
| 125 | { { 0x73, 0x00, 0x14 }, 0x00, ALPS_PROTO_V6, 0xff, 0xff, ALPS_DUALPOINT }, /* Dell XT2 */ | ||
| 106 | { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ | 126 | { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ |
| 107 | { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, | 127 | { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, |
| 108 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ | 128 | ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ |
| @@ -645,6 +665,76 @@ static void alps_process_packet_v3(struct psmouse *psmouse) | |||
| 645 | alps_process_touchpad_packet_v3(psmouse); | 665 | alps_process_touchpad_packet_v3(psmouse); |
| 646 | } | 666 | } |
| 647 | 667 | ||
| 668 | static void alps_process_packet_v6(struct psmouse *psmouse) | ||
| 669 | { | ||
| 670 | struct alps_data *priv = psmouse->private; | ||
| 671 | unsigned char *packet = psmouse->packet; | ||
| 672 | struct input_dev *dev = psmouse->dev; | ||
| 673 | struct input_dev *dev2 = priv->dev2; | ||
| 674 | int x, y, z, left, right, middle; | ||
| 675 | |||
| 676 | /* | ||
| 677 | * We can use Byte5 to distinguish if the packet is from Touchpad | ||
| 678 | * or Trackpoint. | ||
| 679 | * Touchpad: 0 - 0x7E | ||
| 680 | * Trackpoint: 0x7F | ||
| 681 | */ | ||
| 682 | if (packet[5] == 0x7F) { | ||
| 683 | /* It should be a DualPoint when received Trackpoint packet */ | ||
| 684 | if (!(priv->flags & ALPS_DUALPOINT)) | ||
| 685 | return; | ||
| 686 | |||
| 687 | /* Trackpoint packet */ | ||
| 688 | x = packet[1] | ((packet[3] & 0x20) << 2); | ||
| 689 | y = packet[2] | ((packet[3] & 0x40) << 1); | ||
| 690 | z = packet[4]; | ||
| 691 | left = packet[3] & 0x01; | ||
| 692 | right = packet[3] & 0x02; | ||
| 693 | middle = packet[3] & 0x04; | ||
| 694 | |||
| 695 | /* To prevent the cursor jump when finger lifted */ | ||
| 696 | if (x == 0x7F && y == 0x7F && z == 0x7F) | ||
| 697 | x = y = z = 0; | ||
| 698 | |||
| 699 | /* Divide 4 since trackpoint's speed is too fast */ | ||
| 700 | input_report_rel(dev2, REL_X, (char)x / 4); | ||
| 701 | input_report_rel(dev2, REL_Y, -((char)y / 4)); | ||
| 702 | |||
| 703 | input_report_key(dev2, BTN_LEFT, left); | ||
| 704 | input_report_key(dev2, BTN_RIGHT, right); | ||
| 705 | input_report_key(dev2, BTN_MIDDLE, middle); | ||
| 706 | |||
| 707 | input_sync(dev2); | ||
| 708 | return; | ||
| 709 | } | ||
| 710 | |||
| 711 | /* Touchpad packet */ | ||
| 712 | x = packet[1] | ((packet[3] & 0x78) << 4); | ||
| 713 | y = packet[2] | ((packet[4] & 0x78) << 4); | ||
| 714 | z = packet[5]; | ||
| 715 | left = packet[3] & 0x01; | ||
| 716 | right = packet[3] & 0x02; | ||
| 717 | |||
| 718 | if (z > 30) | ||
| 719 | input_report_key(dev, BTN_TOUCH, 1); | ||
| 720 | if (z < 25) | ||
| 721 | input_report_key(dev, BTN_TOUCH, 0); | ||
| 722 | |||
| 723 | if (z > 0) { | ||
| 724 | input_report_abs(dev, ABS_X, x); | ||
| 725 | input_report_abs(dev, ABS_Y, y); | ||
| 726 | } | ||
| 727 | |||
| 728 | input_report_abs(dev, ABS_PRESSURE, z); | ||
| 729 | input_report_key(dev, BTN_TOOL_FINGER, z > 0); | ||
| 730 | |||
| 731 | /* v6 touchpad does not have middle button */ | ||
| 732 | input_report_key(dev, BTN_LEFT, left); | ||
| 733 | input_report_key(dev, BTN_RIGHT, right); | ||
| 734 | |||
| 735 | input_sync(dev); | ||
| 736 | } | ||
| 737 | |||
| 648 | static void alps_process_packet_v4(struct psmouse *psmouse) | 738 | static void alps_process_packet_v4(struct psmouse *psmouse) |
| 649 | { | 739 | { |
| 650 | struct alps_data *priv = psmouse->private; | 740 | struct alps_data *priv = psmouse->private; |
| @@ -897,7 +987,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) | |||
| 897 | } | 987 | } |
| 898 | 988 | ||
| 899 | /* Bytes 2 - pktsize should have 0 in the highest bit */ | 989 | /* Bytes 2 - pktsize should have 0 in the highest bit */ |
| 900 | if (priv->proto_version != ALPS_PROTO_V5 && | 990 | if ((priv->proto_version < ALPS_PROTO_V5) && |
| 901 | psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && | 991 | psmouse->pktcnt >= 2 && psmouse->pktcnt <= psmouse->pktsize && |
| 902 | (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { | 992 | (psmouse->packet[psmouse->pktcnt - 1] & 0x80)) { |
| 903 | psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", | 993 | psmouse_dbg(psmouse, "refusing packet[%i] = %x\n", |
| @@ -1085,6 +1175,80 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) | |||
| 1085 | return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); | 1175 | return ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETPOLL); |
| 1086 | } | 1176 | } |
| 1087 | 1177 | ||
| 1178 | static int alps_monitor_mode_send_word(struct psmouse *psmouse, u16 word) | ||
| 1179 | { | ||
| 1180 | int i, nibble; | ||
| 1181 | |||
| 1182 | /* | ||
| 1183 | * b0-b11 are valid bits, send sequence is inverse. | ||
| 1184 | * e.g. when word = 0x0123, nibble send sequence is 3, 2, 1 | ||
| 1185 | */ | ||
| 1186 | for (i = 0; i <= 8; i += 4) { | ||
| 1187 | nibble = (word >> i) & 0xf; | ||
| 1188 | if (alps_command_mode_send_nibble(psmouse, nibble)) | ||
| 1189 | return -1; | ||
| 1190 | } | ||
| 1191 | |||
| 1192 | return 0; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | static int alps_monitor_mode_write_reg(struct psmouse *psmouse, | ||
| 1196 | u16 addr, u16 value) | ||
| 1197 | { | ||
| 1198 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 1199 | |||
| 1200 | /* 0x0A0 is the command to write the word */ | ||
| 1201 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE) || | ||
| 1202 | alps_monitor_mode_send_word(psmouse, 0x0A0) || | ||
| 1203 | alps_monitor_mode_send_word(psmouse, addr) || | ||
| 1204 | alps_monitor_mode_send_word(psmouse, value) || | ||
| 1205 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE)) | ||
| 1206 | return -1; | ||
| 1207 | |||
| 1208 | return 0; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | static int alps_monitor_mode(struct psmouse *psmouse, bool enable) | ||
| 1212 | { | ||
| 1213 | struct ps2dev *ps2dev = &psmouse->ps2dev; | ||
| 1214 | |||
| 1215 | if (enable) { | ||
| 1216 | /* EC E9 F5 F5 E7 E6 E7 E9 to enter monitor mode */ | ||
| 1217 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || | ||
| 1218 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO) || | ||
| 1219 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 1220 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || | ||
| 1221 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 1222 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 1223 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || | ||
| 1224 | ps2_command(ps2dev, NULL, PSMOUSE_CMD_GETINFO)) | ||
| 1225 | return -1; | ||
| 1226 | } else { | ||
| 1227 | /* EC to exit monitor mode */ | ||
| 1228 | if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP)) | ||
| 1229 | return -1; | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | return 0; | ||
| 1233 | } | ||
| 1234 | |||
| 1235 | static int alps_absolute_mode_v6(struct psmouse *psmouse) | ||
| 1236 | { | ||
| 1237 | u16 reg_val = 0x181; | ||
| 1238 | int ret = -1; | ||
| 1239 | |||
| 1240 | /* enter monitor mode, to write the register */ | ||
| 1241 | if (alps_monitor_mode(psmouse, true)) | ||
| 1242 | return -1; | ||
| 1243 | |||
| 1244 | ret = alps_monitor_mode_write_reg(psmouse, 0x000, reg_val); | ||
| 1245 | |||
| 1246 | if (alps_monitor_mode(psmouse, false)) | ||
| 1247 | ret = -1; | ||
| 1248 | |||
| 1249 | return ret; | ||
| 1250 | } | ||
| 1251 | |||
| 1088 | static int alps_get_status(struct psmouse *psmouse, char *param) | 1252 | static int alps_get_status(struct psmouse *psmouse, char *param) |
| 1089 | { | 1253 | { |
| 1090 | /* Get status: 0xF5 0xF5 0xF5 0xE9 */ | 1254 | /* Get status: 0xF5 0xF5 0xF5 0xE9 */ |
| @@ -1189,6 +1353,32 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) | |||
| 1189 | return 0; | 1353 | return 0; |
| 1190 | } | 1354 | } |
| 1191 | 1355 | ||
| 1356 | static int alps_hw_init_v6(struct psmouse *psmouse) | ||
| 1357 | { | ||
| 1358 | unsigned char param[2] = {0xC8, 0x14}; | ||
| 1359 | |||
| 1360 | /* Enter passthrough mode to let trackpoint enter 6byte raw mode */ | ||
| 1361 | if (alps_passthrough_mode_v2(psmouse, true)) | ||
| 1362 | return -1; | ||
| 1363 | |||
| 1364 | if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 1365 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 1366 | ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || | ||
| 1367 | ps2_command(&psmouse->ps2dev, ¶m[0], PSMOUSE_CMD_SETRATE) || | ||
| 1368 | ps2_command(&psmouse->ps2dev, ¶m[1], PSMOUSE_CMD_SETRATE)) | ||
| 1369 | return -1; | ||
| 1370 | |||
| 1371 | if (alps_passthrough_mode_v2(psmouse, false)) | ||
| 1372 | return -1; | ||
| 1373 | |||
| 1374 | if (alps_absolute_mode_v6(psmouse)) { | ||
| 1375 | psmouse_err(psmouse, "Failed to enable absolute mode\n"); | ||
| 1376 | return -1; | ||
| 1377 | } | ||
| 1378 | |||
| 1379 | return 0; | ||
| 1380 | } | ||
| 1381 | |||
| 1192 | /* | 1382 | /* |
| 1193 | * Enable or disable passthrough mode to the trackstick. | 1383 | * Enable or disable passthrough mode to the trackstick. |
| 1194 | */ | 1384 | */ |
| @@ -1553,6 +1743,8 @@ static void alps_set_defaults(struct alps_data *priv) | |||
| 1553 | priv->hw_init = alps_hw_init_v1_v2; | 1743 | priv->hw_init = alps_hw_init_v1_v2; |
| 1554 | priv->process_packet = alps_process_packet_v1_v2; | 1744 | priv->process_packet = alps_process_packet_v1_v2; |
| 1555 | priv->set_abs_params = alps_set_abs_params_st; | 1745 | priv->set_abs_params = alps_set_abs_params_st; |
| 1746 | priv->x_max = 1023; | ||
| 1747 | priv->y_max = 767; | ||
| 1556 | break; | 1748 | break; |
| 1557 | case ALPS_PROTO_V3: | 1749 | case ALPS_PROTO_V3: |
| 1558 | priv->hw_init = alps_hw_init_v3; | 1750 | priv->hw_init = alps_hw_init_v3; |
| @@ -1584,6 +1776,14 @@ static void alps_set_defaults(struct alps_data *priv) | |||
| 1584 | priv->x_bits = 23; | 1776 | priv->x_bits = 23; |
| 1585 | priv->y_bits = 12; | 1777 | priv->y_bits = 12; |
| 1586 | break; | 1778 | break; |
| 1779 | case ALPS_PROTO_V6: | ||
| 1780 | priv->hw_init = alps_hw_init_v6; | ||
| 1781 | priv->process_packet = alps_process_packet_v6; | ||
| 1782 | priv->set_abs_params = alps_set_abs_params_st; | ||
| 1783 | priv->nibble_commands = alps_v6_nibble_commands; | ||
| 1784 | priv->x_max = 2047; | ||
| 1785 | priv->y_max = 1535; | ||
| 1786 | break; | ||
| 1587 | } | 1787 | } |
| 1588 | } | 1788 | } |
| 1589 | 1789 | ||
| @@ -1705,8 +1905,8 @@ static void alps_disconnect(struct psmouse *psmouse) | |||
| 1705 | static void alps_set_abs_params_st(struct alps_data *priv, | 1905 | static void alps_set_abs_params_st(struct alps_data *priv, |
| 1706 | struct input_dev *dev1) | 1906 | struct input_dev *dev1) |
| 1707 | { | 1907 | { |
| 1708 | input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); | 1908 | input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); |
| 1709 | input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); | 1909 | input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); |
| 1710 | } | 1910 | } |
| 1711 | 1911 | ||
| 1712 | static void alps_set_abs_params_mt(struct alps_data *priv, | 1912 | static void alps_set_abs_params_mt(struct alps_data *priv, |
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index eee59853b9ce..704f0f924307 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #define ALPS_PROTO_V3 3 | 17 | #define ALPS_PROTO_V3 3 |
| 18 | #define ALPS_PROTO_V4 4 | 18 | #define ALPS_PROTO_V4 4 |
| 19 | #define ALPS_PROTO_V5 5 | 19 | #define ALPS_PROTO_V5 5 |
| 20 | #define ALPS_PROTO_V6 6 | ||
| 20 | 21 | ||
| 21 | /** | 22 | /** |
| 22 | * struct alps_model_info - touchpad ID table | 23 | * struct alps_model_info - touchpad ID table |
