1 | /* |
2 | * Cypress APA trackpad with I2C interface |
3 | * |
4 | * Author: Dudley Du <dudl@cypress.com> |
5 | * |
6 | * Copyright (C) 2015 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 <asm/unaligned.h> |
21 | #include <linux/crc-itu-t.h> |
22 | #include "cyapa.h" |
23 | |
24 | |
25 | #define GEN6_ENABLE_CMD_IRQ 0x41 |
26 | #define GEN6_DISABLE_CMD_IRQ 0x42 |
27 | #define GEN6_ENABLE_DEV_IRQ 0x43 |
28 | #define GEN6_DISABLE_DEV_IRQ 0x44 |
29 | |
30 | #define GEN6_POWER_MODE_ACTIVE 0x01 |
31 | #define GEN6_POWER_MODE_LP_MODE1 0x02 |
32 | #define GEN6_POWER_MODE_LP_MODE2 0x03 |
33 | #define GEN6_POWER_MODE_BTN_ONLY 0x04 |
34 | |
35 | #define GEN6_SET_POWER_MODE_INTERVAL 0x47 |
36 | #define GEN6_GET_POWER_MODE_INTERVAL 0x48 |
37 | |
38 | #define GEN6_MAX_RX_NUM 14 |
39 | #define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00 |
40 | #define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12 |
41 | |
42 | |
43 | struct pip_app_cmd_head { |
44 | __le16 addr; |
45 | __le16 length; |
46 | u8 report_id; |
47 | u8 resv; /* Reserved, must be 0 */ |
48 | u8 cmd_code; /* bit7: resv, set to 0; bit6~0: command code.*/ |
49 | } __packed; |
50 | |
51 | struct pip_app_resp_head { |
52 | __le16 length; |
53 | u8 report_id; |
54 | u8 resv; /* Reserved, must be 0 */ |
55 | u8 cmd_code; /* bit7: TGL; bit6~0: command code.*/ |
56 | /* |
57 | * The value of data_status can be the first byte of data or |
58 | * the command status or the unsupported command code depending on the |
59 | * requested command code. |
60 | */ |
61 | u8 data_status; |
62 | } __packed; |
63 | |
64 | struct pip_fixed_info { |
65 | u8 silicon_id_high; |
66 | u8 silicon_id_low; |
67 | u8 family_id; |
68 | }; |
69 | |
70 | static u8 pip_get_bl_info[] = { |
71 | 0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38, |
72 | 0x00, 0x00, 0x70, 0x9E, 0x17 |
73 | }; |
74 | |
75 | static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa, |
76 | u8 *buf, int len) |
77 | { |
78 | if (len != PIP_HID_DESCRIPTOR_SIZE) |
79 | return false; |
80 | |
81 | if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID || |
82 | buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID) |
83 | return true; |
84 | |
85 | return false; |
86 | } |
87 | |
88 | static int cyapa_get_pip_fixed_info(struct cyapa *cyapa, |
89 | struct pip_fixed_info *pip_info, bool is_bootloader) |
90 | { |
91 | u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; |
92 | int resp_len; |
93 | u16 product_family; |
94 | int error; |
95 | |
96 | if (is_bootloader) { |
97 | /* Read Bootloader Information to determine Gen5 or Gen6. */ |
98 | resp_len = sizeof(resp_data); |
99 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
100 | cmd: pip_get_bl_info, cmd_len: sizeof(pip_get_bl_info), |
101 | resp_data, resp_len: &resp_len, |
102 | timeout: 2000, func: cyapa_sort_tsg_pip_bl_resp_data, |
103 | irq_mode: false); |
104 | if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH) |
105 | return error ? error : -EIO; |
106 | |
107 | pip_info->family_id = resp_data[8]; |
108 | pip_info->silicon_id_low = resp_data[10]; |
109 | pip_info->silicon_id_high = resp_data[11]; |
110 | |
111 | return 0; |
112 | } |
113 | |
114 | /* Get App System Information to determine Gen5 or Gen6. */ |
115 | resp_len = sizeof(resp_data); |
116 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
117 | cmd: pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, |
118 | resp_data, resp_len: &resp_len, |
119 | timeout: 2000, func: cyapa_pip_sort_system_info_data, irq_mode: false); |
120 | if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH) |
121 | return error ? error : -EIO; |
122 | |
123 | product_family = get_unaligned_le16(p: &resp_data[7]); |
124 | if ((product_family & PIP_PRODUCT_FAMILY_MASK) != |
125 | PIP_PRODUCT_FAMILY_TRACKPAD) |
126 | return -EINVAL; |
127 | |
128 | pip_info->family_id = resp_data[19]; |
129 | pip_info->silicon_id_low = resp_data[21]; |
130 | pip_info->silicon_id_high = resp_data[22]; |
131 | |
132 | return 0; |
133 | |
134 | } |
135 | |
136 | int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len) |
137 | { |
138 | u8 cmd[] = { 0x01, 0x00}; |
139 | struct pip_fixed_info pip_info; |
140 | u8 resp_data[PIP_HID_DESCRIPTOR_SIZE]; |
141 | int resp_len; |
142 | bool is_bootloader; |
143 | int error; |
144 | |
145 | cyapa->state = CYAPA_STATE_NO_DEVICE; |
146 | |
147 | /* Try to wake from it deep sleep state if it is. */ |
148 | cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); |
149 | |
150 | /* Empty the buffer queue to get fresh data with later commands. */ |
151 | cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL); |
152 | |
153 | /* |
154 | * Read description info from trackpad device to determine running in |
155 | * APP mode or Bootloader mode. |
156 | */ |
157 | resp_len = PIP_HID_DESCRIPTOR_SIZE; |
158 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
159 | cmd, cmd_len: sizeof(cmd), |
160 | resp_data, resp_len: &resp_len, |
161 | timeout: 300, |
162 | func: cyapa_sort_pip_hid_descriptor_data, |
163 | irq_mode: false); |
164 | if (error) |
165 | return error; |
166 | |
167 | if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID) |
168 | is_bootloader = true; |
169 | else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID) |
170 | is_bootloader = false; |
171 | else |
172 | return -EAGAIN; |
173 | |
174 | /* Get PIP fixed information to determine Gen5 or Gen6. */ |
175 | memset(&pip_info, 0, sizeof(struct pip_fixed_info)); |
176 | error = cyapa_get_pip_fixed_info(cyapa, pip_info: &pip_info, is_bootloader); |
177 | if (error) |
178 | return error; |
179 | |
180 | if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) { |
181 | cyapa->gen = CYAPA_GEN6; |
182 | cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL |
183 | : CYAPA_STATE_GEN6_APP; |
184 | } else if (pip_info.family_id == 0x91 && |
185 | pip_info.silicon_id_high == 0x02) { |
186 | cyapa->gen = CYAPA_GEN5; |
187 | cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL |
188 | : CYAPA_STATE_GEN5_APP; |
189 | } |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static int cyapa_gen6_read_sys_info(struct cyapa *cyapa) |
195 | { |
196 | u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH]; |
197 | int resp_len; |
198 | u16 product_family; |
199 | u8 rotat_align; |
200 | int error; |
201 | |
202 | /* Get App System Information to determine Gen5 or Gen6. */ |
203 | resp_len = sizeof(resp_data); |
204 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
205 | cmd: pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, |
206 | resp_data, resp_len: &resp_len, |
207 | timeout: 2000, func: cyapa_pip_sort_system_info_data, irq_mode: false); |
208 | if (error || resp_len < sizeof(resp_data)) |
209 | return error ? error : -EIO; |
210 | |
211 | product_family = get_unaligned_le16(p: &resp_data[7]); |
212 | if ((product_family & PIP_PRODUCT_FAMILY_MASK) != |
213 | PIP_PRODUCT_FAMILY_TRACKPAD) |
214 | return -EINVAL; |
215 | |
216 | cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) & |
217 | PIP_BL_PLATFORM_VER_MASK; |
218 | cyapa->fw_maj_ver = resp_data[9]; |
219 | cyapa->fw_min_ver = resp_data[10]; |
220 | |
221 | cyapa->electrodes_x = resp_data[33]; |
222 | cyapa->electrodes_y = resp_data[34]; |
223 | |
224 | cyapa->physical_size_x = get_unaligned_le16(p: &resp_data[35]) / 100; |
225 | cyapa->physical_size_y = get_unaligned_le16(p: &resp_data[37]) / 100; |
226 | |
227 | cyapa->max_abs_x = get_unaligned_le16(p: &resp_data[39]); |
228 | cyapa->max_abs_y = get_unaligned_le16(p: &resp_data[41]); |
229 | |
230 | cyapa->max_z = get_unaligned_le16(p: &resp_data[43]); |
231 | |
232 | cyapa->x_origin = resp_data[45] & 0x01; |
233 | cyapa->y_origin = resp_data[46] & 0x01; |
234 | |
235 | cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK; |
236 | |
237 | memcpy(&cyapa->product_id[0], &resp_data[51], 5); |
238 | cyapa->product_id[5] = '-'; |
239 | memcpy(&cyapa->product_id[6], &resp_data[56], 6); |
240 | cyapa->product_id[12] = '-'; |
241 | memcpy(&cyapa->product_id[13], &resp_data[62], 2); |
242 | cyapa->product_id[15] = '\0'; |
243 | |
244 | /* Get the number of Rx electrodes. */ |
245 | rotat_align = resp_data[68]; |
246 | cyapa->electrodes_rx = |
247 | rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x; |
248 | cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u; |
249 | |
250 | if (!cyapa->electrodes_x || !cyapa->electrodes_y || |
251 | !cyapa->physical_size_x || !cyapa->physical_size_y || |
252 | !cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z) |
253 | return -EINVAL; |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa) |
259 | { |
260 | u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH]; |
261 | int resp_len; |
262 | int error; |
263 | |
264 | resp_len = sizeof(resp_data); |
265 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
266 | cmd: pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH, |
267 | resp_data, resp_len: &resp_len, |
268 | timeout: 500, func: cyapa_sort_tsg_pip_bl_resp_data, irq_mode: false); |
269 | if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH || |
270 | !PIP_CMD_COMPLETE_SUCCESS(resp_data)) |
271 | return error ? error : -EIO; |
272 | |
273 | cyapa->fw_maj_ver = resp_data[8]; |
274 | cyapa->fw_min_ver = resp_data[9]; |
275 | |
276 | cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) & |
277 | PIP_BL_PLATFORM_VER_MASK; |
278 | |
279 | memcpy(&cyapa->product_id[0], &resp_data[13], 5); |
280 | cyapa->product_id[5] = '-'; |
281 | memcpy(&cyapa->product_id[6], &resp_data[18], 6); |
282 | cyapa->product_id[12] = '-'; |
283 | memcpy(&cyapa->product_id[13], &resp_data[24], 2); |
284 | cyapa->product_id[15] = '\0'; |
285 | |
286 | return 0; |
287 | |
288 | } |
289 | |
290 | static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code) |
291 | { |
292 | u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code }; |
293 | u8 resp_data[6]; |
294 | int resp_len; |
295 | int error; |
296 | |
297 | resp_len = sizeof(resp_data); |
298 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len: sizeof(cmd), |
299 | resp_data, resp_len: &resp_len, |
300 | timeout: 500, func: cyapa_sort_tsg_pip_app_resp_data, irq_mode: false); |
301 | if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) || |
302 | !PIP_CMD_COMPLETE_SUCCESS(resp_data) |
303 | ) |
304 | return error < 0 ? error : -EINVAL; |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable) |
310 | { |
311 | int error; |
312 | |
313 | cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); |
314 | error = cyapa_pip_set_proximity(cyapa, enable); |
315 | cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ); |
316 | |
317 | return error; |
318 | } |
319 | |
320 | static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode) |
321 | { |
322 | u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode }; |
323 | u8 resp_data[6]; |
324 | int resp_len; |
325 | int error; |
326 | |
327 | resp_len = sizeof(resp_data); |
328 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len: sizeof(cmd), |
329 | resp_data, resp_len: &resp_len, |
330 | timeout: 500, func: cyapa_sort_tsg_pip_app_resp_data, irq_mode: false); |
331 | if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46)) |
332 | return error < 0 ? error : -EINVAL; |
333 | |
334 | /* New power state applied in device not match the set power state. */ |
335 | if (resp_data[5] != power_mode) |
336 | return -EAGAIN; |
337 | |
338 | return 0; |
339 | } |
340 | |
341 | static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa, |
342 | struct gen6_interval_setting *interval_setting) |
343 | { |
344 | struct gen6_set_interval_cmd { |
345 | __le16 addr; |
346 | __le16 length; |
347 | u8 report_id; |
348 | u8 rsvd; /* Reserved, must be 0 */ |
349 | u8 cmd_code; |
350 | __le16 active_interval; |
351 | __le16 lp1_interval; |
352 | __le16 lp2_interval; |
353 | } __packed set_interval_cmd; |
354 | u8 resp_data[11]; |
355 | int resp_len; |
356 | int error; |
357 | |
358 | memset(&set_interval_cmd, 0, sizeof(set_interval_cmd)); |
359 | put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, p: &set_interval_cmd.addr); |
360 | put_unaligned_le16(val: sizeof(set_interval_cmd) - 2, |
361 | p: &set_interval_cmd.length); |
362 | set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID; |
363 | set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL; |
364 | put_unaligned_le16(val: interval_setting->active_interval, |
365 | p: &set_interval_cmd.active_interval); |
366 | put_unaligned_le16(val: interval_setting->lp1_interval, |
367 | p: &set_interval_cmd.lp1_interval); |
368 | put_unaligned_le16(val: interval_setting->lp2_interval, |
369 | p: &set_interval_cmd.lp2_interval); |
370 | |
371 | resp_len = sizeof(resp_data); |
372 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
373 | cmd: (u8 *)&set_interval_cmd, cmd_len: sizeof(set_interval_cmd), |
374 | resp_data, resp_len: &resp_len, |
375 | timeout: 500, func: cyapa_sort_tsg_pip_app_resp_data, irq_mode: false); |
376 | if (error || |
377 | !VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL)) |
378 | return error < 0 ? error : -EINVAL; |
379 | |
380 | /* Get the real set intervals from response. */ |
381 | interval_setting->active_interval = get_unaligned_le16(p: &resp_data[5]); |
382 | interval_setting->lp1_interval = get_unaligned_le16(p: &resp_data[7]); |
383 | interval_setting->lp2_interval = get_unaligned_le16(p: &resp_data[9]); |
384 | |
385 | return 0; |
386 | } |
387 | |
388 | static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa, |
389 | struct gen6_interval_setting *interval_setting) |
390 | { |
391 | u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, |
392 | GEN6_GET_POWER_MODE_INTERVAL }; |
393 | u8 resp_data[11]; |
394 | int resp_len; |
395 | int error; |
396 | |
397 | resp_len = sizeof(resp_data); |
398 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, cmd_len: sizeof(cmd), |
399 | resp_data, resp_len: &resp_len, |
400 | timeout: 500, func: cyapa_sort_tsg_pip_app_resp_data, irq_mode: false); |
401 | if (error || |
402 | !VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL)) |
403 | return error < 0 ? error : -EINVAL; |
404 | |
405 | interval_setting->active_interval = get_unaligned_le16(p: &resp_data[5]); |
406 | interval_setting->lp1_interval = get_unaligned_le16(p: &resp_data[7]); |
407 | interval_setting->lp2_interval = get_unaligned_le16(p: &resp_data[9]); |
408 | |
409 | return 0; |
410 | } |
411 | |
412 | static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) |
413 | { |
414 | u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 }; |
415 | |
416 | if (state == PIP_DEEP_SLEEP_STATE_ON) |
417 | /* |
418 | * Send ping command to notify device prepare for wake up |
419 | * when it's in deep sleep mode. At this time, device will |
420 | * response nothing except an I2C NAK. |
421 | */ |
422 | cyapa_i2c_pip_write(cyapa, buf: ping, size: sizeof(ping)); |
423 | |
424 | return cyapa_pip_deep_sleep(cyapa, state); |
425 | } |
426 | |
427 | static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, |
428 | u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) |
429 | { |
430 | struct device *dev = &cyapa->client->dev; |
431 | struct gen6_interval_setting *interval_setting = |
432 | &cyapa->gen6_interval_setting; |
433 | u8 lp_mode; |
434 | int error; |
435 | |
436 | if (cyapa->state != CYAPA_STATE_GEN6_APP) |
437 | return 0; |
438 | |
439 | if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { |
440 | /* |
441 | * Assume TP in deep sleep mode when driver is loaded, |
442 | * avoid driver unload and reload command IO issue caused by TP |
443 | * has been set into deep sleep mode when unloading. |
444 | */ |
445 | PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); |
446 | } |
447 | |
448 | if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) && |
449 | PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF) |
450 | PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME); |
451 | |
452 | if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) { |
453 | if (power_mode == PWR_MODE_OFF || |
454 | power_mode == PWR_MODE_FULL_ACTIVE || |
455 | power_mode == PWR_MODE_BTN_ONLY || |
456 | PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { |
457 | /* Has in correct power mode state, early return. */ |
458 | return 0; |
459 | } |
460 | } |
461 | |
462 | if (power_mode == PWR_MODE_OFF) { |
463 | cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); |
464 | |
465 | error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); |
466 | if (error) { |
467 | dev_err(dev, "enter deep sleep fail: %d\n" , error); |
468 | return error; |
469 | } |
470 | |
471 | PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); |
472 | return 0; |
473 | } |
474 | |
475 | /* |
476 | * When trackpad in power off mode, it cannot change to other power |
477 | * state directly, must be wake up from sleep firstly, then |
478 | * continue to do next power sate change. |
479 | */ |
480 | if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) { |
481 | error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); |
482 | if (error) { |
483 | dev_err(dev, "deep sleep wake fail: %d\n" , error); |
484 | return error; |
485 | } |
486 | } |
487 | |
488 | /* |
489 | * Disable device assert interrupts for command response to avoid |
490 | * disturbing system suspending or hibernating process. |
491 | */ |
492 | cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ); |
493 | |
494 | if (power_mode == PWR_MODE_FULL_ACTIVE) { |
495 | error = cyapa_gen6_change_power_state(cyapa, |
496 | GEN6_POWER_MODE_ACTIVE); |
497 | if (error) { |
498 | dev_err(dev, "change to active fail: %d\n" , error); |
499 | goto out; |
500 | } |
501 | |
502 | PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); |
503 | |
504 | /* Sync the interval setting from device. */ |
505 | cyapa_gen6_get_interval_setting(cyapa, interval_setting); |
506 | |
507 | } else if (power_mode == PWR_MODE_BTN_ONLY) { |
508 | error = cyapa_gen6_change_power_state(cyapa, |
509 | GEN6_POWER_MODE_BTN_ONLY); |
510 | if (error) { |
511 | dev_err(dev, "fail to button only mode: %d\n" , error); |
512 | goto out; |
513 | } |
514 | |
515 | PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); |
516 | } else { |
517 | /* |
518 | * Gen6 internally supports to 2 low power scan interval time, |
519 | * so can help to switch power mode quickly. |
520 | * such as runtime suspend and system suspend. |
521 | */ |
522 | if (interval_setting->lp1_interval == sleep_time) { |
523 | lp_mode = GEN6_POWER_MODE_LP_MODE1; |
524 | } else if (interval_setting->lp2_interval == sleep_time) { |
525 | lp_mode = GEN6_POWER_MODE_LP_MODE2; |
526 | } else { |
527 | if (interval_setting->lp1_interval == 0) { |
528 | interval_setting->lp1_interval = sleep_time; |
529 | lp_mode = GEN6_POWER_MODE_LP_MODE1; |
530 | } else { |
531 | interval_setting->lp2_interval = sleep_time; |
532 | lp_mode = GEN6_POWER_MODE_LP_MODE2; |
533 | } |
534 | cyapa_gen6_set_interval_setting(cyapa, |
535 | interval_setting); |
536 | } |
537 | |
538 | error = cyapa_gen6_change_power_state(cyapa, power_mode: lp_mode); |
539 | if (error) { |
540 | dev_err(dev, "set power state to 0x%02x failed: %d\n" , |
541 | lp_mode, error); |
542 | goto out; |
543 | } |
544 | |
545 | PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time); |
546 | PIP_DEV_SET_PWR_STATE(cyapa, |
547 | cyapa_sleep_time_to_pwr_cmd(sleep_time)); |
548 | } |
549 | |
550 | out: |
551 | cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ); |
552 | return error; |
553 | } |
554 | |
555 | static int cyapa_gen6_initialize(struct cyapa *cyapa) |
556 | { |
557 | return 0; |
558 | } |
559 | |
560 | static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa, |
561 | u16 read_offset, u16 read_len, u8 data_id, |
562 | u8 *data, int *data_buf_lens) |
563 | { |
564 | struct retrieve_data_struct_cmd { |
565 | struct pip_app_cmd_head head; |
566 | __le16 read_offset; |
567 | __le16 read_length; |
568 | u8 data_id; |
569 | } __packed cmd; |
570 | u8 resp_data[GEN6_MAX_RX_NUM + 10]; |
571 | int resp_len; |
572 | int error; |
573 | |
574 | memset(&cmd, 0, sizeof(cmd)); |
575 | put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, p: &cmd.head.addr); |
576 | put_unaligned_le16(val: sizeof(cmd) - 2, p: &cmd.head.length); |
577 | cmd.head.report_id = PIP_APP_CMD_REPORT_ID; |
578 | cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE; |
579 | put_unaligned_le16(val: read_offset, p: &cmd.read_offset); |
580 | put_unaligned_le16(val: read_len, p: &cmd.read_length); |
581 | cmd.data_id = data_id; |
582 | |
583 | resp_len = sizeof(resp_data); |
584 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, |
585 | cmd: (u8 *)&cmd, cmd_len: sizeof(cmd), |
586 | resp_data, resp_len: &resp_len, |
587 | timeout: 500, func: cyapa_sort_tsg_pip_app_resp_data, |
588 | irq_mode: true); |
589 | if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) || |
590 | resp_data[6] != data_id || |
591 | !VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE)) |
592 | return (error < 0) ? error : -EAGAIN; |
593 | |
594 | read_len = get_unaligned_le16(p: &resp_data[7]); |
595 | if (*data_buf_lens < read_len) { |
596 | *data_buf_lens = read_len; |
597 | return -ENOBUFS; |
598 | } |
599 | |
600 | memcpy(data, &resp_data[10], read_len); |
601 | *data_buf_lens = read_len; |
602 | return 0; |
603 | } |
604 | |
605 | static ssize_t cyapa_gen6_show_baseline(struct device *dev, |
606 | struct device_attribute *attr, char *buf) |
607 | { |
608 | struct cyapa *cyapa = dev_get_drvdata(dev); |
609 | u8 data[GEN6_MAX_RX_NUM]; |
610 | int data_len; |
611 | int size = 0; |
612 | int i; |
613 | int error; |
614 | int resume_error; |
615 | |
616 | if (!cyapa_is_pip_app_mode(cyapa)) |
617 | return -EBUSY; |
618 | |
619 | /* 1. Suspend Scanning*/ |
620 | error = cyapa_pip_suspend_scanning(cyapa); |
621 | if (error) |
622 | return error; |
623 | |
624 | /* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */ |
625 | data_len = sizeof(data); |
626 | error = cyapa_pip_retrieve_data_structure(cyapa, read_offset: 0, read_len: data_len, |
627 | GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC, |
628 | data, data_buf_lens: &data_len); |
629 | if (error) |
630 | goto resume_scanning; |
631 | |
632 | size = scnprintf(buf, PAGE_SIZE, fmt: "%d %d %d %d %d %d " , |
633 | data[0], /* RX Attenuator Mutual */ |
634 | data[1], /* IDAC Mutual */ |
635 | data[2], /* RX Attenuator Self RX */ |
636 | data[3], /* IDAC Self RX */ |
637 | data[4], /* RX Attenuator Self TX */ |
638 | data[5] /* IDAC Self TX */ |
639 | ); |
640 | |
641 | /* 3. Read Attenuator Trim. */ |
642 | data_len = sizeof(data); |
643 | error = cyapa_pip_retrieve_data_structure(cyapa, read_offset: 0, read_len: data_len, |
644 | GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM, |
645 | data, data_buf_lens: &data_len); |
646 | if (error) |
647 | goto resume_scanning; |
648 | |
649 | /* set attenuator trim values. */ |
650 | for (i = 0; i < data_len; i++) |
651 | size += scnprintf(buf: buf + size, PAGE_SIZE - size, fmt: "%d " , data[i]); |
652 | size += scnprintf(buf: buf + size, PAGE_SIZE - size, fmt: "\n" ); |
653 | |
654 | resume_scanning: |
655 | /* 4. Resume Scanning*/ |
656 | resume_error = cyapa_pip_resume_scanning(cyapa); |
657 | if (resume_error || error) { |
658 | memset(buf, 0, PAGE_SIZE); |
659 | return resume_error ? resume_error : error; |
660 | } |
661 | |
662 | return size; |
663 | } |
664 | |
665 | static int cyapa_gen6_operational_check(struct cyapa *cyapa) |
666 | { |
667 | struct device *dev = &cyapa->client->dev; |
668 | int error; |
669 | |
670 | if (cyapa->gen != CYAPA_GEN6) |
671 | return -ENODEV; |
672 | |
673 | switch (cyapa->state) { |
674 | case CYAPA_STATE_GEN6_BL: |
675 | error = cyapa_pip_bl_exit(cyapa); |
676 | if (error) { |
677 | /* Try to update trackpad product information. */ |
678 | cyapa_gen6_bl_read_app_info(cyapa); |
679 | goto out; |
680 | } |
681 | |
682 | cyapa->state = CYAPA_STATE_GEN6_APP; |
683 | fallthrough; |
684 | |
685 | case CYAPA_STATE_GEN6_APP: |
686 | /* |
687 | * If trackpad device in deep sleep mode, |
688 | * the app command will fail. |
689 | * So always try to reset trackpad device to full active when |
690 | * the device state is required. |
691 | */ |
692 | error = cyapa_gen6_set_power_mode(cyapa, |
693 | PWR_MODE_FULL_ACTIVE, sleep_time: 0, pm_stage: CYAPA_PM_ACTIVE); |
694 | if (error) |
695 | dev_warn(dev, "%s: failed to set power active mode.\n" , |
696 | __func__); |
697 | |
698 | /* By default, the trackpad proximity function is enabled. */ |
699 | error = cyapa_pip_set_proximity(cyapa, enable: true); |
700 | if (error) |
701 | dev_warn(dev, "%s: failed to enable proximity.\n" , |
702 | __func__); |
703 | |
704 | /* Get trackpad product information. */ |
705 | error = cyapa_gen6_read_sys_info(cyapa); |
706 | if (error) |
707 | goto out; |
708 | /* Only support product ID starting with CYTRA */ |
709 | if (memcmp(p: cyapa->product_id, q: product_id, |
710 | strlen(product_id)) != 0) { |
711 | dev_err(dev, "%s: unknown product ID (%s)\n" , |
712 | __func__, cyapa->product_id); |
713 | error = -EINVAL; |
714 | } |
715 | break; |
716 | default: |
717 | error = -EINVAL; |
718 | } |
719 | |
720 | out: |
721 | return error; |
722 | } |
723 | |
724 | const struct cyapa_dev_ops cyapa_gen6_ops = { |
725 | .check_fw = cyapa_pip_check_fw, |
726 | .bl_enter = cyapa_pip_bl_enter, |
727 | .bl_initiate = cyapa_pip_bl_initiate, |
728 | .update_fw = cyapa_pip_do_fw_update, |
729 | .bl_activate = cyapa_pip_bl_activate, |
730 | .bl_deactivate = cyapa_pip_bl_deactivate, |
731 | |
732 | .show_baseline = cyapa_gen6_show_baseline, |
733 | .calibrate_store = cyapa_pip_do_calibrate, |
734 | |
735 | .initialize = cyapa_gen6_initialize, |
736 | |
737 | .state_parse = cyapa_pip_state_parse, |
738 | .operational_check = cyapa_gen6_operational_check, |
739 | |
740 | .irq_handler = cyapa_pip_irq_handler, |
741 | .irq_cmd_handler = cyapa_pip_irq_cmd_handler, |
742 | .sort_empty_output_data = cyapa_empty_pip_output_data, |
743 | .set_power_mode = cyapa_gen6_set_power_mode, |
744 | |
745 | .set_proximity = cyapa_gen6_set_proximity, |
746 | }; |
747 | |