Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
0799a924 DD |
2 | /* |
3 | * Cypress Trackpad PS/2 mouse driver | |
4 | * | |
5 | * Copyright (c) 2012 Cypress Semiconductor Corporation. | |
6 | * | |
7 | * Author: | |
8 | * Dudley Du <dudl@cypress.com> | |
9 | * | |
10 | * Additional contributors include: | |
11 | * Kamal Mostafa <kamal@canonical.com> | |
12 | * Kyle Fazzari <git@status.e4ward.com> | |
0799a924 DD |
13 | */ |
14 | ||
0799a924 DD |
15 | #include <linux/module.h> |
16 | #include <linux/kernel.h> | |
17 | #include <linux/slab.h> | |
18 | #include <linux/serio.h> | |
19 | #include <linux/libps2.h> | |
20 | #include <linux/input.h> | |
21 | #include <linux/input/mt.h> | |
22 | #include <linux/sched.h> | |
23 | #include <linux/wait.h> | |
24 | ||
25 | #include "cypress_ps2.h" | |
26 | ||
27 | #undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */ | |
28 | ||
29 | static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n) | |
30 | { | |
31 | struct cytp_data *cytp = psmouse->private; | |
32 | cytp->pkt_size = n; | |
33 | } | |
34 | ||
5e13bea7 DT |
35 | static const u8 cytp_rate[] = {10, 20, 40, 60, 100, 200}; |
36 | static const u8 cytp_resolution[] = {0x00, 0x01, 0x02, 0x03}; | |
0799a924 | 37 | |
5e13bea7 | 38 | static int cypress_ps2_sendbyte(struct psmouse *psmouse, u8 cmd) |
0799a924 DD |
39 | { |
40 | struct ps2dev *ps2dev = &psmouse->ps2dev; | |
e8688b93 | 41 | int error; |
0799a924 | 42 | |
5e13bea7 | 43 | error = ps2_sendbyte(ps2dev, cmd, CYTP_CMD_TIMEOUT); |
e8688b93 | 44 | if (error) { |
0799a924 | 45 | psmouse_dbg(psmouse, |
e8688b93 | 46 | "sending command 0x%02x failed, resp 0x%02x, error %d\n", |
5e13bea7 | 47 | cmd, ps2dev->nak, error); |
e8688b93 | 48 | return error; |
0799a924 DD |
49 | } |
50 | ||
51 | #ifdef CYTP_DEBUG_VERBOSE | |
5e13bea7 | 52 | psmouse_dbg(psmouse, "sending command 0x%02x succeeded\n", cmd); |
0799a924 DD |
53 | #endif |
54 | ||
55 | return 0; | |
56 | } | |
57 | ||
5e13bea7 | 58 | static int cypress_ps2_ext_cmd(struct psmouse *psmouse, u8 prefix, u8 nibble) |
0799a924 DD |
59 | { |
60 | struct ps2dev *ps2dev = &psmouse->ps2dev; | |
61 | int tries = CYTP_PS2_CMD_TRIES; | |
62 | int rc; | |
63 | ||
64 | ps2_begin_command(ps2dev); | |
65 | ||
66 | do { | |
67 | /* | |
68 | * Send extension command byte (0xE8 or 0xF3). | |
69 | * If sending the command fails, send recovery command | |
70 | * to make the device return to the ready state. | |
71 | */ | |
5e13bea7 | 72 | rc = cypress_ps2_sendbyte(psmouse, prefix); |
e8688b93 | 73 | if (rc == -EAGAIN) { |
0799a924 | 74 | rc = cypress_ps2_sendbyte(psmouse, 0x00); |
e8688b93 | 75 | if (rc == -EAGAIN) |
0799a924 DD |
76 | rc = cypress_ps2_sendbyte(psmouse, 0x0a); |
77 | } | |
0799a924 | 78 | |
e8688b93 | 79 | if (!rc) { |
5e13bea7 | 80 | rc = cypress_ps2_sendbyte(psmouse, nibble); |
e8688b93 | 81 | if (rc == -EAGAIN) |
5e13bea7 | 82 | rc = cypress_ps2_sendbyte(psmouse, nibble); |
e8688b93 DT |
83 | |
84 | if (!rc) | |
85 | break; | |
86 | } | |
0799a924 DD |
87 | } while (--tries > 0); |
88 | ||
89 | ps2_end_command(ps2dev); | |
90 | ||
91 | return rc; | |
92 | } | |
93 | ||
5e13bea7 | 94 | static bool cypress_verify_cmd_state(struct psmouse *psmouse, u8 cmd, u8* param) |
0799a924 DD |
95 | { |
96 | bool rate_match = false; | |
97 | bool resolution_match = false; | |
98 | int i; | |
99 | ||
100 | /* callers will do further checking. */ | |
101 | if (cmd == CYTP_CMD_READ_CYPRESS_ID || | |
102 | cmd == CYTP_CMD_STANDARD_MODE || | |
103 | cmd == CYTP_CMD_READ_TP_METRICS) | |
104 | return true; | |
105 | ||
106 | if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID && | |
107 | (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) { | |
108 | for (i = 0; i < sizeof(cytp_resolution); i++) | |
109 | if (cytp_resolution[i] == param[1]) | |
110 | resolution_match = true; | |
111 | ||
112 | for (i = 0; i < sizeof(cytp_rate); i++) | |
113 | if (cytp_rate[i] == param[2]) | |
114 | rate_match = true; | |
115 | ||
116 | if (resolution_match && rate_match) | |
117 | return true; | |
118 | } | |
119 | ||
120 | psmouse_dbg(psmouse, "verify cmd state failed.\n"); | |
121 | return false; | |
122 | } | |
123 | ||
5e13bea7 | 124 | static int cypress_send_ext_cmd(struct psmouse *psmouse, u8 cmd, u8 *param) |
0799a924 | 125 | { |
5e13bea7 | 126 | u8 cmd_prefix = PSMOUSE_CMD_SETRES & 0xff; |
c472d33b DT |
127 | unsigned int resp_size = cmd == CYTP_CMD_READ_TP_METRICS ? 8 : 3; |
128 | unsigned int ps2_cmd = (PSMOUSE_CMD_GETINFO & 0xff) | (resp_size << 8); | |
0799a924 | 129 | int tries = CYTP_PS2_CMD_TRIES; |
5e13bea7 | 130 | int error; |
0799a924 DD |
131 | |
132 | psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n", | |
133 | cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd), | |
134 | DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd)); | |
135 | ||
136 | do { | |
5e13bea7 DT |
137 | cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_DD(cmd)); |
138 | cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_CC(cmd)); | |
139 | cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_BB(cmd)); | |
140 | cypress_ps2_ext_cmd(psmouse, cmd_prefix, DECODE_CMD_AA(cmd)); | |
141 | ||
c472d33b DT |
142 | error = ps2_command(&psmouse->ps2dev, param, ps2_cmd); |
143 | if (error) { | |
144 | psmouse_dbg(psmouse, "Command 0x%02x failed: %d\n", | |
145 | cmd, error); | |
146 | } else { | |
147 | psmouse_dbg(psmouse, | |
148 | "Command 0x%02x response data (0x): %*ph\n", | |
149 | cmd, resp_size, param); | |
150 | ||
151 | if (cypress_verify_cmd_state(psmouse, cmd, param)) | |
152 | return 0; | |
153 | } | |
0799a924 DD |
154 | } while (--tries > 0); |
155 | ||
156 | return -EIO; | |
157 | } | |
158 | ||
159 | int cypress_detect(struct psmouse *psmouse, bool set_properties) | |
160 | { | |
5e13bea7 | 161 | u8 param[3]; |
0799a924 DD |
162 | |
163 | if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) | |
164 | return -ENODEV; | |
165 | ||
166 | /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ | |
167 | if (param[0] != 0x33 || param[1] != 0xCC) | |
168 | return -ENODEV; | |
169 | ||
170 | if (set_properties) { | |
171 | psmouse->vendor = "Cypress"; | |
172 | psmouse->name = "Trackpad"; | |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
178 | static int cypress_read_fw_version(struct psmouse *psmouse) | |
179 | { | |
180 | struct cytp_data *cytp = psmouse->private; | |
5e13bea7 | 181 | u8 param[3]; |
0799a924 DD |
182 | |
183 | if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) | |
184 | return -ENODEV; | |
185 | ||
186 | /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ | |
187 | if (param[0] != 0x33 || param[1] != 0xCC) | |
188 | return -ENODEV; | |
189 | ||
190 | cytp->fw_version = param[2] & FW_VERSION_MASX; | |
191 | cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; | |
192 | ||
81bb5d31 KM |
193 | /* |
194 | * Trackpad fw_version 11 (in Dell XPS12) yields a bogus response to | |
195 | * CYTP_CMD_READ_TP_METRICS so do not try to use it. LP: #1103594. | |
196 | */ | |
197 | if (cytp->fw_version >= 11) | |
198 | cytp->tp_metrics_supported = 0; | |
199 | ||
0799a924 DD |
200 | psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); |
201 | psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", | |
202 | cytp->tp_metrics_supported); | |
203 | ||
204 | return 0; | |
205 | } | |
206 | ||
207 | static int cypress_read_tp_metrics(struct psmouse *psmouse) | |
208 | { | |
209 | struct cytp_data *cytp = psmouse->private; | |
5e13bea7 | 210 | u8 param[8]; |
0799a924 DD |
211 | |
212 | /* set default values for tp metrics. */ | |
213 | cytp->tp_width = CYTP_DEFAULT_WIDTH; | |
214 | cytp->tp_high = CYTP_DEFAULT_HIGH; | |
215 | cytp->tp_max_abs_x = CYTP_ABS_MAX_X; | |
216 | cytp->tp_max_abs_y = CYTP_ABS_MAX_Y; | |
217 | cytp->tp_min_pressure = CYTP_MIN_PRESSURE; | |
218 | cytp->tp_max_pressure = CYTP_MAX_PRESSURE; | |
219 | cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; | |
220 | cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; | |
221 | ||
81bb5d31 KM |
222 | if (!cytp->tp_metrics_supported) |
223 | return 0; | |
224 | ||
0799a924 DD |
225 | memset(param, 0, sizeof(param)); |
226 | if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) { | |
227 | /* Update trackpad parameters. */ | |
228 | cytp->tp_max_abs_x = (param[1] << 8) | param[0]; | |
229 | cytp->tp_max_abs_y = (param[3] << 8) | param[2]; | |
230 | cytp->tp_min_pressure = param[4]; | |
231 | cytp->tp_max_pressure = param[5]; | |
232 | } | |
233 | ||
234 | if (!cytp->tp_max_pressure || | |
235 | cytp->tp_max_pressure < cytp->tp_min_pressure || | |
236 | !cytp->tp_width || !cytp->tp_high || | |
237 | !cytp->tp_max_abs_x || | |
238 | cytp->tp_max_abs_x < cytp->tp_width || | |
239 | !cytp->tp_max_abs_y || | |
240 | cytp->tp_max_abs_y < cytp->tp_high) | |
241 | return -EINVAL; | |
242 | ||
243 | cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; | |
244 | cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; | |
245 | ||
246 | #ifdef CYTP_DEBUG_VERBOSE | |
247 | psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); | |
248 | psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); | |
249 | psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); | |
250 | psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); | |
251 | psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); | |
252 | psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); | |
253 | psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); | |
254 | psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); | |
255 | psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); | |
256 | ||
257 | psmouse_dbg(psmouse, "tp_type_APA = %d\n", | |
258 | (param[6] & TP_METRICS_BIT_APA) ? 1 : 0); | |
259 | psmouse_dbg(psmouse, "tp_type_MTG = %d\n", | |
260 | (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); | |
261 | psmouse_dbg(psmouse, "tp_palm = %d\n", | |
262 | (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); | |
263 | psmouse_dbg(psmouse, "tp_stubborn = %d\n", | |
264 | (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); | |
265 | psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", | |
266 | (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); | |
267 | psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", | |
268 | (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); | |
269 | psmouse_dbg(psmouse, "tp_1f_spike = %d\n", | |
270 | param[7] & TP_METRICS_BIT_1F_SPIKE); | |
271 | psmouse_dbg(psmouse, "tp_2f_spike = %d\n", | |
272 | (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); | |
273 | psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", | |
274 | (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); | |
275 | #endif | |
276 | ||
277 | return 0; | |
278 | } | |
279 | ||
280 | static int cypress_query_hardware(struct psmouse *psmouse) | |
281 | { | |
93f25f92 | 282 | int error; |
0799a924 | 283 | |
93f25f92 DT |
284 | error = cypress_read_fw_version(psmouse); |
285 | if (error) | |
286 | return error; | |
0799a924 | 287 | |
93f25f92 DT |
288 | error = cypress_read_tp_metrics(psmouse); |
289 | if (error) | |
290 | return error; | |
0799a924 DD |
291 | |
292 | return 0; | |
293 | } | |
294 | ||
295 | static int cypress_set_absolute_mode(struct psmouse *psmouse) | |
296 | { | |
297 | struct cytp_data *cytp = psmouse->private; | |
5e13bea7 | 298 | u8 param[3]; |
93f25f92 | 299 | int error; |
0799a924 | 300 | |
93f25f92 DT |
301 | error = cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, |
302 | param); | |
303 | if (error) | |
304 | return error; | |
0799a924 DD |
305 | |
306 | cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) | |
307 | | CYTP_BIT_ABS_PRESSURE; | |
308 | cypress_set_packet_size(psmouse, 5); | |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
313 | /* | |
314 | * Reset trackpad device. | |
315 | * This is also the default mode when trackpad powered on. | |
316 | */ | |
317 | static void cypress_reset(struct psmouse *psmouse) | |
318 | { | |
319 | struct cytp_data *cytp = psmouse->private; | |
320 | ||
321 | cytp->mode = 0; | |
322 | ||
323 | psmouse_reset(psmouse); | |
324 | } | |
325 | ||
326 | static int cypress_set_input_params(struct input_dev *input, | |
327 | struct cytp_data *cytp) | |
328 | { | |
93f25f92 | 329 | int error; |
0799a924 DD |
330 | |
331 | if (!cytp->tp_res_x || !cytp->tp_res_y) | |
332 | return -EINVAL; | |
333 | ||
334 | __set_bit(EV_ABS, input->evbit); | |
335 | input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0); | |
336 | input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0); | |
337 | input_set_abs_params(input, ABS_PRESSURE, | |
338 | cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0); | |
339 | input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0); | |
340 | ||
341 | /* finger position */ | |
342 | input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0); | |
343 | input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0); | |
344 | input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); | |
345 | ||
93f25f92 DT |
346 | error = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS, |
347 | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK); | |
348 | if (error) | |
349 | return error; | |
0799a924 DD |
350 | |
351 | __set_bit(INPUT_PROP_SEMI_MT, input->propbit); | |
352 | ||
353 | input_abs_set_res(input, ABS_X, cytp->tp_res_x); | |
354 | input_abs_set_res(input, ABS_Y, cytp->tp_res_y); | |
355 | ||
356 | input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x); | |
357 | input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y); | |
358 | ||
359 | __set_bit(BTN_TOUCH, input->keybit); | |
360 | __set_bit(BTN_TOOL_FINGER, input->keybit); | |
361 | __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); | |
362 | __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); | |
363 | __set_bit(BTN_TOOL_QUADTAP, input->keybit); | |
364 | __set_bit(BTN_TOOL_QUINTTAP, input->keybit); | |
365 | ||
366 | __clear_bit(EV_REL, input->evbit); | |
367 | __clear_bit(REL_X, input->relbit); | |
368 | __clear_bit(REL_Y, input->relbit); | |
369 | ||
0799a924 DD |
370 | __set_bit(EV_KEY, input->evbit); |
371 | __set_bit(BTN_LEFT, input->keybit); | |
372 | __set_bit(BTN_RIGHT, input->keybit); | |
373 | __set_bit(BTN_MIDDLE, input->keybit); | |
374 | ||
0799a924 DD |
375 | return 0; |
376 | } | |
377 | ||
5e13bea7 | 378 | static int cypress_get_finger_count(u8 header_byte) |
0799a924 | 379 | { |
5e13bea7 | 380 | u8 bits6_7; |
0799a924 DD |
381 | int finger_count; |
382 | ||
383 | bits6_7 = header_byte >> 6; | |
384 | finger_count = bits6_7 & 0x03; | |
385 | ||
386 | if (finger_count == 1) | |
387 | return 1; | |
388 | ||
389 | if (header_byte & ABS_HSCROLL_BIT) { | |
390 | /* HSCROLL gets added on to 0 finger count. */ | |
391 | switch (finger_count) { | |
392 | case 0: return 4; | |
393 | case 2: return 5; | |
394 | default: | |
395 | /* Invalid contact (e.g. palm). Ignore it. */ | |
5df682b2 | 396 | return 0; |
0799a924 DD |
397 | } |
398 | } | |
399 | ||
400 | return finger_count; | |
401 | } | |
402 | ||
403 | ||
404 | static int cypress_parse_packet(struct psmouse *psmouse, | |
5e13bea7 DT |
405 | struct cytp_data *cytp, |
406 | struct cytp_report_data *report_data) | |
0799a924 | 407 | { |
5e13bea7 DT |
408 | u8 *packet = psmouse->packet; |
409 | u8 header_byte = packet[0]; | |
0799a924 DD |
410 | |
411 | memset(report_data, 0, sizeof(struct cytp_report_data)); | |
412 | ||
5df682b2 | 413 | report_data->contact_cnt = cypress_get_finger_count(header_byte); |
0799a924 DD |
414 | report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; |
415 | ||
416 | if (report_data->contact_cnt == 1) { | |
417 | report_data->contacts[0].x = | |
418 | ((packet[1] & 0x70) << 4) | packet[2]; | |
419 | report_data->contacts[0].y = | |
420 | ((packet[1] & 0x07) << 8) | packet[3]; | |
421 | if (cytp->mode & CYTP_BIT_ABS_PRESSURE) | |
422 | report_data->contacts[0].z = packet[4]; | |
423 | ||
424 | } else if (report_data->contact_cnt >= 2) { | |
425 | report_data->contacts[0].x = | |
426 | ((packet[1] & 0x70) << 4) | packet[2]; | |
427 | report_data->contacts[0].y = | |
428 | ((packet[1] & 0x07) << 8) | packet[3]; | |
429 | if (cytp->mode & CYTP_BIT_ABS_PRESSURE) | |
430 | report_data->contacts[0].z = packet[4]; | |
431 | ||
432 | report_data->contacts[1].x = | |
433 | ((packet[5] & 0xf0) << 4) | packet[6]; | |
434 | report_data->contacts[1].y = | |
435 | ((packet[5] & 0x0f) << 8) | packet[7]; | |
436 | if (cytp->mode & CYTP_BIT_ABS_PRESSURE) | |
437 | report_data->contacts[1].z = report_data->contacts[0].z; | |
438 | } | |
439 | ||
440 | report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; | |
441 | report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0; | |
442 | ||
443 | /* | |
444 | * This is only true if one of the mouse buttons were tapped. Make | |
445 | * sure it doesn't turn into a click. The regular tap-to-click | |
446 | * functionality will handle that on its own. If we don't do this, | |
447 | * disabling tap-to-click won't affect the mouse button zones. | |
448 | */ | |
449 | if (report_data->tap) | |
450 | report_data->left = 0; | |
451 | ||
452 | #ifdef CYTP_DEBUG_VERBOSE | |
453 | { | |
454 | int i; | |
455 | int n = report_data->contact_cnt; | |
456 | psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); | |
457 | psmouse_dbg(psmouse, "contact_cnt = %d\n", | |
458 | report_data->contact_cnt); | |
459 | if (n > CYTP_MAX_MT_SLOTS) | |
460 | n = CYTP_MAX_MT_SLOTS; | |
461 | for (i = 0; i < n; i++) | |
462 | psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, | |
463 | report_data->contacts[i].x, | |
464 | report_data->contacts[i].y, | |
465 | report_data->contacts[i].z); | |
466 | psmouse_dbg(psmouse, "left = %d\n", report_data->left); | |
467 | psmouse_dbg(psmouse, "right = %d\n", report_data->right); | |
468 | psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); | |
469 | } | |
470 | #endif | |
471 | ||
472 | return 0; | |
473 | } | |
474 | ||
475 | static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) | |
476 | { | |
477 | int i; | |
478 | struct input_dev *input = psmouse->dev; | |
479 | struct cytp_data *cytp = psmouse->private; | |
480 | struct cytp_report_data report_data; | |
481 | struct cytp_contact *contact; | |
482 | struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; | |
483 | int slots[CYTP_MAX_MT_SLOTS]; | |
484 | int n; | |
485 | ||
5df682b2 | 486 | cypress_parse_packet(psmouse, cytp, &report_data); |
0799a924 DD |
487 | |
488 | n = report_data.contact_cnt; | |
0799a924 DD |
489 | if (n > CYTP_MAX_MT_SLOTS) |
490 | n = CYTP_MAX_MT_SLOTS; | |
491 | ||
492 | for (i = 0; i < n; i++) { | |
493 | contact = &report_data.contacts[i]; | |
494 | pos[i].x = contact->x; | |
495 | pos[i].y = contact->y; | |
496 | } | |
497 | ||
448c7f38 | 498 | input_mt_assign_slots(input, slots, pos, n, 0); |
0799a924 DD |
499 | |
500 | for (i = 0; i < n; i++) { | |
501 | contact = &report_data.contacts[i]; | |
502 | input_mt_slot(input, slots[i]); | |
503 | input_mt_report_slot_state(input, MT_TOOL_FINGER, true); | |
504 | input_report_abs(input, ABS_MT_POSITION_X, contact->x); | |
505 | input_report_abs(input, ABS_MT_POSITION_Y, contact->y); | |
506 | input_report_abs(input, ABS_MT_PRESSURE, contact->z); | |
507 | } | |
508 | ||
509 | input_mt_sync_frame(input); | |
510 | ||
511 | input_mt_report_finger_count(input, report_data.contact_cnt); | |
512 | ||
513 | input_report_key(input, BTN_LEFT, report_data.left); | |
514 | input_report_key(input, BTN_RIGHT, report_data.right); | |
515 | input_report_key(input, BTN_MIDDLE, report_data.middle); | |
516 | ||
517 | input_sync(input); | |
518 | } | |
519 | ||
520 | static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) | |
521 | { | |
522 | int contact_cnt; | |
523 | int index = psmouse->pktcnt - 1; | |
5e13bea7 | 524 | u8 *packet = psmouse->packet; |
0799a924 DD |
525 | struct cytp_data *cytp = psmouse->private; |
526 | ||
527 | if (index < 0 || index > cytp->pkt_size) | |
528 | return PSMOUSE_BAD_DATA; | |
529 | ||
530 | if (index == 0 && (packet[0] & 0xfc) == 0) { | |
531 | /* call packet process for reporting finger leave. */ | |
532 | cypress_process_packet(psmouse, 1); | |
533 | return PSMOUSE_FULL_PACKET; | |
534 | } | |
535 | ||
536 | /* | |
537 | * Perform validation (and adjust packet size) based only on the | |
538 | * first byte; allow all further bytes through. | |
539 | */ | |
540 | if (index != 0) | |
541 | return PSMOUSE_GOOD_DATA; | |
542 | ||
543 | /* | |
544 | * If absolute/relative mode bit has not been set yet, just pass | |
545 | * the byte through. | |
546 | */ | |
547 | if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) | |
548 | return PSMOUSE_GOOD_DATA; | |
549 | ||
550 | if ((packet[0] & 0x08) == 0x08) | |
551 | return PSMOUSE_BAD_DATA; | |
552 | ||
553 | contact_cnt = cypress_get_finger_count(packet[0]); | |
0799a924 DD |
554 | if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) |
555 | cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); | |
556 | else | |
557 | cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5); | |
558 | ||
559 | return PSMOUSE_GOOD_DATA; | |
560 | } | |
561 | ||
562 | static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse) | |
563 | { | |
564 | struct cytp_data *cytp = psmouse->private; | |
565 | ||
566 | if (psmouse->pktcnt >= cytp->pkt_size) { | |
567 | cypress_process_packet(psmouse, 0); | |
568 | return PSMOUSE_FULL_PACKET; | |
569 | } | |
570 | ||
571 | return cypress_validate_byte(psmouse); | |
572 | } | |
573 | ||
574 | static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate) | |
575 | { | |
576 | struct cytp_data *cytp = psmouse->private; | |
c1a33900 | 577 | u8 rate_param; |
0799a924 DD |
578 | |
579 | if (rate >= 80) { | |
580 | psmouse->rate = 80; | |
581 | cytp->mode |= CYTP_BIT_HIGH_RATE; | |
582 | } else { | |
583 | psmouse->rate = 40; | |
584 | cytp->mode &= ~CYTP_BIT_HIGH_RATE; | |
585 | } | |
586 | ||
c1a33900 DT |
587 | rate_param = (u8)rate; |
588 | ps2_command(&psmouse->ps2dev, &rate_param, PSMOUSE_CMD_SETRATE); | |
0799a924 DD |
589 | } |
590 | ||
591 | static void cypress_disconnect(struct psmouse *psmouse) | |
592 | { | |
593 | cypress_reset(psmouse); | |
594 | kfree(psmouse->private); | |
595 | psmouse->private = NULL; | |
596 | } | |
597 | ||
598 | static int cypress_reconnect(struct psmouse *psmouse) | |
599 | { | |
600 | int tries = CYTP_PS2_CMD_TRIES; | |
93f25f92 | 601 | int error; |
0799a924 DD |
602 | |
603 | do { | |
604 | cypress_reset(psmouse); | |
93f25f92 DT |
605 | error = cypress_detect(psmouse, false); |
606 | } while (error && (--tries > 0)); | |
0799a924 | 607 | |
93f25f92 | 608 | if (error) { |
0799a924 | 609 | psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n"); |
93f25f92 | 610 | return error; |
0799a924 DD |
611 | } |
612 | ||
93f25f92 DT |
613 | error = cypress_set_absolute_mode(psmouse); |
614 | if (error) { | |
0799a924 | 615 | psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); |
93f25f92 | 616 | return error; |
0799a924 DD |
617 | } |
618 | ||
619 | return 0; | |
620 | } | |
621 | ||
622 | int cypress_init(struct psmouse *psmouse) | |
623 | { | |
624 | struct cytp_data *cytp; | |
93f25f92 | 625 | int error; |
0799a924 | 626 | |
dc2f1423 | 627 | cytp = kzalloc(sizeof(*cytp), GFP_KERNEL); |
c2e60959 | 628 | if (!cytp) |
0799a924 DD |
629 | return -ENOMEM; |
630 | ||
c2e60959 | 631 | psmouse->private = cytp; |
0799a924 DD |
632 | psmouse->pktsize = 8; |
633 | ||
c2e60959 GB |
634 | cypress_reset(psmouse); |
635 | ||
93f25f92 DT |
636 | error = cypress_query_hardware(psmouse); |
637 | if (error) { | |
0799a924 DD |
638 | psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); |
639 | goto err_exit; | |
640 | } | |
641 | ||
93f25f92 DT |
642 | error = cypress_set_absolute_mode(psmouse); |
643 | if (error) { | |
0799a924 DD |
644 | psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n"); |
645 | goto err_exit; | |
646 | } | |
647 | ||
93f25f92 DT |
648 | error = cypress_set_input_params(psmouse->dev, cytp); |
649 | if (error) { | |
0799a924 DD |
650 | psmouse_err(psmouse, "init: Unable to set input params.\n"); |
651 | goto err_exit; | |
652 | } | |
653 | ||
654 | psmouse->model = 1; | |
655 | psmouse->protocol_handler = cypress_protocol_handler; | |
656 | psmouse->set_rate = cypress_set_rate; | |
657 | psmouse->disconnect = cypress_disconnect; | |
658 | psmouse->reconnect = cypress_reconnect; | |
659 | psmouse->cleanup = cypress_reset; | |
660 | psmouse->resync_time = 0; | |
661 | ||
662 | return 0; | |
663 | ||
664 | err_exit: | |
665 | /* | |
666 | * Reset Cypress Trackpad as a standard mouse. Then | |
fabcf4d8 | 667 | * let psmouse driver communicating with it as default PS2 mouse. |
0799a924 DD |
668 | */ |
669 | cypress_reset(psmouse); | |
670 | ||
671 | psmouse->private = NULL; | |
672 | kfree(cytp); | |
673 | ||
93f25f92 | 674 | return error; |
0799a924 | 675 | } |