Commit | Line | Data |
---|---|---|
c2c06c41 DD |
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 | pip_get_bl_info, sizeof(pip_get_bl_info), | |
101 | resp_data, &resp_len, | |
102 | 2000, cyapa_sort_tsg_pip_bl_resp_data, | |
103 | 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 | pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, | |
118 | resp_data, &resp_len, | |
119 | 2000, cyapa_pip_sort_system_info_data, false); | |
120 | if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH) | |
121 | return error ? error : -EIO; | |
122 | ||
123 | product_family = get_unaligned_le16(&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, sizeof(cmd), | |
160 | resp_data, &resp_len, | |
161 | 300, | |
162 | cyapa_sort_pip_hid_descriptor_data, | |
163 | 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, 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 | pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH, | |
206 | resp_data, &resp_len, | |
207 | 2000, cyapa_pip_sort_system_info_data, false); | |
208 | if (error || resp_len < sizeof(resp_data)) | |
209 | return error ? error : -EIO; | |
210 | ||
211 | product_family = get_unaligned_le16(&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(&resp_data[35]) / 100; | |
225 | cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100; | |
226 | ||
227 | cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]); | |
228 | cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]); | |
229 | ||
230 | cyapa->max_z = get_unaligned_le16(&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 | ||
a487c03f | 244 | /* Get the number of Rx electrodes. */ |
c2c06c41 | 245 | rotat_align = resp_data[68]; |
a487c03f DD |
246 | cyapa->electrodes_rx = |
247 | rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x; | |
c2c06c41 DD |
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 | pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH, | |
267 | resp_data, &resp_len, | |
268 | 500, cyapa_sort_tsg_pip_bl_resp_data, 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, sizeof(cmd), | |
299 | resp_data, &resp_len, | |
300 | 500, cyapa_sort_tsg_pip_app_resp_data, 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 | ||
945525ee DD |
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 | ||
c2c06c41 DD |
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, sizeof(cmd), | |
329 | resp_data, &resp_len, | |
330 | 500, cyapa_sort_tsg_pip_app_resp_data, 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, &set_interval_cmd.addr); | |
360 | put_unaligned_le16(sizeof(set_interval_cmd) - 2, | |
361 | &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(interval_setting->active_interval, | |
365 | &set_interval_cmd.active_interval); | |
366 | put_unaligned_le16(interval_setting->lp1_interval, | |
367 | &set_interval_cmd.lp1_interval); | |
368 | put_unaligned_le16(interval_setting->lp2_interval, | |
369 | &set_interval_cmd.lp2_interval); | |
370 | ||
371 | resp_len = sizeof(resp_data); | |
372 | error = cyapa_i2c_pip_cmd_irq_sync(cyapa, | |
373 | (u8 *)&set_interval_cmd, sizeof(set_interval_cmd), | |
374 | resp_data, &resp_len, | |
375 | 500, cyapa_sort_tsg_pip_app_resp_data, 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(&resp_data[5]); | |
382 | interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]); | |
383 | interval_setting->lp2_interval = get_unaligned_le16(&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, sizeof(cmd), | |
399 | resp_data, &resp_len, | |
400 | 500, cyapa_sort_tsg_pip_app_resp_data, 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(&resp_data[5]); | |
406 | interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]); | |
407 | interval_setting->lp2_interval = get_unaligned_le16(&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, ping, sizeof(ping)); | |
423 | ||
424 | return cyapa_pip_deep_sleep(cyapa, state); | |
425 | } | |
426 | ||
427 | static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, | |
3cd47869 | 428 | u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) |
c2c06c41 DD |
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, 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, &cmd.head.addr); | |
576 | put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2); | |
577 | cmd.head.report_id = PIP_APP_CMD_REPORT_ID; | |
578 | cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE; | |
579 | put_unaligned_le16(read_offset, &cmd.read_offset); | |
580 | put_unaligned_le16(read_len, &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 | (u8 *)&cmd, sizeof(cmd), | |
586 | resp_data, &resp_len, | |
587 | 500, cyapa_sort_tsg_pip_app_resp_data, | |
588 | 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(&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, 0, data_len, | |
627 | GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC, | |
628 | data, &data_len); | |
629 | if (error) | |
630 | goto resume_scanning; | |
631 | ||
632 | size = scnprintf(buf, PAGE_SIZE, "%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, 0, data_len, | |
644 | GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM, | |
645 | data, &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 + size, PAGE_SIZE - size, "%d ", data[i]); | |
652 | size += scnprintf(buf + size, PAGE_SIZE - size, "\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 | ||
684 | case CYAPA_STATE_GEN6_APP: | |
685 | /* | |
686 | * If trackpad device in deep sleep mode, | |
687 | * the app command will fail. | |
688 | * So always try to reset trackpad device to full active when | |
689 | * the device state is required. | |
690 | */ | |
691 | error = cyapa_gen6_set_power_mode(cyapa, | |
3cd47869 | 692 | PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); |
c2c06c41 DD |
693 | if (error) |
694 | dev_warn(dev, "%s: failed to set power active mode.\n", | |
695 | __func__); | |
696 | ||
945525ee DD |
697 | /* By default, the trackpad proximity function is enabled. */ |
698 | error = cyapa_pip_set_proximity(cyapa, true); | |
699 | if (error) | |
700 | dev_warn(dev, "%s: failed to enable proximity.\n", | |
701 | __func__); | |
702 | ||
c2c06c41 DD |
703 | /* Get trackpad product information. */ |
704 | error = cyapa_gen6_read_sys_info(cyapa); | |
705 | if (error) | |
706 | goto out; | |
707 | /* Only support product ID starting with CYTRA */ | |
708 | if (memcmp(cyapa->product_id, product_id, | |
709 | strlen(product_id)) != 0) { | |
710 | dev_err(dev, "%s: unknown product ID (%s)\n", | |
711 | __func__, cyapa->product_id); | |
712 | error = -EINVAL; | |
713 | } | |
714 | break; | |
715 | default: | |
716 | error = -EINVAL; | |
717 | } | |
718 | ||
719 | out: | |
720 | return error; | |
721 | } | |
722 | ||
723 | const struct cyapa_dev_ops cyapa_gen6_ops = { | |
724 | .check_fw = cyapa_pip_check_fw, | |
725 | .bl_enter = cyapa_pip_bl_enter, | |
726 | .bl_initiate = cyapa_pip_bl_initiate, | |
727 | .update_fw = cyapa_pip_do_fw_update, | |
728 | .bl_activate = cyapa_pip_bl_activate, | |
729 | .bl_deactivate = cyapa_pip_bl_deactivate, | |
730 | ||
731 | .show_baseline = cyapa_gen6_show_baseline, | |
732 | .calibrate_store = cyapa_pip_do_calibrate, | |
733 | ||
734 | .initialize = cyapa_gen6_initialize, | |
735 | ||
736 | .state_parse = cyapa_pip_state_parse, | |
737 | .operational_check = cyapa_gen6_operational_check, | |
738 | ||
739 | .irq_handler = cyapa_pip_irq_handler, | |
740 | .irq_cmd_handler = cyapa_pip_irq_cmd_handler, | |
741 | .sort_empty_output_data = cyapa_empty_pip_output_data, | |
742 | .set_power_mode = cyapa_gen6_set_power_mode, | |
945525ee DD |
743 | |
744 | .set_proximity = cyapa_gen6_set_proximity, | |
c2c06c41 | 745 | }; |