xref: /openbmc/linux/drivers/input/mouse/cyapa_gen6.c (revision a2cce7a9)
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 
244 	rotat_align = resp_data[68];
245 	if (rotat_align) {
246 		cyapa->electrodes_rx = cyapa->electrodes_y;
247 		cyapa->electrodes_rx = cyapa->electrodes_y;
248 	} else {
249 		cyapa->electrodes_rx = cyapa->electrodes_x;
250 		cyapa->electrodes_rx = cyapa->electrodes_y;
251 	}
252 	cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
253 
254 	if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
255 		!cyapa->physical_size_x || !cyapa->physical_size_y ||
256 		!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
257 		return -EINVAL;
258 
259 	return 0;
260 }
261 
262 static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
263 {
264 	u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
265 	int resp_len;
266 	int error;
267 
268 	resp_len = sizeof(resp_data);
269 	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
270 			pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
271 			resp_data, &resp_len,
272 			500, cyapa_sort_tsg_pip_bl_resp_data, false);
273 	if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
274 		!PIP_CMD_COMPLETE_SUCCESS(resp_data))
275 		return error ? error : -EIO;
276 
277 	cyapa->fw_maj_ver = resp_data[8];
278 	cyapa->fw_min_ver = resp_data[9];
279 
280 	cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
281 			      PIP_BL_PLATFORM_VER_MASK;
282 
283 	memcpy(&cyapa->product_id[0], &resp_data[13], 5);
284 	cyapa->product_id[5] = '-';
285 	memcpy(&cyapa->product_id[6], &resp_data[18], 6);
286 	cyapa->product_id[12] = '-';
287 	memcpy(&cyapa->product_id[13], &resp_data[24], 2);
288 	cyapa->product_id[15] = '\0';
289 
290 	return 0;
291 
292 }
293 
294 static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
295 {
296 	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
297 	u8 resp_data[6];
298 	int resp_len;
299 	int error;
300 
301 	resp_len = sizeof(resp_data);
302 	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
303 			resp_data, &resp_len,
304 			500, cyapa_sort_tsg_pip_app_resp_data, false);
305 	if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
306 			!PIP_CMD_COMPLETE_SUCCESS(resp_data)
307 			)
308 		return error < 0 ? error : -EINVAL;
309 
310 	return 0;
311 }
312 
313 static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
314 {
315 	int error;
316 
317 	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
318 	error = cyapa_pip_set_proximity(cyapa, enable);
319 	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
320 
321 	return error;
322 }
323 
324 static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
325 {
326 	u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
327 	u8 resp_data[6];
328 	int resp_len;
329 	int error;
330 
331 	resp_len = sizeof(resp_data);
332 	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
333 			resp_data, &resp_len,
334 			500, cyapa_sort_tsg_pip_app_resp_data, false);
335 	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
336 		return error < 0 ? error : -EINVAL;
337 
338 	/* New power state applied in device not match the set power state. */
339 	if (resp_data[5] != power_mode)
340 		return -EAGAIN;
341 
342 	return 0;
343 }
344 
345 static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
346 		struct gen6_interval_setting *interval_setting)
347 {
348 	struct gen6_set_interval_cmd {
349 		__le16 addr;
350 		__le16 length;
351 		u8 report_id;
352 		u8 rsvd;  /* Reserved, must be 0 */
353 		u8 cmd_code;
354 		__le16 active_interval;
355 		__le16 lp1_interval;
356 		__le16 lp2_interval;
357 	} __packed set_interval_cmd;
358 	u8 resp_data[11];
359 	int resp_len;
360 	int error;
361 
362 	memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
363 	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
364 	put_unaligned_le16(sizeof(set_interval_cmd) - 2,
365 			   &set_interval_cmd.length);
366 	set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
367 	set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
368 	put_unaligned_le16(interval_setting->active_interval,
369 			   &set_interval_cmd.active_interval);
370 	put_unaligned_le16(interval_setting->lp1_interval,
371 			   &set_interval_cmd.lp1_interval);
372 	put_unaligned_le16(interval_setting->lp2_interval,
373 			   &set_interval_cmd.lp2_interval);
374 
375 	resp_len = sizeof(resp_data);
376 	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
377 			(u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
378 			resp_data, &resp_len,
379 			500, cyapa_sort_tsg_pip_app_resp_data, false);
380 	if (error ||
381 		!VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
382 		return error < 0 ? error : -EINVAL;
383 
384 	/* Get the real set intervals from response. */
385 	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
386 	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
387 	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
388 
389 	return 0;
390 }
391 
392 static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
393 		struct gen6_interval_setting *interval_setting)
394 {
395 	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
396 		     GEN6_GET_POWER_MODE_INTERVAL };
397 	u8 resp_data[11];
398 	int resp_len;
399 	int error;
400 
401 	resp_len = sizeof(resp_data);
402 	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
403 			resp_data, &resp_len,
404 			500, cyapa_sort_tsg_pip_app_resp_data, false);
405 	if (error ||
406 		!VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
407 		return error < 0 ? error : -EINVAL;
408 
409 	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
410 	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
411 	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
412 
413 	return 0;
414 }
415 
416 static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
417 {
418 	u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
419 
420 	if (state == PIP_DEEP_SLEEP_STATE_ON)
421 		/*
422 		 * Send ping command to notify device prepare for wake up
423 		 * when it's in deep sleep mode. At this time, device will
424 		 * response nothing except an I2C NAK.
425 		 */
426 		cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
427 
428 	return cyapa_pip_deep_sleep(cyapa, state);
429 }
430 
431 static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
432 		u8 power_mode, u16 sleep_time, bool is_suspend)
433 {
434 	struct device *dev = &cyapa->client->dev;
435 	struct gen6_interval_setting *interval_setting =
436 			&cyapa->gen6_interval_setting;
437 	u8 lp_mode;
438 	int error;
439 
440 	if (cyapa->state != CYAPA_STATE_GEN6_APP)
441 		return 0;
442 
443 	if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
444 		/*
445 		 * Assume TP in deep sleep mode when driver is loaded,
446 		 * avoid driver unload and reload command IO issue caused by TP
447 		 * has been set into deep sleep mode when unloading.
448 		 */
449 		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
450 	}
451 
452 	if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
453 		PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
454 		PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
455 
456 	if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
457 		if (power_mode == PWR_MODE_OFF ||
458 			power_mode == PWR_MODE_FULL_ACTIVE ||
459 			power_mode == PWR_MODE_BTN_ONLY ||
460 			PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
461 			/* Has in correct power mode state, early return. */
462 			return 0;
463 		}
464 	}
465 
466 	if (power_mode == PWR_MODE_OFF) {
467 		cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
468 
469 		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
470 		if (error) {
471 			dev_err(dev, "enter deep sleep fail: %d\n", error);
472 			return error;
473 		}
474 
475 		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
476 		return 0;
477 	}
478 
479 	/*
480 	 * When trackpad in power off mode, it cannot change to other power
481 	 * state directly, must be wake up from sleep firstly, then
482 	 * continue to do next power sate change.
483 	 */
484 	if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
485 		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
486 		if (error) {
487 			dev_err(dev, "deep sleep wake fail: %d\n", error);
488 			return error;
489 		}
490 	}
491 
492 	/*
493 	 * Disable device assert interrupts for command response to avoid
494 	 * disturbing system suspending or hibernating process.
495 	 */
496 	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
497 
498 	if (power_mode == PWR_MODE_FULL_ACTIVE) {
499 		error = cyapa_gen6_change_power_state(cyapa,
500 				GEN6_POWER_MODE_ACTIVE);
501 		if (error) {
502 			dev_err(dev, "change to active fail: %d\n", error);
503 			goto out;
504 		}
505 
506 		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
507 
508 		/* Sync the interval setting from device. */
509 		cyapa_gen6_get_interval_setting(cyapa, interval_setting);
510 
511 	} else if (power_mode == PWR_MODE_BTN_ONLY) {
512 		error = cyapa_gen6_change_power_state(cyapa,
513 				GEN6_POWER_MODE_BTN_ONLY);
514 		if (error) {
515 			dev_err(dev, "fail to button only mode: %d\n", error);
516 			goto out;
517 		}
518 
519 		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
520 	} else {
521 		/*
522 		 * Gen6 internally supports to 2 low power scan interval time,
523 		 * so can help to switch power mode quickly.
524 		 * such as runtime suspend and system suspend.
525 		 */
526 		if (interval_setting->lp1_interval == sleep_time) {
527 			lp_mode = GEN6_POWER_MODE_LP_MODE1;
528 		} else if (interval_setting->lp2_interval == sleep_time) {
529 			lp_mode = GEN6_POWER_MODE_LP_MODE2;
530 		} else {
531 			if (interval_setting->lp1_interval == 0) {
532 				interval_setting->lp1_interval = sleep_time;
533 				lp_mode = GEN6_POWER_MODE_LP_MODE1;
534 			} else {
535 				interval_setting->lp2_interval = sleep_time;
536 				lp_mode = GEN6_POWER_MODE_LP_MODE2;
537 			}
538 			cyapa_gen6_set_interval_setting(cyapa,
539 							interval_setting);
540 		}
541 
542 		error = cyapa_gen6_change_power_state(cyapa, lp_mode);
543 		if (error) {
544 			dev_err(dev, "set power state to 0x%02x failed: %d\n",
545 				lp_mode, error);
546 			goto out;
547 		}
548 
549 		PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
550 		PIP_DEV_SET_PWR_STATE(cyapa,
551 			cyapa_sleep_time_to_pwr_cmd(sleep_time));
552 	}
553 
554 out:
555 	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
556 	return error;
557 }
558 
559 static int cyapa_gen6_initialize(struct cyapa *cyapa)
560 {
561 	return 0;
562 }
563 
564 static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
565 		u16 read_offset, u16 read_len, u8 data_id,
566 		u8 *data, int *data_buf_lens)
567 {
568 	struct retrieve_data_struct_cmd {
569 		struct pip_app_cmd_head head;
570 		__le16 read_offset;
571 		__le16 read_length;
572 		u8 data_id;
573 	} __packed cmd;
574 	u8 resp_data[GEN6_MAX_RX_NUM + 10];
575 	int resp_len;
576 	int error;
577 
578 	memset(&cmd, 0, sizeof(cmd));
579 	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
580 	put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2);
581 	cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
582 	cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
583 	put_unaligned_le16(read_offset, &cmd.read_offset);
584 	put_unaligned_le16(read_len, &cmd.read_length);
585 	cmd.data_id = data_id;
586 
587 	resp_len = sizeof(resp_data);
588 	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
589 				(u8 *)&cmd, sizeof(cmd),
590 				resp_data, &resp_len,
591 				500, cyapa_sort_tsg_pip_app_resp_data,
592 				true);
593 	if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
594 		resp_data[6] != data_id ||
595 		!VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
596 		return (error < 0) ? error : -EAGAIN;
597 
598 	read_len = get_unaligned_le16(&resp_data[7]);
599 	if (*data_buf_lens < read_len) {
600 		*data_buf_lens = read_len;
601 		return -ENOBUFS;
602 	}
603 
604 	memcpy(data, &resp_data[10], read_len);
605 	*data_buf_lens = read_len;
606 	return 0;
607 }
608 
609 static ssize_t cyapa_gen6_show_baseline(struct device *dev,
610 		struct device_attribute *attr, char *buf)
611 {
612 	struct cyapa *cyapa = dev_get_drvdata(dev);
613 	u8 data[GEN6_MAX_RX_NUM];
614 	int data_len;
615 	int size = 0;
616 	int i;
617 	int error;
618 	int resume_error;
619 
620 	if (!cyapa_is_pip_app_mode(cyapa))
621 		return -EBUSY;
622 
623 	/* 1. Suspend Scanning*/
624 	error = cyapa_pip_suspend_scanning(cyapa);
625 	if (error)
626 		return error;
627 
628 	/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
629 	data_len = sizeof(data);
630 	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
631 			GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
632 			data, &data_len);
633 	if (error)
634 		goto resume_scanning;
635 
636 	size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
637 			data[0],  /* RX Attenuator Mutual */
638 			data[1],  /* IDAC Mutual */
639 			data[2],  /* RX Attenuator Self RX */
640 			data[3],  /* IDAC Self RX */
641 			data[4],  /* RX Attenuator Self TX */
642 			data[5]	  /* IDAC Self TX */
643 			);
644 
645 	/* 3. Read Attenuator Trim. */
646 	data_len = sizeof(data);
647 	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
648 			GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
649 			data, &data_len);
650 	if (error)
651 		goto resume_scanning;
652 
653 	/* set attenuator trim values. */
654 	for (i = 0; i < data_len; i++)
655 		size += scnprintf(buf + size, PAGE_SIZE - size,	"%d ", data[i]);
656 	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
657 
658 resume_scanning:
659 	/* 4. Resume Scanning*/
660 	resume_error = cyapa_pip_resume_scanning(cyapa);
661 	if (resume_error || error) {
662 		memset(buf, 0, PAGE_SIZE);
663 		return resume_error ? resume_error : error;
664 	}
665 
666 	return size;
667 }
668 
669 static int cyapa_gen6_operational_check(struct cyapa *cyapa)
670 {
671 	struct device *dev = &cyapa->client->dev;
672 	int error;
673 
674 	if (cyapa->gen != CYAPA_GEN6)
675 		return -ENODEV;
676 
677 	switch (cyapa->state) {
678 	case CYAPA_STATE_GEN6_BL:
679 		error = cyapa_pip_bl_exit(cyapa);
680 		if (error) {
681 			/* Try to update trackpad product information. */
682 			cyapa_gen6_bl_read_app_info(cyapa);
683 			goto out;
684 		}
685 
686 		cyapa->state = CYAPA_STATE_GEN6_APP;
687 
688 	case CYAPA_STATE_GEN6_APP:
689 		/*
690 		 * If trackpad device in deep sleep mode,
691 		 * the app command will fail.
692 		 * So always try to reset trackpad device to full active when
693 		 * the device state is required.
694 		 */
695 		error = cyapa_gen6_set_power_mode(cyapa,
696 				PWR_MODE_FULL_ACTIVE, 0, false);
697 		if (error)
698 			dev_warn(dev, "%s: failed to set power active mode.\n",
699 				__func__);
700 
701 		/* By default, the trackpad proximity function is enabled. */
702 		error = cyapa_pip_set_proximity(cyapa, true);
703 		if (error)
704 			dev_warn(dev, "%s: failed to enable proximity.\n",
705 				__func__);
706 
707 		/* Get trackpad product information. */
708 		error = cyapa_gen6_read_sys_info(cyapa);
709 		if (error)
710 			goto out;
711 		/* Only support product ID starting with CYTRA */
712 		if (memcmp(cyapa->product_id, product_id,
713 				strlen(product_id)) != 0) {
714 			dev_err(dev, "%s: unknown product ID (%s)\n",
715 				__func__, cyapa->product_id);
716 			error = -EINVAL;
717 		}
718 		break;
719 	default:
720 		error = -EINVAL;
721 	}
722 
723 out:
724 	return error;
725 }
726 
727 const struct cyapa_dev_ops cyapa_gen6_ops = {
728 	.check_fw = cyapa_pip_check_fw,
729 	.bl_enter = cyapa_pip_bl_enter,
730 	.bl_initiate = cyapa_pip_bl_initiate,
731 	.update_fw = cyapa_pip_do_fw_update,
732 	.bl_activate = cyapa_pip_bl_activate,
733 	.bl_deactivate = cyapa_pip_bl_deactivate,
734 
735 	.show_baseline = cyapa_gen6_show_baseline,
736 	.calibrate_store = cyapa_pip_do_calibrate,
737 
738 	.initialize = cyapa_gen6_initialize,
739 
740 	.state_parse = cyapa_pip_state_parse,
741 	.operational_check = cyapa_gen6_operational_check,
742 
743 	.irq_handler = cyapa_pip_irq_handler,
744 	.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
745 	.sort_empty_output_data = cyapa_empty_pip_output_data,
746 	.set_power_mode = cyapa_gen6_set_power_mode,
747 
748 	.set_proximity = cyapa_gen6_set_proximity,
749 };
750