.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Elantech Touchpad driver (v6) |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 2007-2009 Arjan Opmeer <arjan@opmeer.net> |
---|
5 | | - * |
---|
6 | | - * This program is free software; you can redistribute it and/or modify it |
---|
7 | | - * under the terms of the GNU General Public License version 2 as published |
---|
8 | | - * by the Free Software Foundation. |
---|
9 | 6 | * |
---|
10 | 7 | * Trademarks are the property of their respective owners. |
---|
11 | 8 | */ |
---|
.. | .. |
---|
90 | 87 | psmouse_err(psmouse, "ps2 command 0x%02x failed.\n", command); |
---|
91 | 88 | |
---|
92 | 89 | return rc; |
---|
| 90 | +} |
---|
| 91 | + |
---|
| 92 | +/* |
---|
| 93 | + * Send an Elantech style special command to read 3 bytes from a register |
---|
| 94 | + */ |
---|
| 95 | +static int elantech_read_reg_params(struct psmouse *psmouse, u8 reg, u8 *param) |
---|
| 96 | +{ |
---|
| 97 | + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || |
---|
| 98 | + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || |
---|
| 99 | + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || |
---|
| 100 | + elantech_ps2_command(psmouse, NULL, reg) || |
---|
| 101 | + elantech_ps2_command(psmouse, param, PSMOUSE_CMD_GETINFO)) { |
---|
| 102 | + psmouse_err(psmouse, |
---|
| 103 | + "failed to read register %#02x\n", reg); |
---|
| 104 | + return -EIO; |
---|
| 105 | + } |
---|
| 106 | + |
---|
| 107 | + return 0; |
---|
| 108 | +} |
---|
| 109 | + |
---|
| 110 | +/* |
---|
| 111 | + * Send an Elantech style special command to write a register with a parameter |
---|
| 112 | + */ |
---|
| 113 | +static int elantech_write_reg_params(struct psmouse *psmouse, u8 reg, u8 *param) |
---|
| 114 | +{ |
---|
| 115 | + if (elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || |
---|
| 116 | + elantech_ps2_command(psmouse, NULL, ETP_REGISTER_READWRITE) || |
---|
| 117 | + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || |
---|
| 118 | + elantech_ps2_command(psmouse, NULL, reg) || |
---|
| 119 | + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || |
---|
| 120 | + elantech_ps2_command(psmouse, NULL, param[0]) || |
---|
| 121 | + elantech_ps2_command(psmouse, NULL, ETP_PS2_CUSTOM_COMMAND) || |
---|
| 122 | + elantech_ps2_command(psmouse, NULL, param[1]) || |
---|
| 123 | + elantech_ps2_command(psmouse, NULL, PSMOUSE_CMD_SETSCALE11)) { |
---|
| 124 | + psmouse_err(psmouse, |
---|
| 125 | + "failed to write register %#02x with value %#02x%#02x\n", |
---|
| 126 | + reg, param[0], param[1]); |
---|
| 127 | + return -EIO; |
---|
| 128 | + } |
---|
| 129 | + |
---|
| 130 | + return 0; |
---|
93 | 131 | } |
---|
94 | 132 | |
---|
95 | 133 | /* |
---|
.. | .. |
---|
230 | 268 | } |
---|
231 | 269 | |
---|
232 | 270 | /* |
---|
| 271 | + * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in |
---|
| 272 | + * fw_version for this is based on the following fw_version & caps table: |
---|
| 273 | + * |
---|
| 274 | + * Laptop-model: fw_version: caps: buttons: |
---|
| 275 | + * Acer S3 0x461f00 10, 13, 0e clickpad |
---|
| 276 | + * Acer S7-392 0x581f01 50, 17, 0d clickpad |
---|
| 277 | + * Acer V5-131 0x461f02 01, 16, 0c clickpad |
---|
| 278 | + * Acer V5-551 0x461f00 ? clickpad |
---|
| 279 | + * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons |
---|
| 280 | + * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons |
---|
| 281 | + * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons |
---|
| 282 | + * Asus TP500LN 0x381f17 10, 14, 0e clickpad |
---|
| 283 | + * Asus X750JN 0x381f17 10, 14, 0e clickpad |
---|
| 284 | + * Asus UX31 0x361f00 20, 15, 0e clickpad |
---|
| 285 | + * Asus UX32VD 0x361f02 00, 15, 0e clickpad |
---|
| 286 | + * Avatar AVIU-145A2 0x361f00 ? clickpad |
---|
| 287 | + * Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**) |
---|
| 288 | + * Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**) |
---|
| 289 | + * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons |
---|
| 290 | + * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons |
---|
| 291 | + * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons |
---|
| 292 | + * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons |
---|
| 293 | + * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons |
---|
| 294 | + * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons |
---|
| 295 | + * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) |
---|
| 296 | + * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons |
---|
| 297 | + * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) |
---|
| 298 | + * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*) |
---|
| 299 | + * Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons |
---|
| 300 | + * Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad |
---|
| 301 | + * Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad |
---|
| 302 | + * Samsung NP900X3E-A02 0x575f03 ? clickpad |
---|
| 303 | + * Samsung NP-QX410 0x851b00 19, 14, 0c clickpad |
---|
| 304 | + * Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons |
---|
| 305 | + * Samsung RF710 0x450f00 ? 2 hw buttons |
---|
| 306 | + * System76 Pangolin 0x250f01 ? 2 hw buttons |
---|
| 307 | + * (*) + 3 trackpoint buttons |
---|
| 308 | + * (**) + 0 trackpoint buttons |
---|
| 309 | + * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps |
---|
| 310 | + */ |
---|
| 311 | +static inline int elantech_is_buttonpad(struct elantech_device_info *info) |
---|
| 312 | +{ |
---|
| 313 | + return info->fw_version & 0x001000; |
---|
| 314 | +} |
---|
| 315 | + |
---|
| 316 | +/* |
---|
233 | 317 | * Interpret complete data packets and report absolute mode input events for |
---|
234 | 318 | * hardware version 1. (4 byte packets) |
---|
235 | 319 | */ |
---|
.. | .. |
---|
340 | 424 | */ |
---|
341 | 425 | if (packet[3] & 0x80) |
---|
342 | 426 | fingers = 4; |
---|
343 | | - /* fall through */ |
---|
| 427 | + fallthrough; |
---|
344 | 428 | case 1: |
---|
345 | 429 | /* |
---|
346 | 430 | * byte 1: . . . . x11 x10 x9 x8 |
---|
.. | .. |
---|
539 | 623 | input_report_key(dev, BTN_TOOL_TRIPLETAP, fingers == 3); |
---|
540 | 624 | |
---|
541 | 625 | /* For clickpads map both buttons to BTN_LEFT */ |
---|
542 | | - if (etd->info.fw_version & 0x001000) |
---|
| 626 | + if (elantech_is_buttonpad(&etd->info)) |
---|
543 | 627 | input_report_key(dev, BTN_LEFT, packet[0] & 0x03); |
---|
544 | 628 | else |
---|
545 | 629 | psmouse_report_standard_buttons(dev, packet[0]); |
---|
.. | .. |
---|
557 | 641 | unsigned char *packet = psmouse->packet; |
---|
558 | 642 | |
---|
559 | 643 | /* For clickpads map both buttons to BTN_LEFT */ |
---|
560 | | - if (etd->info.fw_version & 0x001000) |
---|
| 644 | + if (elantech_is_buttonpad(&etd->info)) |
---|
561 | 645 | input_report_key(dev, BTN_LEFT, packet[0] & 0x03); |
---|
562 | 646 | else |
---|
563 | 647 | psmouse_report_standard_buttons(dev, packet[0]); |
---|
.. | .. |
---|
590 | 674 | struct input_dev *dev = psmouse->dev; |
---|
591 | 675 | struct elantech_data *etd = psmouse->private; |
---|
592 | 676 | unsigned char *packet = psmouse->packet; |
---|
593 | | - int id = ((packet[3] & 0xe0) >> 5) - 1; |
---|
| 677 | + int id; |
---|
594 | 678 | int pres, traces; |
---|
595 | 679 | |
---|
596 | | - if (id < 0) |
---|
| 680 | + id = ((packet[3] & 0xe0) >> 5) - 1; |
---|
| 681 | + if (id < 0 || id >= ETP_MAX_FINGERS) |
---|
597 | 682 | return; |
---|
598 | 683 | |
---|
599 | 684 | etd->mt[id].x = ((packet[1] & 0x0f) << 8) | packet[2]; |
---|
.. | .. |
---|
623 | 708 | int id, sid; |
---|
624 | 709 | |
---|
625 | 710 | id = ((packet[0] & 0xe0) >> 5) - 1; |
---|
626 | | - if (id < 0) |
---|
| 711 | + if (id < 0 || id >= ETP_MAX_FINGERS) |
---|
627 | 712 | return; |
---|
628 | 713 | |
---|
629 | 714 | sid = ((packet[3] & 0xe0) >> 5) - 1; |
---|
.. | .. |
---|
644 | 729 | input_report_abs(dev, ABS_MT_POSITION_X, etd->mt[id].x); |
---|
645 | 730 | input_report_abs(dev, ABS_MT_POSITION_Y, etd->mt[id].y); |
---|
646 | 731 | |
---|
647 | | - if (sid >= 0) { |
---|
| 732 | + if (sid >= 0 && sid < ETP_MAX_FINGERS) { |
---|
648 | 733 | etd->mt[sid].x += delta_x2 * weight; |
---|
649 | 734 | etd->mt[sid].y -= delta_y2 * weight; |
---|
650 | 735 | input_mt_slot(dev, sid); |
---|
.. | .. |
---|
1007 | 1092 | return rc; |
---|
1008 | 1093 | } |
---|
1009 | 1094 | |
---|
1010 | | -static int elantech_set_range(struct psmouse *psmouse, |
---|
1011 | | - unsigned int *x_min, unsigned int *y_min, |
---|
1012 | | - unsigned int *x_max, unsigned int *y_max, |
---|
1013 | | - unsigned int *width) |
---|
1014 | | -{ |
---|
1015 | | - struct elantech_data *etd = psmouse->private; |
---|
1016 | | - struct elantech_device_info *info = &etd->info; |
---|
1017 | | - unsigned char param[3]; |
---|
1018 | | - unsigned char traces; |
---|
1019 | | - |
---|
1020 | | - switch (info->hw_version) { |
---|
1021 | | - case 1: |
---|
1022 | | - *x_min = ETP_XMIN_V1; |
---|
1023 | | - *y_min = ETP_YMIN_V1; |
---|
1024 | | - *x_max = ETP_XMAX_V1; |
---|
1025 | | - *y_max = ETP_YMAX_V1; |
---|
1026 | | - break; |
---|
1027 | | - |
---|
1028 | | - case 2: |
---|
1029 | | - if (info->fw_version == 0x020800 || |
---|
1030 | | - info->fw_version == 0x020b00 || |
---|
1031 | | - info->fw_version == 0x020030) { |
---|
1032 | | - *x_min = ETP_XMIN_V2; |
---|
1033 | | - *y_min = ETP_YMIN_V2; |
---|
1034 | | - *x_max = ETP_XMAX_V2; |
---|
1035 | | - *y_max = ETP_YMAX_V2; |
---|
1036 | | - } else { |
---|
1037 | | - int i; |
---|
1038 | | - int fixed_dpi; |
---|
1039 | | - |
---|
1040 | | - i = (info->fw_version > 0x020800 && |
---|
1041 | | - info->fw_version < 0x020900) ? 1 : 2; |
---|
1042 | | - |
---|
1043 | | - if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) |
---|
1044 | | - return -1; |
---|
1045 | | - |
---|
1046 | | - fixed_dpi = param[1] & 0x10; |
---|
1047 | | - |
---|
1048 | | - if (((info->fw_version >> 16) == 0x14) && fixed_dpi) { |
---|
1049 | | - if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) |
---|
1050 | | - return -1; |
---|
1051 | | - |
---|
1052 | | - *x_max = (info->capabilities[1] - i) * param[1] / 2; |
---|
1053 | | - *y_max = (info->capabilities[2] - i) * param[2] / 2; |
---|
1054 | | - } else if (info->fw_version == 0x040216) { |
---|
1055 | | - *x_max = 819; |
---|
1056 | | - *y_max = 405; |
---|
1057 | | - } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) { |
---|
1058 | | - *x_max = 900; |
---|
1059 | | - *y_max = 500; |
---|
1060 | | - } else { |
---|
1061 | | - *x_max = (info->capabilities[1] - i) * 64; |
---|
1062 | | - *y_max = (info->capabilities[2] - i) * 64; |
---|
1063 | | - } |
---|
1064 | | - } |
---|
1065 | | - break; |
---|
1066 | | - |
---|
1067 | | - case 3: |
---|
1068 | | - if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) |
---|
1069 | | - return -1; |
---|
1070 | | - |
---|
1071 | | - *x_max = (0x0f & param[0]) << 8 | param[1]; |
---|
1072 | | - *y_max = (0xf0 & param[0]) << 4 | param[2]; |
---|
1073 | | - break; |
---|
1074 | | - |
---|
1075 | | - case 4: |
---|
1076 | | - if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) |
---|
1077 | | - return -1; |
---|
1078 | | - |
---|
1079 | | - *x_max = (0x0f & param[0]) << 8 | param[1]; |
---|
1080 | | - *y_max = (0xf0 & param[0]) << 4 | param[2]; |
---|
1081 | | - traces = info->capabilities[1]; |
---|
1082 | | - if ((traces < 2) || (traces > *x_max)) |
---|
1083 | | - return -1; |
---|
1084 | | - |
---|
1085 | | - *width = *x_max / (traces - 1); |
---|
1086 | | - break; |
---|
1087 | | - } |
---|
1088 | | - |
---|
1089 | | - return 0; |
---|
1090 | | -} |
---|
1091 | | - |
---|
1092 | 1095 | /* |
---|
1093 | 1096 | * (value from firmware) * 10 + 790 = dpi |
---|
1094 | 1097 | * we also have to convert dpi to dots/mm (*10/254 to avoid floating point) |
---|
.. | .. |
---|
1115 | 1118 | return 0; |
---|
1116 | 1119 | } |
---|
1117 | 1120 | |
---|
1118 | | -/* |
---|
1119 | | - * Advertise INPUT_PROP_BUTTONPAD for clickpads. The testing of bit 12 in |
---|
1120 | | - * fw_version for this is based on the following fw_version & caps table: |
---|
1121 | | - * |
---|
1122 | | - * Laptop-model: fw_version: caps: buttons: |
---|
1123 | | - * Acer S3 0x461f00 10, 13, 0e clickpad |
---|
1124 | | - * Acer S7-392 0x581f01 50, 17, 0d clickpad |
---|
1125 | | - * Acer V5-131 0x461f02 01, 16, 0c clickpad |
---|
1126 | | - * Acer V5-551 0x461f00 ? clickpad |
---|
1127 | | - * Asus K53SV 0x450f01 78, 15, 0c 2 hw buttons |
---|
1128 | | - * Asus G46VW 0x460f02 00, 18, 0c 2 hw buttons |
---|
1129 | | - * Asus G750JX 0x360f00 00, 16, 0c 2 hw buttons |
---|
1130 | | - * Asus TP500LN 0x381f17 10, 14, 0e clickpad |
---|
1131 | | - * Asus X750JN 0x381f17 10, 14, 0e clickpad |
---|
1132 | | - * Asus UX31 0x361f00 20, 15, 0e clickpad |
---|
1133 | | - * Asus UX32VD 0x361f02 00, 15, 0e clickpad |
---|
1134 | | - * Avatar AVIU-145A2 0x361f00 ? clickpad |
---|
1135 | | - * Fujitsu CELSIUS H760 0x570f02 40, 14, 0c 3 hw buttons (**) |
---|
1136 | | - * Fujitsu CELSIUS H780 0x5d0f02 41, 16, 0d 3 hw buttons (**) |
---|
1137 | | - * Fujitsu LIFEBOOK E544 0x470f00 d0, 12, 09 2 hw buttons |
---|
1138 | | - * Fujitsu LIFEBOOK E546 0x470f00 50, 12, 09 2 hw buttons |
---|
1139 | | - * Fujitsu LIFEBOOK E547 0x470f00 50, 12, 09 2 hw buttons |
---|
1140 | | - * Fujitsu LIFEBOOK E554 0x570f01 40, 14, 0c 2 hw buttons |
---|
1141 | | - * Fujitsu LIFEBOOK E557 0x570f01 40, 14, 0c 2 hw buttons |
---|
1142 | | - * Fujitsu T725 0x470f01 05, 12, 09 2 hw buttons |
---|
1143 | | - * Fujitsu H730 0x570f00 c0, 14, 0c 3 hw buttons (**) |
---|
1144 | | - * Gigabyte U2442 0x450f01 58, 17, 0c 2 hw buttons |
---|
1145 | | - * Lenovo L430 0x350f02 b9, 15, 0c 2 hw buttons (*) |
---|
1146 | | - * Lenovo L530 0x350f02 b9, 15, 0c 2 hw buttons (*) |
---|
1147 | | - * Samsung NF210 0x150b00 78, 14, 0a 2 hw buttons |
---|
1148 | | - * Samsung NP770Z5E 0x575f01 10, 15, 0f clickpad |
---|
1149 | | - * Samsung NP700Z5B 0x361f06 21, 15, 0f clickpad |
---|
1150 | | - * Samsung NP900X3E-A02 0x575f03 ? clickpad |
---|
1151 | | - * Samsung NP-QX410 0x851b00 19, 14, 0c clickpad |
---|
1152 | | - * Samsung RC512 0x450f00 08, 15, 0c 2 hw buttons |
---|
1153 | | - * Samsung RF710 0x450f00 ? 2 hw buttons |
---|
1154 | | - * System76 Pangolin 0x250f01 ? 2 hw buttons |
---|
1155 | | - * (*) + 3 trackpoint buttons |
---|
1156 | | - * (**) + 0 trackpoint buttons |
---|
1157 | | - * Note: Lenovo L430 and Lenovo L530 have the same fw_version/caps |
---|
1158 | | - */ |
---|
1159 | 1121 | static void elantech_set_buttonpad_prop(struct psmouse *psmouse) |
---|
1160 | 1122 | { |
---|
1161 | 1123 | struct input_dev *dev = psmouse->dev; |
---|
1162 | 1124 | struct elantech_data *etd = psmouse->private; |
---|
1163 | 1125 | |
---|
1164 | | - if (etd->info.fw_version & 0x001000) { |
---|
| 1126 | + if (elantech_is_buttonpad(&etd->info)) { |
---|
1165 | 1127 | __set_bit(INPUT_PROP_BUTTONPAD, dev->propbit); |
---|
1166 | 1128 | __clear_bit(BTN_RIGHT, dev->keybit); |
---|
1167 | 1129 | } |
---|
.. | .. |
---|
1197 | 1159 | { } |
---|
1198 | 1160 | }; |
---|
1199 | 1161 | |
---|
1200 | | -static const char * const middle_button_pnp_ids[] = { |
---|
1201 | | - "LEN2131", /* ThinkPad P52 w/ NFC */ |
---|
1202 | | - "LEN2132", /* ThinkPad P52 */ |
---|
1203 | | - "LEN2133", /* ThinkPad P72 w/ NFC */ |
---|
1204 | | - "LEN2134", /* ThinkPad P72 */ |
---|
1205 | | - "LEN0407", |
---|
1206 | | - "LEN0408", |
---|
1207 | | - NULL |
---|
1208 | | -}; |
---|
1209 | | - |
---|
1210 | 1162 | /* |
---|
1211 | 1163 | * Set the appropriate event bits for the input subsystem |
---|
1212 | 1164 | */ |
---|
.. | .. |
---|
1215 | 1167 | struct input_dev *dev = psmouse->dev; |
---|
1216 | 1168 | struct elantech_data *etd = psmouse->private; |
---|
1217 | 1169 | struct elantech_device_info *info = &etd->info; |
---|
1218 | | - unsigned int x_min = 0, y_min = 0, x_max = 0, y_max = 0, width = 0; |
---|
1219 | | - |
---|
1220 | | - if (elantech_set_range(psmouse, &x_min, &y_min, &x_max, &y_max, &width)) |
---|
1221 | | - return -1; |
---|
| 1170 | + unsigned int x_min = info->x_min, y_min = info->y_min, |
---|
| 1171 | + x_max = info->x_max, y_max = info->y_max, |
---|
| 1172 | + width = info->width; |
---|
1222 | 1173 | |
---|
1223 | 1174 | __set_bit(INPUT_PROP_POINTER, dev->propbit); |
---|
1224 | 1175 | __set_bit(EV_KEY, dev->evbit); |
---|
.. | .. |
---|
1226 | 1177 | __clear_bit(EV_REL, dev->evbit); |
---|
1227 | 1178 | |
---|
1228 | 1179 | __set_bit(BTN_LEFT, dev->keybit); |
---|
1229 | | - if (dmi_check_system(elantech_dmi_has_middle_button) || |
---|
1230 | | - psmouse_matches_pnp_id(psmouse, middle_button_pnp_ids)) |
---|
| 1180 | + if (info->has_middle_button) |
---|
1231 | 1181 | __set_bit(BTN_MIDDLE, dev->keybit); |
---|
1232 | 1182 | __set_bit(BTN_RIGHT, dev->keybit); |
---|
1233 | 1183 | |
---|
.. | .. |
---|
1251 | 1201 | case 2: |
---|
1252 | 1202 | __set_bit(BTN_TOOL_QUADTAP, dev->keybit); |
---|
1253 | 1203 | __set_bit(INPUT_PROP_SEMI_MT, dev->propbit); |
---|
1254 | | - /* fall through */ |
---|
| 1204 | + fallthrough; |
---|
1255 | 1205 | case 3: |
---|
1256 | 1206 | if (info->hw_version == 3) |
---|
1257 | 1207 | elantech_set_buttonpad_prop(psmouse); |
---|
.. | .. |
---|
1635 | 1585 | }; |
---|
1636 | 1586 | |
---|
1637 | 1587 | /* |
---|
| 1588 | + * Change Report id 0x5E to 0x5F. |
---|
| 1589 | + */ |
---|
| 1590 | +static int elantech_change_report_id(struct psmouse *psmouse) |
---|
| 1591 | +{ |
---|
| 1592 | + /* |
---|
| 1593 | + * NOTE: the code is expecting to receive param[] as an array of 3 |
---|
| 1594 | + * items (see __ps2_command()), even if in this case only 2 are |
---|
| 1595 | + * actually needed. Make sure the array size is 3 to avoid potential |
---|
| 1596 | + * stack out-of-bound accesses. |
---|
| 1597 | + */ |
---|
| 1598 | + unsigned char param[3] = { 0x10, 0x03 }; |
---|
| 1599 | + |
---|
| 1600 | + if (elantech_write_reg_params(psmouse, 0x7, param) || |
---|
| 1601 | + elantech_read_reg_params(psmouse, 0x7, param) || |
---|
| 1602 | + param[0] != 0x10 || param[1] != 0x03) { |
---|
| 1603 | + psmouse_err(psmouse, "Unable to change report ID to 0x5f.\n"); |
---|
| 1604 | + return -EIO; |
---|
| 1605 | + } |
---|
| 1606 | + |
---|
| 1607 | + return 0; |
---|
| 1608 | +} |
---|
| 1609 | +/* |
---|
1638 | 1610 | * determine hardware version and set some properties according to it. |
---|
1639 | 1611 | */ |
---|
1640 | 1612 | static int elantech_set_properties(struct elantech_device_info *info) |
---|
1641 | 1613 | { |
---|
1642 | 1614 | /* This represents the version of IC body. */ |
---|
1643 | | - int ver = (info->fw_version & 0x0f0000) >> 16; |
---|
| 1615 | + info->ic_version = (info->fw_version & 0x0f0000) >> 16; |
---|
1644 | 1616 | |
---|
1645 | 1617 | /* Early version of Elan touchpads doesn't obey the rule. */ |
---|
1646 | 1618 | if (info->fw_version < 0x020030 || info->fw_version == 0x020600) |
---|
1647 | 1619 | info->hw_version = 1; |
---|
1648 | 1620 | else { |
---|
1649 | | - switch (ver) { |
---|
| 1621 | + switch (info->ic_version) { |
---|
1650 | 1622 | case 2: |
---|
1651 | 1623 | case 4: |
---|
1652 | 1624 | info->hw_version = 2; |
---|
.. | .. |
---|
1661 | 1633 | return -1; |
---|
1662 | 1634 | } |
---|
1663 | 1635 | } |
---|
| 1636 | + |
---|
| 1637 | + /* Get information pattern for hw_version 4 */ |
---|
| 1638 | + info->pattern = 0x00; |
---|
| 1639 | + if (info->ic_version == 0x0f && (info->fw_version & 0xff) <= 0x02) |
---|
| 1640 | + info->pattern = info->fw_version & 0xff; |
---|
1664 | 1641 | |
---|
1665 | 1642 | /* decide which send_cmd we're gonna use early */ |
---|
1666 | 1643 | info->send_cmd = info->hw_version >= 3 ? elantech_send_cmd : |
---|
.. | .. |
---|
1702 | 1679 | struct elantech_device_info *info) |
---|
1703 | 1680 | { |
---|
1704 | 1681 | unsigned char param[3]; |
---|
| 1682 | + unsigned char traces; |
---|
| 1683 | + unsigned char ic_body[3]; |
---|
1705 | 1684 | |
---|
1706 | 1685 | memset(info, 0, sizeof(*info)); |
---|
1707 | 1686 | |
---|
.. | .. |
---|
1744 | 1723 | info->samples[2]); |
---|
1745 | 1724 | } |
---|
1746 | 1725 | |
---|
| 1726 | + if (info->pattern > 0x00 && info->ic_version == 0xf) { |
---|
| 1727 | + if (info->send_cmd(psmouse, ETP_ICBODY_QUERY, ic_body)) { |
---|
| 1728 | + psmouse_err(psmouse, "failed to query ic body\n"); |
---|
| 1729 | + return -EINVAL; |
---|
| 1730 | + } |
---|
| 1731 | + info->ic_version = be16_to_cpup((__be16 *)ic_body); |
---|
| 1732 | + psmouse_info(psmouse, |
---|
| 1733 | + "Elan ic body: %#04x, current fw version: %#02x\n", |
---|
| 1734 | + info->ic_version, ic_body[2]); |
---|
| 1735 | + } |
---|
| 1736 | + |
---|
| 1737 | + info->product_id = be16_to_cpup((__be16 *)info->samples); |
---|
| 1738 | + if (info->pattern == 0x00) |
---|
| 1739 | + info->product_id &= 0xff; |
---|
| 1740 | + |
---|
1747 | 1741 | if (info->samples[1] == 0x74 && info->hw_version == 0x03) { |
---|
1748 | 1742 | /* |
---|
1749 | 1743 | * This module has a bug which makes absolute mode |
---|
.. | .. |
---|
1758 | 1752 | /* The MSB indicates the presence of the trackpoint */ |
---|
1759 | 1753 | info->has_trackpoint = (info->capabilities[0] & 0x80) == 0x80; |
---|
1760 | 1754 | |
---|
| 1755 | + if (info->has_trackpoint && info->ic_version == 0x0011 && |
---|
| 1756 | + (info->product_id == 0x08 || info->product_id == 0x09 || |
---|
| 1757 | + info->product_id == 0x0d || info->product_id == 0x0e)) { |
---|
| 1758 | + /* |
---|
| 1759 | + * This module has a bug which makes trackpoint in SMBus |
---|
| 1760 | + * mode return invalid data unless trackpoint is switched |
---|
| 1761 | + * from using 0x5e reports to 0x5f. If we are not able to |
---|
| 1762 | + * make the switch, let's abort initialization so we'll be |
---|
| 1763 | + * using standard PS/2 protocol. |
---|
| 1764 | + */ |
---|
| 1765 | + if (elantech_change_report_id(psmouse)) { |
---|
| 1766 | + psmouse_info(psmouse, |
---|
| 1767 | + "Trackpoint report is broken, forcing standard PS/2 protocol\n"); |
---|
| 1768 | + return -ENODEV; |
---|
| 1769 | + } |
---|
| 1770 | + } |
---|
| 1771 | + |
---|
1761 | 1772 | info->x_res = 31; |
---|
1762 | 1773 | info->y_res = 31; |
---|
1763 | 1774 | if (info->hw_version == 4) { |
---|
.. | .. |
---|
1769 | 1780 | "failed to query resolution data.\n"); |
---|
1770 | 1781 | } |
---|
1771 | 1782 | } |
---|
| 1783 | + |
---|
| 1784 | + /* query range information */ |
---|
| 1785 | + switch (info->hw_version) { |
---|
| 1786 | + case 1: |
---|
| 1787 | + info->x_min = ETP_XMIN_V1; |
---|
| 1788 | + info->y_min = ETP_YMIN_V1; |
---|
| 1789 | + info->x_max = ETP_XMAX_V1; |
---|
| 1790 | + info->y_max = ETP_YMAX_V1; |
---|
| 1791 | + break; |
---|
| 1792 | + |
---|
| 1793 | + case 2: |
---|
| 1794 | + if (info->fw_version == 0x020800 || |
---|
| 1795 | + info->fw_version == 0x020b00 || |
---|
| 1796 | + info->fw_version == 0x020030) { |
---|
| 1797 | + info->x_min = ETP_XMIN_V2; |
---|
| 1798 | + info->y_min = ETP_YMIN_V2; |
---|
| 1799 | + info->x_max = ETP_XMAX_V2; |
---|
| 1800 | + info->y_max = ETP_YMAX_V2; |
---|
| 1801 | + } else { |
---|
| 1802 | + int i; |
---|
| 1803 | + int fixed_dpi; |
---|
| 1804 | + |
---|
| 1805 | + i = (info->fw_version > 0x020800 && |
---|
| 1806 | + info->fw_version < 0x020900) ? 1 : 2; |
---|
| 1807 | + |
---|
| 1808 | + if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) |
---|
| 1809 | + return -EINVAL; |
---|
| 1810 | + |
---|
| 1811 | + fixed_dpi = param[1] & 0x10; |
---|
| 1812 | + |
---|
| 1813 | + if (((info->fw_version >> 16) == 0x14) && fixed_dpi) { |
---|
| 1814 | + if (info->send_cmd(psmouse, ETP_SAMPLE_QUERY, param)) |
---|
| 1815 | + return -EINVAL; |
---|
| 1816 | + |
---|
| 1817 | + info->x_max = (info->capabilities[1] - i) * param[1] / 2; |
---|
| 1818 | + info->y_max = (info->capabilities[2] - i) * param[2] / 2; |
---|
| 1819 | + } else if (info->fw_version == 0x040216) { |
---|
| 1820 | + info->x_max = 819; |
---|
| 1821 | + info->y_max = 405; |
---|
| 1822 | + } else if (info->fw_version == 0x040219 || info->fw_version == 0x040215) { |
---|
| 1823 | + info->x_max = 900; |
---|
| 1824 | + info->y_max = 500; |
---|
| 1825 | + } else { |
---|
| 1826 | + info->x_max = (info->capabilities[1] - i) * 64; |
---|
| 1827 | + info->y_max = (info->capabilities[2] - i) * 64; |
---|
| 1828 | + } |
---|
| 1829 | + } |
---|
| 1830 | + break; |
---|
| 1831 | + |
---|
| 1832 | + case 3: |
---|
| 1833 | + if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) |
---|
| 1834 | + return -EINVAL; |
---|
| 1835 | + |
---|
| 1836 | + info->x_max = (0x0f & param[0]) << 8 | param[1]; |
---|
| 1837 | + info->y_max = (0xf0 & param[0]) << 4 | param[2]; |
---|
| 1838 | + break; |
---|
| 1839 | + |
---|
| 1840 | + case 4: |
---|
| 1841 | + if (info->send_cmd(psmouse, ETP_FW_ID_QUERY, param)) |
---|
| 1842 | + return -EINVAL; |
---|
| 1843 | + |
---|
| 1844 | + info->x_max = (0x0f & param[0]) << 8 | param[1]; |
---|
| 1845 | + info->y_max = (0xf0 & param[0]) << 4 | param[2]; |
---|
| 1846 | + traces = info->capabilities[1]; |
---|
| 1847 | + if ((traces < 2) || (traces > info->x_max)) |
---|
| 1848 | + return -EINVAL; |
---|
| 1849 | + |
---|
| 1850 | + info->width = info->x_max / (traces - 1); |
---|
| 1851 | + |
---|
| 1852 | + /* column number of traces */ |
---|
| 1853 | + info->x_traces = traces; |
---|
| 1854 | + |
---|
| 1855 | + /* row number of traces */ |
---|
| 1856 | + traces = info->capabilities[2]; |
---|
| 1857 | + if ((traces >= 2) && (traces <= info->y_max)) |
---|
| 1858 | + info->y_traces = traces; |
---|
| 1859 | + |
---|
| 1860 | + break; |
---|
| 1861 | + } |
---|
| 1862 | + |
---|
| 1863 | + /* check for the middle button: DMI matching or new v4 firmwares */ |
---|
| 1864 | + info->has_middle_button = dmi_check_system(elantech_dmi_has_middle_button) || |
---|
| 1865 | + (ETP_NEW_IC_SMBUS_HOST_NOTIFY(info->fw_version) && |
---|
| 1866 | + !elantech_is_buttonpad(info)); |
---|
1772 | 1867 | |
---|
1773 | 1868 | return 0; |
---|
1774 | 1869 | } |
---|
.. | .. |
---|
1796 | 1891 | * These are known to not be working properly as bits are missing |
---|
1797 | 1892 | * in elan_i2c. |
---|
1798 | 1893 | */ |
---|
1799 | | - "LEN2131", /* ThinkPad P52 w/ NFC */ |
---|
1800 | | - "LEN2132", /* ThinkPad P52 */ |
---|
1801 | | - "LEN2133", /* ThinkPad P72 w/ NFC */ |
---|
1802 | | - "LEN2134", /* ThinkPad P72 */ |
---|
1803 | 1894 | NULL |
---|
1804 | 1895 | }; |
---|
1805 | 1896 | |
---|
.. | .. |
---|
1807 | 1898 | struct elantech_device_info *info, |
---|
1808 | 1899 | bool leave_breadcrumbs) |
---|
1809 | 1900 | { |
---|
1810 | | - const struct property_entry i2c_properties[] = { |
---|
1811 | | - PROPERTY_ENTRY_BOOL("elan,trackpoint"), |
---|
1812 | | - { }, |
---|
1813 | | - }; |
---|
| 1901 | + struct property_entry i2c_props[11] = {}; |
---|
1814 | 1902 | struct i2c_board_info smbus_board = { |
---|
1815 | 1903 | I2C_BOARD_INFO("elan_i2c", 0x15), |
---|
1816 | 1904 | .flags = I2C_CLIENT_HOST_NOTIFY, |
---|
1817 | 1905 | }; |
---|
| 1906 | + unsigned int idx = 0; |
---|
| 1907 | + |
---|
| 1908 | + smbus_board.properties = i2c_props; |
---|
| 1909 | + |
---|
| 1910 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-x", |
---|
| 1911 | + info->x_max + 1); |
---|
| 1912 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-size-y", |
---|
| 1913 | + info->y_max + 1); |
---|
| 1914 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-x", |
---|
| 1915 | + info->x_min); |
---|
| 1916 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-min-y", |
---|
| 1917 | + info->y_min); |
---|
| 1918 | + if (info->x_res) |
---|
| 1919 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-x-mm", |
---|
| 1920 | + (info->x_max + 1) / info->x_res); |
---|
| 1921 | + if (info->y_res) |
---|
| 1922 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("touchscreen-y-mm", |
---|
| 1923 | + (info->y_max + 1) / info->y_res); |
---|
1818 | 1924 | |
---|
1819 | 1925 | if (info->has_trackpoint) |
---|
1820 | | - smbus_board.properties = i2c_properties; |
---|
| 1926 | + i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,trackpoint"); |
---|
| 1927 | + |
---|
| 1928 | + if (info->has_middle_button) |
---|
| 1929 | + i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,middle-button"); |
---|
| 1930 | + |
---|
| 1931 | + if (info->x_traces) |
---|
| 1932 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,x_traces", |
---|
| 1933 | + info->x_traces); |
---|
| 1934 | + if (info->y_traces) |
---|
| 1935 | + i2c_props[idx++] = PROPERTY_ENTRY_U32("elan,y_traces", |
---|
| 1936 | + info->y_traces); |
---|
| 1937 | + |
---|
| 1938 | + if (elantech_is_buttonpad(info)) |
---|
| 1939 | + i2c_props[idx++] = PROPERTY_ENTRY_BOOL("elan,clickpad"); |
---|
1821 | 1940 | |
---|
1822 | 1941 | return psmouse_smbus_init(psmouse, &smbus_board, NULL, 0, false, |
---|
1823 | 1942 | leave_breadcrumbs); |
---|
.. | .. |
---|
1873 | 1992 | /* expected case */ |
---|
1874 | 1993 | break; |
---|
1875 | 1994 | case ETP_BUS_SMB_ALERT_ONLY: |
---|
1876 | | - /* fall-through */ |
---|
1877 | 1995 | case ETP_BUS_PS2_SMB_ALERT: |
---|
1878 | 1996 | psmouse_dbg(psmouse, "Ignoring SMBus provider through alert protocol.\n"); |
---|
1879 | 1997 | break; |
---|
1880 | 1998 | case ETP_BUS_SMB_HST_NTFY_ONLY: |
---|
1881 | | - /* fall-through */ |
---|
1882 | 1999 | case ETP_BUS_PS2_SMB_HST_NTFY: |
---|
1883 | 2000 | return true; |
---|
1884 | 2001 | default: |
---|
.. | .. |
---|
1893 | 2010 | int elantech_init_smbus(struct psmouse *psmouse) |
---|
1894 | 2011 | { |
---|
1895 | 2012 | struct elantech_device_info info; |
---|
1896 | | - int error = -EINVAL; |
---|
| 2013 | + int error; |
---|
1897 | 2014 | |
---|
1898 | 2015 | psmouse_reset(psmouse); |
---|
1899 | 2016 | |
---|
.. | .. |
---|
2011 | 2128 | int elantech_init_ps2(struct psmouse *psmouse) |
---|
2012 | 2129 | { |
---|
2013 | 2130 | struct elantech_device_info info; |
---|
2014 | | - int error = -EINVAL; |
---|
| 2131 | + int error; |
---|
2015 | 2132 | |
---|
2016 | 2133 | psmouse_reset(psmouse); |
---|
2017 | 2134 | |
---|
.. | .. |
---|
2032 | 2149 | int elantech_init(struct psmouse *psmouse) |
---|
2033 | 2150 | { |
---|
2034 | 2151 | struct elantech_device_info info; |
---|
2035 | | - int error = -EINVAL; |
---|
| 2152 | + int error; |
---|
2036 | 2153 | |
---|
2037 | 2154 | psmouse_reset(psmouse); |
---|
2038 | 2155 | |
---|