aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDudley Du <dudl@cypress.com>2015-01-17 21:49:37 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-01-18 03:10:28 -0500
commit6972a859601ab295f0873762d333ee1449152245 (patch)
treee93975e5f1abcafaef9da0a0c157b89e4acf7e72
parent9f1cd857518f29e1cb77c56c32354775994b27a6 (diff)
Input: cyapa - add gen5 trackpad device basic functions support
This change adds support for Gen5 Cypress trackpads. The driver detects generation of the device at probe time and automatically selects appropriate protocol. Signed-off-by: Dudley Du <dudl@cypress.com> Tested-by: Jeremiah Mahler <jmmahler@gmail.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r--drivers/input/mouse/Makefile2
-rw-r--r--drivers/input/mouse/cyapa.c13
-rw-r--r--drivers/input/mouse/cyapa.h1
-rw-r--r--drivers/input/mouse/cyapa_gen5.c1678
4 files changed, 1693 insertions, 1 deletions
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 8bd950d52d5c..8a9c98e76d9c 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -24,7 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
24obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o 24obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
25obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o 25obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
26 26
27cyapatp-objs := cyapa.o cyapa_gen3.o 27cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o
28psmouse-objs := psmouse-base.o synaptics.o focaltech.o 28psmouse-objs := psmouse-base.o synaptics.o focaltech.o
29 29
30psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o 30psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index 36c64335662c..fa4d5010534d 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -184,6 +184,14 @@ static int cyapa_get_state(struct cyapa *cyapa)
184 if (!error) 184 if (!error)
185 goto out_detected; 185 goto out_detected;
186 } 186 }
187 if ((cyapa->gen == CYAPA_GEN_UNKNOWN ||
188 cyapa->gen == CYAPA_GEN5) &&
189 !smbus && even_addr) {
190 error = cyapa_gen5_ops.state_parse(cyapa,
191 status, BL_STATUS_SIZE);
192 if (!error)
193 goto out_detected;
194 }
187 195
188 /* 196 /*
189 * Write 0x00 0x00 to trackpad device to force update its 197 * Write 0x00 0x00 to trackpad device to force update its
@@ -272,6 +280,9 @@ static int cyapa_check_is_operational(struct cyapa *cyapa)
272 return error; 280 return error;
273 281
274 switch (cyapa->gen) { 282 switch (cyapa->gen) {
283 case CYAPA_GEN5:
284 cyapa->ops = &cyapa_gen5_ops;
285 break;
275 case CYAPA_GEN3: 286 case CYAPA_GEN3:
276 cyapa->ops = &cyapa_gen3_ops; 287 cyapa->ops = &cyapa_gen3_ops;
277 break; 288 break;
@@ -506,6 +517,8 @@ static int cyapa_initialize(struct cyapa *cyapa)
506 517
507 /* ops.initialize() is aimed to prepare for module communications. */ 518 /* ops.initialize() is aimed to prepare for module communications. */
508 error = cyapa_gen3_ops.initialize(cyapa); 519 error = cyapa_gen3_ops.initialize(cyapa);
520 if (!error)
521 error = cyapa_gen5_ops.initialize(cyapa);
509 if (error) 522 if (error)
510 return error; 523 return error;
511 524
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index aab19b7d9357..481a60d51efd 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -292,5 +292,6 @@ u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
292 292
293extern const char product_id[]; 293extern const char product_id[];
294extern const struct cyapa_dev_ops cyapa_gen3_ops; 294extern const struct cyapa_dev_ops cyapa_gen3_ops;
295extern const struct cyapa_dev_ops cyapa_gen5_ops;
295 296
296#endif 297#endif
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
new file mode 100644
index 000000000000..a049ae383027
--- /dev/null
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -0,0 +1,1678 @@
1/*
2 * Cypress APA trackpad with I2C interface
3 *
4 * Author: Dudley Du <dudl@cypress.com>
5 *
6 * Copyright (C) 2014 Cypress Semiconductor, Inc.
7 *
8 * This file is subject to the terms and conditions of the GNU General Public
9 * License. See the file COPYING in the main directory of this archive for
10 * more details.
11 */
12
13#include <linux/delay.h>
14#include <linux/i2c.h>
15#include <linux/input.h>
16#include <linux/input/mt.h>
17#include <linux/mutex.h>
18#include <linux/completion.h>
19#include <linux/slab.h>
20#include <linux/unaligned/access_ok.h>
21#include "cyapa.h"
22
23
24/* Macro of Gen5 */
25#define RECORD_EVENT_NONE 0
26#define RECORD_EVENT_TOUCHDOWN 1
27#define RECORD_EVENT_DISPLACE 2
28#define RECORD_EVENT_LIFTOFF 3
29
30#define CYAPA_TSG_FLASH_MAP_BLOCK_SIZE 0x80
31#define CYAPA_TSG_IMG_FW_HDR_SIZE 13
32#define CYAPA_TSG_FW_ROW_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE)
33#define CYAPA_TSG_IMG_START_ROW_NUM 0x002e
34#define CYAPA_TSG_IMG_END_ROW_NUM 0x01fe
35#define CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM 0x01ff
36#define CYAPA_TSG_IMG_MAX_RECORDS (CYAPA_TSG_IMG_END_ROW_NUM - \
37 CYAPA_TSG_IMG_START_ROW_NUM + 1 + 1)
38#define CYAPA_TSG_IMG_READ_SIZE (CYAPA_TSG_FLASH_MAP_BLOCK_SIZE / 2)
39#define CYAPA_TSG_START_OF_APPLICATION 0x1700
40#define CYAPA_TSG_APP_INTEGRITY_SIZE 60
41#define CYAPA_TSG_FLASH_MAP_METADATA_SIZE 60
42#define CYAPA_TSG_BL_KEY_SIZE 8
43
44#define CYAPA_TSG_MAX_CMD_SIZE 256
45
46#define GEN5_BL_CMD_VERIFY_APP_INTEGRITY 0x31
47#define GEN5_BL_CMD_GET_BL_INFO 0x38
48#define GEN5_BL_CMD_PROGRAM_VERIFY_ROW 0x39
49#define GEN5_BL_CMD_LAUNCH_APP 0x3b
50#define GEN5_BL_CMD_INITIATE_BL 0x48
51
52#define GEN5_HID_DESCRIPTOR_ADDR 0x0001
53#define GEN5_REPORT_DESCRIPTOR_ADDR 0x0002
54#define GEN5_INPUT_REPORT_ADDR 0x0003
55#define GEN5_OUTPUT_REPORT_ADDR 0x0004
56#define GEN5_CMD_DATA_ADDR 0x0006
57
58#define GEN5_TOUCH_REPORT_HEAD_SIZE 7
59#define GEN5_TOUCH_REPORT_MAX_SIZE 127
60#define GEN5_BTN_REPORT_HEAD_SIZE 6
61#define GEN5_BTN_REPORT_MAX_SIZE 14
62#define GEN5_WAKEUP_EVENT_SIZE 4
63#define GEN5_RAW_DATA_HEAD_SIZE 24
64
65#define GEN5_BL_CMD_REPORT_ID 0x40
66#define GEN5_BL_RESP_REPORT_ID 0x30
67#define GEN5_APP_CMD_REPORT_ID 0x2f
68#define GEN5_APP_RESP_REPORT_ID 0x1f
69
70#define GEN5_APP_DEEP_SLEEP_REPORT_ID 0xf0
71#define GEN5_DEEP_SLEEP_RESP_LENGTH 5
72
73#define GEN5_CMD_GET_PARAMETER 0x05
74#define GEN5_CMD_SET_PARAMETER 0x06
75#define GEN5_PARAMETER_ACT_INTERVL_ID 0x4d
76#define GEN5_PARAMETER_ACT_INTERVL_SIZE 1
77#define GEN5_PARAMETER_ACT_LFT_INTERVL_ID 0x4f
78#define GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE 2
79#define GEN5_PARAMETER_LP_INTRVL_ID 0x4c
80#define GEN5_PARAMETER_LP_INTRVL_SIZE 2
81
82#define GEN5_PARAMETER_DISABLE_PIP_REPORT 0x08
83
84#define GEN5_POWER_STATE_ACTIVE 0x01
85#define GEN5_POWER_STATE_LOOK_FOR_TOUCH 0x02
86#define GEN5_POWER_STATE_READY 0x03
87#define GEN5_POWER_STATE_IDLE 0x04
88#define GEN5_POWER_STATE_BTN_ONLY 0x05
89#define GEN5_POWER_STATE_OFF 0x06
90
91#define GEN5_DEEP_SLEEP_STATE_MASK 0x03
92#define GEN5_DEEP_SLEEP_STATE_ON 0x00
93#define GEN5_DEEP_SLEEP_STATE_OFF 0x01
94
95#define GEN5_DEEP_SLEEP_OPCODE 0x08
96#define GEN5_DEEP_SLEEP_OPCODE_MASK 0x0f
97
98#define GEN5_POWER_READY_MAX_INTRVL_TIME 50 /* Unit: ms */
99#define GEN5_POWER_IDLE_MAX_INTRVL_TIME 250 /* Unit: ms */
100
101#define GEN5_CMD_REPORT_ID_OFFSET 4
102
103#define GEN5_RESP_REPORT_ID_OFFSET 2
104#define GEN5_RESP_RSVD_OFFSET 3
105#define GEN5_RESP_RSVD_KEY 0x00
106#define GEN5_RESP_BL_SOP_OFFSET 4
107#define GEN5_SOP_KEY 0x01 /* Start of Packet */
108#define GEN5_EOP_KEY 0x17 /* End of Packet */
109#define GEN5_RESP_APP_CMD_OFFSET 4
110#define GET_GEN5_CMD_CODE(reg) ((reg) & 0x7f)
111
112#define VALID_CMD_RESP_HEADER(resp, cmd) \
113 (((resp)[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID) && \
114 ((resp)[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) && \
115 (GET_GEN5_CMD_CODE((resp)[GEN5_RESP_APP_CMD_OFFSET]) == (cmd)))
116
117#define GEN5_MIN_BL_CMD_LENGTH 13
118#define GEN5_MIN_BL_RESP_LENGTH 11
119#define GEN5_MIN_APP_CMD_LENGTH 7
120#define GEN5_MIN_APP_RESP_LENGTH 5
121#define GEN5_UNSUPPORTED_CMD_RESP_LENGTH 6
122
123#define GEN5_RESP_LENGTH_OFFSET 0x00
124#define GEN5_RESP_LENGTH_SIZE 2
125
126#define GEN5_HID_DESCRIPTOR_SIZE 32
127#define GEN5_BL_HID_REPORT_ID 0xff
128#define GEN5_APP_HID_REPORT_ID 0xf7
129#define GEN5_BL_MAX_OUTPUT_LENGTH 0x0100
130#define GEN5_APP_MAX_OUTPUT_LENGTH 0x00fe
131
132#define GEN5_BL_REPORT_DESCRIPTOR_SIZE 0x1d
133#define GEN5_BL_REPORT_DESCRIPTOR_ID 0xfe
134#define GEN5_APP_REPORT_DESCRIPTOR_SIZE 0xee
135#define GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE 0xfa
136#define GEN5_APP_REPORT_DESCRIPTOR_ID 0xf6
137
138#define GEN5_TOUCH_REPORT_ID 0x01
139#define GEN5_BTN_REPORT_ID 0x03
140#define GEN5_WAKEUP_EVENT_REPORT_ID 0x04
141#define GEN5_OLD_PUSH_BTN_REPORT_ID 0x05
142#define GEN5_PUSH_BTN_REPORT_ID 0x06
143
144#define GEN5_CMD_COMPLETE_SUCCESS(status) ((status) == 0x00)
145
146#define GEN5_BL_INITIATE_RESP_LEN 11
147#define GEN5_BL_FAIL_EXIT_RESP_LEN 11
148#define GEN5_BL_FAIL_EXIT_STATUS_CODE 0x0c
149#define GEN5_BL_VERIFY_INTEGRITY_RESP_LEN 12
150#define GEN5_BL_INTEGRITY_CHEKC_PASS 0x00
151#define GEN5_BL_BLOCK_WRITE_RESP_LEN 11
152#define GEN5_BL_READ_APP_INFO_RESP_LEN 31
153#define GEN5_CMD_CALIBRATE 0x28
154#define CYAPA_SENSING_MODE_MUTUAL_CAP_FINE 0x00
155#define CYAPA_SENSING_MODE_SELF_CAP 0x02
156
157#define GEN5_CMD_RETRIEVE_DATA_STRUCTURE 0x24
158#define GEN5_RETRIEVE_MUTUAL_PWC_DATA 0x00
159#define GEN5_RETRIEVE_SELF_CAP_PWC_DATA 0x01
160
161#define GEN5_RETRIEVE_DATA_ELEMENT_SIZE_MASK 0x07
162
163#define GEN5_CMD_EXECUTE_PANEL_SCAN 0x2a
164#define GEN5_CMD_RETRIEVE_PANEL_SCAN 0x2b
165#define GEN5_PANEL_SCAN_MUTUAL_RAW_DATA 0x00
166#define GEN5_PANEL_SCAN_MUTUAL_BASELINE 0x01
167#define GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT 0x02
168#define GEN5_PANEL_SCAN_SELF_RAW_DATA 0x03
169#define GEN5_PANEL_SCAN_SELF_BASELINE 0x04
170#define GEN5_PANEL_SCAN_SELF_DIFFCOUNT 0x05
171
172/* The offset only valid for reterive PWC and panel scan commands */
173#define GEN5_RESP_DATA_STRUCTURE_OFFSET 10
174#define GEN5_PWC_DATA_ELEMENT_SIZE_MASK 0x07
175
176#define GEN5_NUMBER_OF_TOUCH_OFFSET 5
177#define GEN5_NUMBER_OF_TOUCH_MASK 0x1f
178#define GEN5_BUTTONS_OFFSET 5
179#define GEN5_BUTTONS_MASK 0x0f
180#define GEN5_GET_EVENT_ID(reg) (((reg) >> 5) & 0x03)
181#define GEN5_GET_TOUCH_ID(reg) ((reg) & 0x1f)
182
183#define GEN5_PRODUCT_FAMILY_MASK 0xf000
184#define GEN5_PRODUCT_FAMILY_TRACKPAD 0x1000
185
186#define TSG_INVALID_CMD 0xff
187
188struct cyapa_gen5_touch_record {
189 /*
190 * Bit 7 - 3: reserved
191 * Bit 2 - 0: touch type;
192 * 0 : standard finger;
193 * 1 - 15 : reserved.
194 */
195 u8 touch_type;
196
197 /*
198 * Bit 7: indicates touch liftoff status.
199 * 0 : touch is currently on the panel.
200 * 1 : touch record indicates a liftoff.
201 * Bit 6 - 5: indicates an event associated with this touch instance
202 * 0 : no event
203 * 1 : touchdown
204 * 2 : significant displacement (> active distance)
205 * 3 : liftoff (record reports last known coordinates)
206 * Bit 4 - 0: An arbitrary ID tag associated with a finger
207 * to allow tracking a touch as it moves around the panel.
208 */
209 u8 touch_tip_event_id;
210
211 /* Bit 7 - 0 of X-axis coordinate of the touch in pixel. */
212 u8 x_lo;
213
214 /* Bit 15 - 8 of X-axis coordinate of the touch in pixel. */
215 u8 x_hi;
216
217 /* Bit 7 - 0 of Y-axis coordinate of the touch in pixel. */
218 u8 y_lo;
219
220 /* Bit 15 - 8 of Y-axis coordinate of the touch in pixel. */
221 u8 y_hi;
222
223 /* Touch intensity in counts, pressure value. */
224 u8 z;
225
226 /*
227 * The length of the major axis of the ellipse of contact between
228 * the finger and the panel (ABS_MT_TOUCH_MAJOR).
229 */
230 u8 major_axis_len;
231
232 /*
233 * The length of the minor axis of the ellipse of contact between
234 * the finger and the panel (ABS_MT_TOUCH_MINOR).
235 */
236 u8 minor_axis_len;
237
238 /*
239 * The length of the major axis of the approaching tool.
240 * (ABS_MT_WIDTH_MAJOR)
241 */
242 u8 major_tool_len;
243
244 /*
245 * The length of the minor axis of the approaching tool.
246 * (ABS_MT_WIDTH_MINOR)
247 */
248 u8 minor_tool_len;
249
250 /*
251 * The angle between the panel vertical axis and
252 * the major axis of the contact ellipse. This value is an 8-bit
253 * signed integer. The range is -127 to +127 (corresponding to
254 * -90 degree and +90 degree respectively).
255 * The positive direction is clockwise from the vertical axis.
256 * If the ellipse of contact degenerates into a circle,
257 * orientation is reported as 0.
258 */
259 u8 orientation;
260} __packed;
261
262struct cyapa_gen5_report_data {
263 u8 report_head[GEN5_TOUCH_REPORT_HEAD_SIZE];
264 struct cyapa_gen5_touch_record touch_records[10];
265} __packed;
266
267struct gen5_app_cmd_head {
268 __le16 addr; /* Output report register address, must be 0004h */
269 /* Size of packet not including output report register address */
270 __le16 length;
271 u8 report_id; /* Application output report id, must be 2Fh */
272 u8 rsvd; /* Reserved, must be 0 */
273 /*
274 * Bit 7: reserved, must be 0.
275 * Bit 6-0: command code.
276 */
277 u8 cmd_code;
278 u8 parameter_data[0]; /* Parameter data variable based on cmd_code */
279} __packed;
280
281/* Applicaton get/set parameter command data structure */
282struct gen5_app_set_parameter_data {
283 u8 parameter_id;
284 u8 parameter_size;
285 __le32 value;
286} __packed;
287
288struct gen5_app_get_parameter_data {
289 u8 parameter_id;
290} __packed;
291
292/* Variables to record latest gen5 trackpad power states. */
293#define GEN5_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s))
294#define GEN5_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode)
295#define GEN5_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t))
296#define GEN5_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time)
297#define GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) \
298 (((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME)
299
300static int cyapa_gen5_initialize(struct cyapa *cyapa)
301{
302 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
303
304 init_completion(&gen5_pip->cmd_ready);
305 atomic_set(&gen5_pip->cmd_issued, 0);
306 mutex_init(&gen5_pip->cmd_lock);
307
308 gen5_pip->resp_sort_func = NULL;
309 gen5_pip->in_progress_cmd = TSG_INVALID_CMD;
310 gen5_pip->resp_data = NULL;
311 gen5_pip->resp_len = NULL;
312
313 cyapa->dev_pwr_mode = UNINIT_PWR_MODE;
314 cyapa->dev_sleep_time = UNINIT_SLEEP_TIME;
315
316 return 0;
317}
318
319/* Return negative errno, or else the number of bytes read. */
320static ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size)
321{
322 int ret;
323
324 if (size == 0)
325 return 0;
326
327 if (!buf || size > CYAPA_REG_MAP_SIZE)
328 return -EINVAL;
329
330 ret = i2c_master_recv(cyapa->client, buf, size);
331
332 if (ret != size)
333 return (ret < 0) ? ret : -EIO;
334
335 return size;
336}
337
338/**
339 * Return a negative errno code else zero on success.
340 */
341static ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size)
342{
343 int ret;
344
345 if (!buf || !size)
346 return -EINVAL;
347
348 ret = i2c_master_send(cyapa->client, buf, size);
349
350 if (ret != size)
351 return (ret < 0) ? ret : -EIO;
352
353 return 0;
354}
355
356/**
357 * This function is aimed to dump all not read data in Gen5 trackpad
358 * before send any command, otherwise, the interrupt line will be blocked.
359 */
360static int cyapa_empty_pip_output_data(struct cyapa *cyapa,
361 u8 *buf, int *len, cb_sort func)
362{
363 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
364 int length;
365 int report_count;
366 int empty_count;
367 int buf_len;
368 int error;
369
370 buf_len = 0;
371 if (len) {
372 buf_len = (*len < CYAPA_REG_MAP_SIZE) ?
373 *len : CYAPA_REG_MAP_SIZE;
374 *len = 0;
375 }
376
377 report_count = 8; /* max 7 pending data before command response data */
378 empty_count = 0;
379 do {
380 /*
381 * Depending on testing in cyapa driver, there are max 5 "02 00"
382 * packets between two valid buffered data report in firmware.
383 * So in order to dump all buffered data out and
384 * make interrupt line release for reassert again,
385 * we must set the empty_count check value bigger than 5 to
386 * make it work. Otherwise, in some situation,
387 * the interrupt line may unable to reactive again,
388 * which will cause trackpad device unable to
389 * report data any more.
390 * for example, it may happen in EFT and ESD testing.
391 */
392 if (empty_count > 5)
393 return 0;
394
395 error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf,
396 GEN5_RESP_LENGTH_SIZE);
397 if (error < 0)
398 return error;
399
400 length = get_unaligned_le16(gen5_pip->empty_buf);
401 if (length == GEN5_RESP_LENGTH_SIZE) {
402 empty_count++;
403 continue;
404 } else if (length > CYAPA_REG_MAP_SIZE) {
405 /* Should not happen */
406 return -EINVAL;
407 } else if (length == 0) {
408 /* Application or bootloader launch data polled out. */
409 length = GEN5_RESP_LENGTH_SIZE;
410 if (buf && buf_len && func &&
411 func(cyapa, gen5_pip->empty_buf, length)) {
412 length = min(buf_len, length);
413 memcpy(buf, gen5_pip->empty_buf, length);
414 *len = length;
415 /* Response found, success. */
416 return 0;
417 }
418 continue;
419 }
420
421 error = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length);
422 if (error < 0)
423 return error;
424
425 report_count--;
426 empty_count = 0;
427 length = get_unaligned_le16(gen5_pip->empty_buf);
428 if (length <= GEN5_RESP_LENGTH_SIZE) {
429 empty_count++;
430 } else if (buf && buf_len && func &&
431 func(cyapa, gen5_pip->empty_buf, length)) {
432 length = min(buf_len, length);
433 memcpy(buf, gen5_pip->empty_buf, length);
434 *len = length;
435 /* Response found, success. */
436 return 0;
437 }
438
439 error = -EINVAL;
440 } while (report_count);
441
442 return error;
443}
444
445static int cyapa_do_i2c_pip_cmd_irq_sync(
446 struct cyapa *cyapa,
447 u8 *cmd, size_t cmd_len,
448 unsigned long timeout)
449{
450 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
451 int error;
452
453 /* Wait for interrupt to set ready completion */
454 init_completion(&gen5_pip->cmd_ready);
455
456 atomic_inc(&gen5_pip->cmd_issued);
457 error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len);
458 if (error) {
459 atomic_dec(&gen5_pip->cmd_issued);
460 return (error < 0) ? error : -EIO;
461 }
462
463 /* Wait for interrupt to indicate command is completed. */
464 timeout = wait_for_completion_timeout(&gen5_pip->cmd_ready,
465 msecs_to_jiffies(timeout));
466 if (timeout == 0) {
467 atomic_dec(&gen5_pip->cmd_issued);
468 return -ETIMEDOUT;
469 }
470
471 return 0;
472}
473
474static int cyapa_do_i2c_pip_cmd_polling(
475 struct cyapa *cyapa,
476 u8 *cmd, size_t cmd_len,
477 u8 *resp_data, int *resp_len,
478 unsigned long timeout,
479 cb_sort func)
480{
481 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
482 int tries;
483 int length;
484 int error;
485
486 atomic_inc(&gen5_pip->cmd_issued);
487 error = cyapa_i2c_pip_write(cyapa, cmd, cmd_len);
488 if (error) {
489 atomic_dec(&gen5_pip->cmd_issued);
490 return error < 0 ? error : -EIO;
491 }
492
493 length = resp_len ? *resp_len : 0;
494 if (resp_data && resp_len && length != 0 && func) {
495 tries = timeout / 5;
496 do {
497 usleep_range(3000, 5000);
498 *resp_len = length;
499 error = cyapa_empty_pip_output_data(cyapa,
500 resp_data, resp_len, func);
501 if (error || *resp_len == 0)
502 continue;
503 else
504 break;
505 } while (--tries > 0);
506 if ((error || *resp_len == 0) || tries <= 0)
507 error = error ? error : -ETIMEDOUT;
508 }
509
510 atomic_dec(&gen5_pip->cmd_issued);
511 return error;
512}
513
514static int cyapa_i2c_pip_cmd_irq_sync(
515 struct cyapa *cyapa,
516 u8 *cmd, int cmd_len,
517 u8 *resp_data, int *resp_len,
518 unsigned long timeout,
519 cb_sort func,
520 bool irq_mode)
521{
522 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
523 int error;
524
525 if (!cmd || !cmd_len)
526 return -EINVAL;
527
528 /* Commands must be serialized. */
529 error = mutex_lock_interruptible(&gen5_pip->cmd_lock);
530 if (error)
531 return error;
532
533 gen5_pip->resp_sort_func = func;
534 gen5_pip->resp_data = resp_data;
535 gen5_pip->resp_len = resp_len;
536
537 if (cmd_len >= GEN5_MIN_APP_CMD_LENGTH &&
538 cmd[4] == GEN5_APP_CMD_REPORT_ID) {
539 /* Application command */
540 gen5_pip->in_progress_cmd = cmd[6] & 0x7f;
541 } else if (cmd_len >= GEN5_MIN_BL_CMD_LENGTH &&
542 cmd[4] == GEN5_BL_CMD_REPORT_ID) {
543 /* Bootloader command */
544 gen5_pip->in_progress_cmd = cmd[7];
545 }
546
547 /* Send command data, wait and read output response data's length. */
548 if (irq_mode) {
549 gen5_pip->is_irq_mode = true;
550 error = cyapa_do_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
551 timeout);
552 if (error == -ETIMEDOUT && resp_data &&
553 resp_len && *resp_len != 0 && func) {
554 /*
555 * For some old version, there was no interrupt for
556 * the command response data, so need to poll here
557 * to try to get the response data.
558 */
559 error = cyapa_empty_pip_output_data(cyapa,
560 resp_data, resp_len, func);
561 if (error || *resp_len == 0)
562 error = error ? error : -ETIMEDOUT;
563 }
564 } else {
565 gen5_pip->is_irq_mode = false;
566 error = cyapa_do_i2c_pip_cmd_polling(cyapa, cmd, cmd_len,
567 resp_data, resp_len, timeout, func);
568 }
569
570 gen5_pip->resp_sort_func = NULL;
571 gen5_pip->resp_data = NULL;
572 gen5_pip->resp_len = NULL;
573 gen5_pip->in_progress_cmd = TSG_INVALID_CMD;
574
575 mutex_unlock(&gen5_pip->cmd_lock);
576 return error;
577}
578
579static bool cyapa_gen5_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa,
580 u8 *data, int len)
581{
582 if (!data || len < GEN5_MIN_BL_RESP_LENGTH)
583 return false;
584
585 /* Bootloader input report id 30h */
586 if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_RESP_REPORT_ID &&
587 data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY &&
588 data[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY)
589 return true;
590
591 return false;
592}
593
594static bool cyapa_gen5_sort_tsg_pip_app_resp_data(struct cyapa *cyapa,
595 u8 *data, int len)
596{
597 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
598 int resp_len;
599
600 if (!data || len < GEN5_MIN_APP_RESP_LENGTH)
601 return false;
602
603 if (data[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_APP_RESP_REPORT_ID &&
604 data[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY) {
605 resp_len = get_unaligned_le16(&data[GEN5_RESP_LENGTH_OFFSET]);
606 if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) == 0x00 &&
607 resp_len == GEN5_UNSUPPORTED_CMD_RESP_LENGTH &&
608 data[5] == gen5_pip->in_progress_cmd) {
609 /* Unsupported command code */
610 return false;
611 } else if (GET_GEN5_CMD_CODE(data[GEN5_RESP_APP_CMD_OFFSET]) ==
612 gen5_pip->in_progress_cmd) {
613 /* Correct command response received */
614 return true;
615 }
616 }
617
618 return false;
619}
620
621static bool cyapa_gen5_sort_hid_descriptor_data(struct cyapa *cyapa,
622 u8 *buf, int len)
623{
624 int resp_len;
625 int max_output_len;
626
627 /* Check hid descriptor. */
628 if (len != GEN5_HID_DESCRIPTOR_SIZE)
629 return false;
630
631 resp_len = get_unaligned_le16(&buf[GEN5_RESP_LENGTH_OFFSET]);
632 max_output_len = get_unaligned_le16(&buf[16]);
633 if (resp_len == GEN5_HID_DESCRIPTOR_SIZE) {
634 if (buf[GEN5_RESP_REPORT_ID_OFFSET] == GEN5_BL_HID_REPORT_ID &&
635 max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
636 /* BL mode HID Descriptor */
637 return true;
638 } else if ((buf[GEN5_RESP_REPORT_ID_OFFSET] ==
639 GEN5_APP_HID_REPORT_ID) &&
640 max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
641 /* APP mode HID Descriptor */
642 return true;
643 }
644 }
645
646 return false;
647}
648
649static bool cyapa_gen5_sort_deep_sleep_data(struct cyapa *cyapa,
650 u8 *buf, int len)
651{
652 if (len == GEN5_DEEP_SLEEP_RESP_LENGTH &&
653 buf[GEN5_RESP_REPORT_ID_OFFSET] ==
654 GEN5_APP_DEEP_SLEEP_REPORT_ID &&
655 (buf[4] & GEN5_DEEP_SLEEP_OPCODE_MASK) ==
656 GEN5_DEEP_SLEEP_OPCODE)
657 return true;
658 return false;
659}
660
661static int gen5_idle_state_parse(struct cyapa *cyapa)
662{
663 u8 resp_data[GEN5_HID_DESCRIPTOR_SIZE];
664 int max_output_len;
665 int length;
666 u8 cmd[2];
667 int ret;
668 int error;
669
670 /*
671 * Dump all buffered data firstly for the situation
672 * when the trackpad is just power on the cyapa go here.
673 */
674 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
675
676 memset(resp_data, 0, sizeof(resp_data));
677 ret = cyapa_i2c_pip_read(cyapa, resp_data, 3);
678 if (ret != 3)
679 return ret < 0 ? ret : -EIO;
680
681 length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]);
682 if (length == GEN5_RESP_LENGTH_SIZE) {
683 /* Normal state of Gen5 with no data to respose */
684 cyapa->gen = CYAPA_GEN5;
685
686 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
687
688 /* Read description from trackpad device */
689 cmd[0] = 0x01;
690 cmd[1] = 0x00;
691 length = GEN5_HID_DESCRIPTOR_SIZE;
692 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
693 cmd, GEN5_RESP_LENGTH_SIZE,
694 resp_data, &length,
695 300,
696 cyapa_gen5_sort_hid_descriptor_data,
697 false);
698 if (error)
699 return error;
700
701 length = get_unaligned_le16(
702 &resp_data[GEN5_RESP_LENGTH_OFFSET]);
703 max_output_len = get_unaligned_le16(&resp_data[16]);
704 if ((length == GEN5_HID_DESCRIPTOR_SIZE ||
705 length == GEN5_RESP_LENGTH_SIZE) &&
706 (resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
707 GEN5_BL_HID_REPORT_ID) &&
708 max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
709 /* BL mode HID Description read */
710 cyapa->state = CYAPA_STATE_GEN5_BL;
711 } else if ((length == GEN5_HID_DESCRIPTOR_SIZE ||
712 length == GEN5_RESP_LENGTH_SIZE) &&
713 (resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
714 GEN5_APP_HID_REPORT_ID) &&
715 max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
716 /* APP mode HID Description read */
717 cyapa->state = CYAPA_STATE_GEN5_APP;
718 } else {
719 /* Should not happen!!! */
720 cyapa->state = CYAPA_STATE_NO_DEVICE;
721 }
722 }
723
724 return 0;
725}
726
727static int gen5_hid_description_header_parse(struct cyapa *cyapa, u8 *reg_data)
728{
729 int length;
730 u8 resp_data[32];
731 int max_output_len;
732 int ret;
733
734 /* 0x20 0x00 0xF7 is Gen5 Application HID Description Header;
735 * 0x20 0x00 0xFF is Gen5 Booloader HID Description Header.
736 *
737 * Must read HID Description content through out,
738 * otherwise Gen5 trackpad cannot response next command
739 * or report any touch or button data.
740 */
741 ret = cyapa_i2c_pip_read(cyapa, resp_data,
742 GEN5_HID_DESCRIPTOR_SIZE);
743 if (ret != GEN5_HID_DESCRIPTOR_SIZE)
744 return ret < 0 ? ret : -EIO;
745 length = get_unaligned_le16(&resp_data[GEN5_RESP_LENGTH_OFFSET]);
746 max_output_len = get_unaligned_le16(&resp_data[16]);
747 if (length == GEN5_RESP_LENGTH_SIZE) {
748 if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] ==
749 GEN5_BL_HID_REPORT_ID) {
750 /*
751 * BL mode HID Description has been previously
752 * read out.
753 */
754 cyapa->gen = CYAPA_GEN5;
755 cyapa->state = CYAPA_STATE_GEN5_BL;
756 } else {
757 /*
758 * APP mode HID Description has been previously
759 * read out.
760 */
761 cyapa->gen = CYAPA_GEN5;
762 cyapa->state = CYAPA_STATE_GEN5_APP;
763 }
764 } else if (length == GEN5_HID_DESCRIPTOR_SIZE &&
765 resp_data[2] == GEN5_BL_HID_REPORT_ID &&
766 max_output_len == GEN5_BL_MAX_OUTPUT_LENGTH) {
767 /* BL mode HID Description read. */
768 cyapa->gen = CYAPA_GEN5;
769 cyapa->state = CYAPA_STATE_GEN5_BL;
770 } else if (length == GEN5_HID_DESCRIPTOR_SIZE &&
771 (resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
772 GEN5_APP_HID_REPORT_ID) &&
773 max_output_len == GEN5_APP_MAX_OUTPUT_LENGTH) {
774 /* APP mode HID Description read. */
775 cyapa->gen = CYAPA_GEN5;
776 cyapa->state = CYAPA_STATE_GEN5_APP;
777 } else {
778 /* Should not happen!!! */
779 cyapa->state = CYAPA_STATE_NO_DEVICE;
780 }
781
782 return 0;
783}
784
785static int gen5_report_data_header_parse(struct cyapa *cyapa, u8 *reg_data)
786{
787 int length;
788
789 length = get_unaligned_le16(&reg_data[GEN5_RESP_LENGTH_OFFSET]);
790 switch (reg_data[GEN5_RESP_REPORT_ID_OFFSET]) {
791 case GEN5_TOUCH_REPORT_ID:
792 if (length < GEN5_TOUCH_REPORT_HEAD_SIZE ||
793 length > GEN5_TOUCH_REPORT_MAX_SIZE)
794 return -EINVAL;
795 break;
796 case GEN5_BTN_REPORT_ID:
797 case GEN5_OLD_PUSH_BTN_REPORT_ID:
798 case GEN5_PUSH_BTN_REPORT_ID:
799 if (length < GEN5_BTN_REPORT_HEAD_SIZE ||
800 length > GEN5_BTN_REPORT_MAX_SIZE)
801 return -EINVAL;
802 break;
803 case GEN5_WAKEUP_EVENT_REPORT_ID:
804 if (length != GEN5_WAKEUP_EVENT_SIZE)
805 return -EINVAL;
806 break;
807 default:
808 return -EINVAL;
809 }
810
811 cyapa->gen = CYAPA_GEN5;
812 cyapa->state = CYAPA_STATE_GEN5_APP;
813 return 0;
814}
815
816static int gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data)
817{
818 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
819 int length;
820 int ret;
821
822 /*
823 * Must read report data through out,
824 * otherwise Gen5 trackpad cannot response next command
825 * or report any touch or button data.
826 */
827 length = get_unaligned_le16(&reg_data[GEN5_RESP_LENGTH_OFFSET]);
828 ret = cyapa_i2c_pip_read(cyapa, gen5_pip->empty_buf, length);
829 if (ret != length)
830 return ret < 0 ? ret : -EIO;
831
832 if (length == GEN5_RESP_LENGTH_SIZE) {
833 /* Previous command has read the data through out. */
834 if (reg_data[GEN5_RESP_REPORT_ID_OFFSET] ==
835 GEN5_BL_RESP_REPORT_ID) {
836 /* Gen5 BL command response data detected */
837 cyapa->gen = CYAPA_GEN5;
838 cyapa->state = CYAPA_STATE_GEN5_BL;
839 } else {
840 /* Gen5 APP command response data detected */
841 cyapa->gen = CYAPA_GEN5;
842 cyapa->state = CYAPA_STATE_GEN5_APP;
843 }
844 } else if ((gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] ==
845 GEN5_BL_RESP_REPORT_ID) &&
846 (gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] ==
847 GEN5_RESP_RSVD_KEY) &&
848 (gen5_pip->empty_buf[GEN5_RESP_BL_SOP_OFFSET] ==
849 GEN5_SOP_KEY) &&
850 (gen5_pip->empty_buf[length - 1] ==
851 GEN5_EOP_KEY)) {
852 /* Gen5 BL command response data detected */
853 cyapa->gen = CYAPA_GEN5;
854 cyapa->state = CYAPA_STATE_GEN5_BL;
855 } else if (gen5_pip->empty_buf[GEN5_RESP_REPORT_ID_OFFSET] ==
856 GEN5_APP_RESP_REPORT_ID &&
857 gen5_pip->empty_buf[GEN5_RESP_RSVD_OFFSET] ==
858 GEN5_RESP_RSVD_KEY) {
859 /* Gen5 APP command response data detected */
860 cyapa->gen = CYAPA_GEN5;
861 cyapa->state = CYAPA_STATE_GEN5_APP;
862 } else {
863 /* Should not happen!!! */
864 cyapa->state = CYAPA_STATE_NO_DEVICE;
865 }
866
867 return 0;
868}
869
870static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
871{
872 int length;
873
874 if (!reg_data || len < 3)
875 return -EINVAL;
876
877 cyapa->state = CYAPA_STATE_NO_DEVICE;
878
879 /* Parse based on Gen5 characteristic registers and bits */
880 length = get_unaligned_le16(&reg_data[GEN5_RESP_LENGTH_OFFSET]);
881 if (length == 0 || length == GEN5_RESP_LENGTH_SIZE) {
882 gen5_idle_state_parse(cyapa);
883 } else if (length == GEN5_HID_DESCRIPTOR_SIZE &&
884 (reg_data[2] == GEN5_BL_HID_REPORT_ID ||
885 reg_data[2] == GEN5_APP_HID_REPORT_ID)) {
886 gen5_hid_description_header_parse(cyapa, reg_data);
887 } else if ((length == GEN5_APP_REPORT_DESCRIPTOR_SIZE ||
888 length == GEN5_APP_CONTRACT_REPORT_DESCRIPTOR_SIZE) &&
889 reg_data[2] == GEN5_APP_REPORT_DESCRIPTOR_ID) {
890 /* 0xEE 0x00 0xF6 is Gen5 APP report description header. */
891 cyapa->gen = CYAPA_GEN5;
892 cyapa->state = CYAPA_STATE_GEN5_APP;
893 } else if (length == GEN5_BL_REPORT_DESCRIPTOR_SIZE &&
894 reg_data[2] == GEN5_BL_REPORT_DESCRIPTOR_ID) {
895 /* 0x1D 0x00 0xFE is Gen5 BL report descriptior header. */
896 cyapa->gen = CYAPA_GEN5;
897 cyapa->state = CYAPA_STATE_GEN5_BL;
898 } else if (reg_data[2] == GEN5_TOUCH_REPORT_ID ||
899 reg_data[2] == GEN5_BTN_REPORT_ID ||
900 reg_data[2] == GEN5_OLD_PUSH_BTN_REPORT_ID ||
901 reg_data[2] == GEN5_PUSH_BTN_REPORT_ID ||
902 reg_data[2] == GEN5_WAKEUP_EVENT_REPORT_ID) {
903 gen5_report_data_header_parse(cyapa, reg_data);
904 } else if (reg_data[2] == GEN5_BL_RESP_REPORT_ID ||
905 reg_data[2] == GEN5_APP_RESP_REPORT_ID) {
906 gen5_cmd_resp_header_parse(cyapa, reg_data);
907 }
908
909 if (cyapa->gen == CYAPA_GEN5) {
910 /*
911 * Must read the content (e.g.: report description and so on)
912 * from trackpad device throughout. Otherwise,
913 * Gen5 trackpad cannot response to next command or
914 * report any touch or button data later.
915 */
916 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
917
918 if (cyapa->state == CYAPA_STATE_GEN5_APP ||
919 cyapa->state == CYAPA_STATE_GEN5_BL)
920 return 0;
921 }
922
923 return -EAGAIN;
924}
925
926static bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len)
927{
928 if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
929 return false;
930
931 if (buf[0] == 0 && buf[1] == 0)
932 return true;
933
934 /* Exit bootloader failed for some reason. */
935 if (len == GEN5_BL_FAIL_EXIT_RESP_LEN &&
936 buf[GEN5_RESP_REPORT_ID_OFFSET] ==
937 GEN5_BL_RESP_REPORT_ID &&
938 buf[GEN5_RESP_RSVD_OFFSET] == GEN5_RESP_RSVD_KEY &&
939 buf[GEN5_RESP_BL_SOP_OFFSET] == GEN5_SOP_KEY &&
940 buf[10] == GEN5_EOP_KEY)
941 return true;
942
943 return false;
944}
945
946static int cyapa_gen5_bl_exit(struct cyapa *cyapa)
947{
948
949 u8 bl_gen5_bl_exit[] = { 0x04, 0x00,
950 0x0B, 0x00, 0x40, 0x00, 0x01, 0x3b, 0x00, 0x00,
951 0x20, 0xc7, 0x17
952 };
953 u8 resp_data[11];
954 int resp_len;
955 int error;
956
957 resp_len = sizeof(resp_data);
958 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
959 bl_gen5_bl_exit, sizeof(bl_gen5_bl_exit),
960 resp_data, &resp_len,
961 5000, cyapa_gen5_sort_bl_exit_data, false);
962 if (error)
963 return error;
964
965 if (resp_len == GEN5_BL_FAIL_EXIT_RESP_LEN ||
966 resp_data[GEN5_RESP_REPORT_ID_OFFSET] ==
967 GEN5_BL_RESP_REPORT_ID)
968 return -EAGAIN;
969
970 if (resp_data[0] == 0x00 && resp_data[1] == 0x00)
971 return 0;
972
973 return -ENODEV;
974}
975
976static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
977{
978 u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 };
979 u8 resp_data[6];
980 int resp_len;
981 int error;
982
983 cmd[7] = power_state;
984 resp_len = sizeof(resp_data);
985 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
986 resp_data, &resp_len,
987 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
988 if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x08) ||
989 !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
990 return error < 0 ? error : -EINVAL;
991
992 return 0;
993}
994
995static int cyapa_gen5_set_interval_time(struct cyapa *cyapa,
996 u8 parameter_id, u16 interval_time)
997{
998 struct gen5_app_cmd_head *app_cmd_head;
999 struct gen5_app_set_parameter_data *parameter_data;
1000 u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
1001 int cmd_len;
1002 u8 resp_data[7];
1003 int resp_len;
1004 u8 parameter_size;
1005 int error;
1006
1007 memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
1008 app_cmd_head = (struct gen5_app_cmd_head *)cmd;
1009 parameter_data = (struct gen5_app_set_parameter_data *)
1010 app_cmd_head->parameter_data;
1011 cmd_len = sizeof(struct gen5_app_cmd_head) +
1012 sizeof(struct gen5_app_set_parameter_data);
1013
1014 switch (parameter_id) {
1015 case GEN5_PARAMETER_ACT_INTERVL_ID:
1016 parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE;
1017 break;
1018 case GEN5_PARAMETER_ACT_LFT_INTERVL_ID:
1019 parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE;
1020 break;
1021 case GEN5_PARAMETER_LP_INTRVL_ID:
1022 parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE;
1023 break;
1024 default:
1025 return -EINVAL;
1026 }
1027
1028 put_unaligned_le16(GEN5_OUTPUT_REPORT_ADDR, &app_cmd_head->addr);
1029 /*
1030 * Don't include unused parameter value bytes and
1031 * 2 bytes register address.
1032 */
1033 put_unaligned_le16(cmd_len - (4 - parameter_size) - 2,
1034 &app_cmd_head->length);
1035 app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID;
1036 app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER;
1037 parameter_data->parameter_id = parameter_id;
1038 parameter_data->parameter_size = parameter_size;
1039 put_unaligned_le32((u32)interval_time, &parameter_data->value);
1040 resp_len = sizeof(resp_data);
1041 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
1042 resp_data, &resp_len,
1043 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
1044 if (error || resp_data[5] != parameter_id ||
1045 resp_data[6] != parameter_size ||
1046 !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER))
1047 return error < 0 ? error : -EINVAL;
1048
1049 return 0;
1050}
1051
1052static int cyapa_gen5_get_interval_time(struct cyapa *cyapa,
1053 u8 parameter_id, u16 *interval_time)
1054{
1055 struct gen5_app_cmd_head *app_cmd_head;
1056 struct gen5_app_get_parameter_data *parameter_data;
1057 u8 cmd[CYAPA_TSG_MAX_CMD_SIZE];
1058 int cmd_len;
1059 u8 resp_data[11];
1060 int resp_len;
1061 u8 parameter_size;
1062 u16 mask, i;
1063 int error;
1064
1065 memset(cmd, 0, CYAPA_TSG_MAX_CMD_SIZE);
1066 app_cmd_head = (struct gen5_app_cmd_head *)cmd;
1067 parameter_data = (struct gen5_app_get_parameter_data *)
1068 app_cmd_head->parameter_data;
1069 cmd_len = sizeof(struct gen5_app_cmd_head) +
1070 sizeof(struct gen5_app_get_parameter_data);
1071
1072 *interval_time = 0;
1073 switch (parameter_id) {
1074 case GEN5_PARAMETER_ACT_INTERVL_ID:
1075 parameter_size = GEN5_PARAMETER_ACT_INTERVL_SIZE;
1076 break;
1077 case GEN5_PARAMETER_ACT_LFT_INTERVL_ID:
1078 parameter_size = GEN5_PARAMETER_ACT_LFT_INTERVL_SIZE;
1079 break;
1080 case GEN5_PARAMETER_LP_INTRVL_ID:
1081 parameter_size = GEN5_PARAMETER_LP_INTRVL_SIZE;
1082 break;
1083 default:
1084 return -EINVAL;
1085 }
1086
1087 put_unaligned_le16(GEN5_HID_DESCRIPTOR_ADDR, &app_cmd_head->addr);
1088 /* Don't include 2 bytes register address */
1089 put_unaligned_le16(cmd_len - 2, &app_cmd_head->length);
1090 app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID;
1091 app_cmd_head->cmd_code = GEN5_CMD_GET_PARAMETER;
1092 parameter_data->parameter_id = parameter_id;
1093
1094 resp_len = sizeof(resp_data);
1095 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len,
1096 resp_data, &resp_len,
1097 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
1098 if (error || resp_data[5] != parameter_id || resp_data[6] == 0 ||
1099 !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_GET_PARAMETER))
1100 return error < 0 ? error : -EINVAL;
1101
1102 mask = 0;
1103 for (i = 0; i < parameter_size; i++)
1104 mask |= (0xff << (i * 8));
1105 *interval_time = get_unaligned_le16(&resp_data[7]) & mask;
1106
1107 return 0;
1108}
1109
1110static int cyapa_gen5_disable_pip_report(struct cyapa *cyapa)
1111{
1112 struct gen5_app_cmd_head *app_cmd_head;
1113 u8 cmd[10];
1114 u8 resp_data[7];
1115 int resp_len;
1116 int error;
1117
1118 memset(cmd, 0, sizeof(cmd));
1119 app_cmd_head = (struct gen5_app_cmd_head *)cmd;
1120
1121 put_unaligned_le16(GEN5_HID_DESCRIPTOR_ADDR, &app_cmd_head->addr);
1122 put_unaligned_le16(sizeof(cmd) - 2, &app_cmd_head->length);
1123 app_cmd_head->report_id = GEN5_APP_CMD_REPORT_ID;
1124 app_cmd_head->cmd_code = GEN5_CMD_SET_PARAMETER;
1125 app_cmd_head->parameter_data[0] = GEN5_PARAMETER_DISABLE_PIP_REPORT;
1126 app_cmd_head->parameter_data[1] = 0x01;
1127 app_cmd_head->parameter_data[2] = 0x01;
1128 resp_len = sizeof(resp_data);
1129 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
1130 resp_data, &resp_len,
1131 500, cyapa_gen5_sort_tsg_pip_app_resp_data, false);
1132 if (error || resp_data[5] != GEN5_PARAMETER_DISABLE_PIP_REPORT ||
1133 !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_SET_PARAMETER) ||
1134 resp_data[6] != 0x01)
1135 return error < 0 ? error : -EINVAL;
1136
1137 return 0;
1138}
1139
1140static int cyapa_gen5_deep_sleep(struct cyapa *cyapa, u8 state)
1141{
1142 u8 cmd[] = { 0x05, 0x00, 0x00, 0x08};
1143 u8 resp_data[5];
1144 int resp_len;
1145 int error;
1146
1147 cmd[2] = state & GEN5_DEEP_SLEEP_STATE_MASK;
1148 resp_len = sizeof(resp_data);
1149 error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
1150 resp_data, &resp_len,
1151 500, cyapa_gen5_sort_deep_sleep_data, false);
1152 if (error || ((resp_data[3] & GEN5_DEEP_SLEEP_STATE_MASK) != state))
1153 return -EINVAL;
1154
1155 return 0;
1156}
1157
1158static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
1159 u8 power_mode, u16 sleep_time)
1160{
1161 struct device *dev = &cyapa->client->dev;
1162 u8 power_state;
1163 int error;
1164
1165 if (cyapa->state != CYAPA_STATE_GEN5_APP)
1166 return 0;
1167
1168 /* Dump all the report data before do power mode commmands. */
1169 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
1170
1171 if (GEN5_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
1172 /*
1173 * Assume TP in deep sleep mode when driver is loaded,
1174 * avoid driver unload and reload command IO issue caused by TP
1175 * has been set into deep sleep mode when unloading.
1176 */
1177 GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
1178 }
1179
1180 if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) &&
1181 GEN5_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
1182 if (cyapa_gen5_get_interval_time(cyapa,
1183 GEN5_PARAMETER_LP_INTRVL_ID,
1184 &cyapa->dev_sleep_time) != 0)
1185 GEN5_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
1186
1187 if (GEN5_DEV_GET_PWR_STATE(cyapa) == power_mode) {
1188 if (power_mode == PWR_MODE_OFF ||
1189 power_mode == PWR_MODE_FULL_ACTIVE ||
1190 power_mode == PWR_MODE_BTN_ONLY ||
1191 GEN5_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
1192 /* Has in correct power mode state, early return. */
1193 return 0;
1194 }
1195 }
1196
1197 if (power_mode == PWR_MODE_OFF) {
1198 error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_OFF);
1199 if (error) {
1200 dev_err(dev, "enter deep sleep fail: %d\n", error);
1201 return error;
1202 }
1203
1204 GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
1205 return 0;
1206 }
1207
1208 /*
1209 * When trackpad in power off mode, it cannot change to other power
1210 * state directly, must be wake up from sleep firstly, then
1211 * continue to do next power sate change.
1212 */
1213 if (GEN5_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
1214 error = cyapa_gen5_deep_sleep(cyapa, GEN5_DEEP_SLEEP_STATE_ON);
1215 if (error) {
1216 dev_err(dev, "deep sleep wake fail: %d\n", error);
1217 return error;
1218 }
1219 }
1220
1221 if (power_mode == PWR_MODE_FULL_ACTIVE) {
1222 error = cyapa_gen5_change_power_state(cyapa,
1223 GEN5_POWER_STATE_ACTIVE);
1224 if (error) {
1225 dev_err(dev, "change to active fail: %d\n", error);
1226 return error;
1227 }
1228
1229 GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
1230 } else if (power_mode == PWR_MODE_BTN_ONLY) {
1231 error = cyapa_gen5_change_power_state(cyapa,
1232 GEN5_POWER_STATE_BTN_ONLY);
1233 if (error) {
1234 dev_err(dev, "fail to button only mode: %d\n", error);
1235 return error;
1236 }
1237
1238 GEN5_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
1239 } else {
1240 /*
1241 * Continue to change power mode even failed to set
1242 * interval time, it won't affect the power mode change.
1243 * except the sleep interval time is not correct.
1244 */
1245 if (GEN5_DEV_UNINIT_SLEEP_TIME(cyapa) ||
1246 sleep_time != GEN5_DEV_GET_SLEEP_TIME(cyapa))
1247 if (cyapa_gen5_set_interval_time(cyapa,
1248 GEN5_PARAMETER_LP_INTRVL_ID,
1249 sleep_time) == 0)
1250 GEN5_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
1251
1252 if (sleep_time <= GEN5_POWER_READY_MAX_INTRVL_TIME)
1253 power_state = GEN5_POWER_STATE_READY;
1254 else
1255 power_state = GEN5_POWER_STATE_IDLE;
1256 error = cyapa_gen5_change_power_state(cyapa, power_state);
1257 if (error) {
1258 dev_err(dev, "set power state to 0x%02x failed: %d\n",
1259 power_state, error);
1260 return error;
1261 }
1262
1263 /*
1264 * Disable pip report for a little time, firmware will
1265 * re-enable it automatically. It's used to fix the issue
1266 * that trackpad unable to report signal to wake system up
1267 * in the special situation that system is in suspending, and
1268 * at the same time, user touch trackpad to wake system up.
1269 * This function can avoid the data to be buffured when system
1270 * is suspending which may cause interrput line unable to be
1271 * asserted again.
1272 */
1273 cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
1274 cyapa_gen5_disable_pip_report(cyapa);
1275
1276 GEN5_DEV_SET_PWR_STATE(cyapa,
1277 cyapa_sleep_time_to_pwr_cmd(sleep_time));
1278 }
1279
1280 return 0;
1281}
1282
1283static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa,
1284 u8 *buf, int len)
1285{
1286 /* Check the report id and command code */
1287 if (VALID_CMD_RESP_HEADER(buf, 0x02))
1288 return true;
1289
1290 return false;
1291}
1292
1293static int cyapa_gen5_bl_query_data(struct cyapa *cyapa)
1294{
1295 u8 bl_query_data_cmd[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00,
1296 0x01, 0x3c, 0x00, 0x00, 0xb0, 0x42, 0x17
1297 };
1298 u8 resp_data[GEN5_BL_READ_APP_INFO_RESP_LEN];
1299 int resp_len;
1300 int error;
1301
1302 resp_len = GEN5_BL_READ_APP_INFO_RESP_LEN;
1303 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
1304 bl_query_data_cmd, sizeof(bl_query_data_cmd),
1305 resp_data, &resp_len,
1306 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, false);
1307 if (error || resp_len != GEN5_BL_READ_APP_INFO_RESP_LEN ||
1308 !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
1309 return error ? error : -EIO;
1310
1311 memcpy(&cyapa->product_id[0], &resp_data[8], 5);
1312 cyapa->product_id[5] = '-';
1313 memcpy(&cyapa->product_id[6], &resp_data[13], 6);
1314 cyapa->product_id[12] = '-';
1315 memcpy(&cyapa->product_id[13], &resp_data[19], 2);
1316 cyapa->product_id[15] = '\0';
1317
1318 cyapa->fw_maj_ver = resp_data[22];
1319 cyapa->fw_min_ver = resp_data[23];
1320
1321 return 0;
1322}
1323
1324static int cyapa_gen5_get_query_data(struct cyapa *cyapa)
1325{
1326 u8 get_system_information[] = {
1327 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x02
1328 };
1329 u8 resp_data[71];
1330 int resp_len;
1331 u16 product_family;
1332 int error;
1333
1334 resp_len = sizeof(resp_data);
1335 error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
1336 get_system_information, sizeof(get_system_information),
1337 resp_data, &resp_len,
1338 2000, cyapa_gen5_sort_system_info_data, false);
1339 if (error || resp_len < sizeof(resp_data))
1340 return error ? error : -EIO;
1341
1342 product_family = get_unaligned_le16(&resp_data[7]);
1343 if ((product_family & GEN5_PRODUCT_FAMILY_MASK) !=
1344 GEN5_PRODUCT_FAMILY_TRACKPAD)
1345 return -EINVAL;
1346
1347 cyapa->fw_maj_ver = resp_data[15];
1348 cyapa->fw_min_ver = resp_data[16];
1349
1350 cyapa->electrodes_x = resp_data[52];
1351 cyapa->electrodes_y = resp_data[53];
1352
1353 cyapa->physical_size_x = get_unaligned_le16(&resp_data[54]) / 100;
1354 cyapa->physical_size_y = get_unaligned_le16(&resp_data[56]) / 100;
1355
1356 cyapa->max_abs_x = get_unaligned_le16(&resp_data[58]);
1357 cyapa->max_abs_y = get_unaligned_le16(&resp_data[60]);
1358
1359 cyapa->max_z = get_unaligned_le16(&resp_data[62]);
1360
1361 cyapa->x_origin = resp_data[64] & 0x01;
1362 cyapa->y_origin = resp_data[65] & 0x01;
1363
1364 cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
1365
1366 memcpy(&cyapa->product_id[0], &resp_data[33], 5);
1367 cyapa->product_id[5] = '-';
1368 memcpy(&cyapa->product_id[6], &resp_data[38], 6);
1369 cyapa->product_id[12] = '-';
1370 memcpy(&cyapa->product_id[13], &resp_data[44], 2);
1371 cyapa->product_id[15] = '\0';
1372
1373 if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
1374 !cyapa->physical_size_x || !cyapa->physical_size_y ||
1375 !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
1376 return -EINVAL;
1377
1378 return 0;
1379}
1380
1381static int cyapa_gen5_do_operational_check(struct cyapa *cyapa)
1382{
1383 struct device *dev = &cyapa->client->dev;
1384 int error;
1385
1386 if (cyapa->gen != CYAPA_GEN5)
1387 return -ENODEV;
1388
1389 switch (cyapa->state) {
1390 case CYAPA_STATE_GEN5_BL:
1391 error = cyapa_gen5_bl_exit(cyapa);
1392 if (error) {
1393 /* Rry to update trackpad product information. */
1394 cyapa_gen5_bl_query_data(cyapa);
1395 goto out;
1396 }
1397
1398 cyapa->state = CYAPA_STATE_GEN5_APP;
1399
1400 case CYAPA_STATE_GEN5_APP:
1401 /*
1402 * If trackpad device in deep sleep mode,
1403 * the app command will fail.
1404 * So always try to reset trackpad device to full active when
1405 * the device state is requeried.
1406 */
1407 error = cyapa_gen5_set_power_mode(cyapa,
1408 PWR_MODE_FULL_ACTIVE, 0);
1409 if (error)
1410 dev_warn(dev, "%s: failed to set power active mode.\n",
1411 __func__);
1412
1413 /* Get trackpad product information. */
1414 error = cyapa_gen5_get_query_data(cyapa);
1415 if (error)
1416 goto out;
1417 /* Only support product ID starting with CYTRA */
1418 if (memcmp(cyapa->product_id, product_id,
1419 strlen(product_id)) != 0) {
1420 dev_err(dev, "%s: unknown product ID (%s)\n",
1421 __func__, cyapa->product_id);
1422 error = -EINVAL;
1423 }
1424 break;
1425 default:
1426 error = -EINVAL;
1427 }
1428
1429out:
1430 return error;
1431}
1432
1433/*
1434 * Return false, do not continue process
1435 * Return true, continue process.
1436 */
1437static bool cyapa_gen5_irq_cmd_handler(struct cyapa *cyapa)
1438{
1439 struct cyapa_gen5_cmd_states *gen5_pip = &cyapa->cmd_states.gen5;
1440 int length;
1441
1442 if (atomic_read(&gen5_pip->cmd_issued)) {
1443 /* Polling command response data. */
1444 if (gen5_pip->is_irq_mode == false)
1445 return false;
1446
1447 /*
1448 * Read out all none command response data.
1449 * these output data may caused by user put finger on
1450 * trackpad when host waiting the command response.
1451 */
1452 cyapa_i2c_pip_read(cyapa, gen5_pip->irq_cmd_buf,
1453 GEN5_RESP_LENGTH_SIZE);
1454 length = get_unaligned_le16(gen5_pip->irq_cmd_buf);
1455 length = (length <= GEN5_RESP_LENGTH_SIZE) ?
1456 GEN5_RESP_LENGTH_SIZE : length;
1457 if (length > GEN5_RESP_LENGTH_SIZE)
1458 cyapa_i2c_pip_read(cyapa,
1459 gen5_pip->irq_cmd_buf, length);
1460
1461 if (!(gen5_pip->resp_sort_func &&
1462 gen5_pip->resp_sort_func(cyapa,
1463 gen5_pip->irq_cmd_buf, length))) {
1464 /*
1465 * Cover the Gen5 V1 firmware issue.
1466 * The issue is there is no interrut will be
1467 * asserted to notityf host to read a command
1468 * data out when always has finger touch on
1469 * trackpad during the command is issued to
1470 * trackad device.
1471 * This issue has the scenario is that,
1472 * user always has his fingers touched on
1473 * trackpad device when booting/rebooting
1474 * their chrome book.
1475 */
1476 length = *gen5_pip->resp_len;
1477 cyapa_empty_pip_output_data(cyapa,
1478 gen5_pip->resp_data,
1479 &length,
1480 gen5_pip->resp_sort_func);
1481 if (gen5_pip->resp_len && length != 0) {
1482 *gen5_pip->resp_len = length;
1483 atomic_dec(&gen5_pip->cmd_issued);
1484 complete(&gen5_pip->cmd_ready);
1485 }
1486 return false;
1487 }
1488
1489 if (gen5_pip->resp_data && gen5_pip->resp_len) {
1490 *gen5_pip->resp_len = (*gen5_pip->resp_len < length) ?
1491 *gen5_pip->resp_len : length;
1492 memcpy(gen5_pip->resp_data, gen5_pip->irq_cmd_buf,
1493 *gen5_pip->resp_len);
1494 }
1495 atomic_dec(&gen5_pip->cmd_issued);
1496 complete(&gen5_pip->cmd_ready);
1497 return false;
1498 }
1499
1500 return true;
1501}
1502
1503static void cyapa_gen5_report_buttons(struct cyapa *cyapa,
1504 const struct cyapa_gen5_report_data *report_data)
1505{
1506 struct input_dev *input = cyapa->input;
1507 u8 buttons = report_data->report_head[GEN5_BUTTONS_OFFSET];
1508
1509 buttons = (buttons << CAPABILITY_BTN_SHIFT) & CAPABILITY_BTN_MASK;
1510
1511 if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) {
1512 input_report_key(input, BTN_LEFT,
1513 !!(buttons & CAPABILITY_LEFT_BTN_MASK));
1514 }
1515 if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) {
1516 input_report_key(input, BTN_MIDDLE,
1517 !!(buttons & CAPABILITY_MIDDLE_BTN_MASK));
1518 }
1519 if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) {
1520 input_report_key(input, BTN_RIGHT,
1521 !!(buttons & CAPABILITY_RIGHT_BTN_MASK));
1522 }
1523
1524 input_sync(input);
1525}
1526
1527static void cyapa_gen5_report_slot_data(struct cyapa *cyapa,
1528 const struct cyapa_gen5_touch_record *touch)
1529{
1530 struct input_dev *input = cyapa->input;
1531 u8 event_id = GEN5_GET_EVENT_ID(touch->touch_tip_event_id);
1532 int slot = GEN5_GET_TOUCH_ID(touch->touch_tip_event_id);
1533 int x, y;
1534
1535 if (event_id == RECORD_EVENT_LIFTOFF)
1536 return;
1537
1538 input_mt_slot(input, slot);
1539 input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
1540 x = (touch->x_hi << 8) | touch->x_lo;
1541 if (cyapa->x_origin)
1542 x = cyapa->max_abs_x - x;
1543 input_report_abs(input, ABS_MT_POSITION_X, x);
1544 y = (touch->y_hi << 8) | touch->y_lo;
1545 if (cyapa->y_origin)
1546 y = cyapa->max_abs_y - y;
1547 input_report_abs(input, ABS_MT_POSITION_Y, y);
1548 input_report_abs(input, ABS_MT_PRESSURE,
1549 touch->z);
1550 input_report_abs(input, ABS_MT_TOUCH_MAJOR,
1551 touch->major_axis_len);
1552 input_report_abs(input, ABS_MT_TOUCH_MINOR,
1553 touch->minor_axis_len);
1554
1555 input_report_abs(input, ABS_MT_WIDTH_MAJOR,
1556 touch->major_tool_len);
1557 input_report_abs(input, ABS_MT_WIDTH_MINOR,
1558 touch->minor_tool_len);
1559
1560 input_report_abs(input, ABS_MT_ORIENTATION,
1561 touch->orientation);
1562}
1563
1564static void cyapa_gen5_report_touches(struct cyapa *cyapa,
1565 const struct cyapa_gen5_report_data *report_data)
1566{
1567 struct input_dev *input = cyapa->input;
1568 unsigned int touch_num;
1569 int i;
1570
1571 touch_num = report_data->report_head[GEN5_NUMBER_OF_TOUCH_OFFSET] &
1572 GEN5_NUMBER_OF_TOUCH_MASK;
1573
1574 for (i = 0; i < touch_num; i++)
1575 cyapa_gen5_report_slot_data(cyapa,
1576 &report_data->touch_records[i]);
1577
1578 input_mt_sync_frame(input);
1579 input_sync(input);
1580}
1581
1582static int cyapa_gen5_irq_handler(struct cyapa *cyapa)
1583{
1584 struct device *dev = &cyapa->client->dev;
1585 struct cyapa_gen5_report_data report_data;
1586 int ret;
1587 u8 report_id;
1588 unsigned int report_len;
1589
1590 if (cyapa->gen != CYAPA_GEN5 ||
1591 cyapa->state != CYAPA_STATE_GEN5_APP) {
1592 dev_err(dev, "invalid device state, gen=%d, state=0x%02x\n",
1593 cyapa->gen, cyapa->state);
1594 return -EINVAL;
1595 }
1596
1597 ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data,
1598 GEN5_RESP_LENGTH_SIZE);
1599 if (ret != GEN5_RESP_LENGTH_SIZE) {
1600 dev_err(dev, "failed to read length bytes, (%d)\n", ret);
1601 return -EINVAL;
1602 }
1603
1604 report_len = get_unaligned_le16(
1605 &report_data.report_head[GEN5_RESP_LENGTH_OFFSET]);
1606 if (report_len < GEN5_RESP_LENGTH_SIZE) {
1607 /* Invliad length or internal reset happened. */
1608 dev_err(dev, "invalid report_len=%d. bytes: %02x %02x\n",
1609 report_len, report_data.report_head[0],
1610 report_data.report_head[1]);
1611 return -EINVAL;
1612 }
1613
1614 /* Idle, no data for report. */
1615 if (report_len == GEN5_RESP_LENGTH_SIZE)
1616 return 0;
1617
1618 ret = cyapa_i2c_pip_read(cyapa, (u8 *)&report_data, report_len);
1619 if (ret != report_len) {
1620 dev_err(dev, "failed to read %d bytes report data, (%d)\n",
1621 report_len, ret);
1622 return -EINVAL;
1623 }
1624
1625 report_id = report_data.report_head[GEN5_RESP_REPORT_ID_OFFSET];
1626 if (report_id == GEN5_WAKEUP_EVENT_REPORT_ID &&
1627 report_len == GEN5_WAKEUP_EVENT_SIZE) {
1628 /*
1629 * Device wake event from deep sleep mode for touch.
1630 * This interrupt event is used to wake system up.
1631 */
1632 return 0;
1633 } else if (report_id != GEN5_TOUCH_REPORT_ID &&
1634 report_id != GEN5_BTN_REPORT_ID &&
1635 report_id != GEN5_OLD_PUSH_BTN_REPORT_ID &&
1636 report_id != GEN5_PUSH_BTN_REPORT_ID) {
1637 /* Running in BL mode or unknown response data read. */
1638 dev_err(dev, "invalid report_id=0x%02x\n", report_id);
1639 return -EINVAL;
1640 }
1641
1642 if (report_id == GEN5_TOUCH_REPORT_ID &&
1643 (report_len < GEN5_TOUCH_REPORT_HEAD_SIZE ||
1644 report_len > GEN5_TOUCH_REPORT_MAX_SIZE)) {
1645 /* Invalid report data length for finger packet. */
1646 dev_err(dev, "invalid touch packet length=%d\n", report_len);
1647 return 0;
1648 }
1649
1650 if ((report_id == GEN5_BTN_REPORT_ID ||
1651 report_id == GEN5_OLD_PUSH_BTN_REPORT_ID ||
1652 report_id == GEN5_PUSH_BTN_REPORT_ID) &&
1653 (report_len < GEN5_BTN_REPORT_HEAD_SIZE ||
1654 report_len > GEN5_BTN_REPORT_MAX_SIZE)) {
1655 /* Invalid report data length of button packet. */
1656 dev_err(dev, "invalid button packet length=%d\n", report_len);
1657 return 0;
1658 }
1659
1660 if (report_id == GEN5_TOUCH_REPORT_ID)
1661 cyapa_gen5_report_touches(cyapa, &report_data);
1662 else
1663 cyapa_gen5_report_buttons(cyapa, &report_data);
1664
1665 return 0;
1666}
1667
1668const struct cyapa_dev_ops cyapa_gen5_ops = {
1669 .initialize = cyapa_gen5_initialize,
1670
1671 .state_parse = cyapa_gen5_state_parse,
1672 .operational_check = cyapa_gen5_do_operational_check,
1673
1674 .irq_handler = cyapa_gen5_irq_handler,
1675 .irq_cmd_handler = cyapa_gen5_irq_cmd_handler,
1676 .sort_empty_output_data = cyapa_empty_pip_output_data,
1677 .set_power_mode = cyapa_gen5_set_power_mode,
1678};