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
43struct 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
51struct 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
64struct pip_fixed_info {
65	u8 silicon_id_high;
66	u8 silicon_id_low;
67	u8 family_id;
68};
69
70static u8 pip_get_bl_info[] = {
71	0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
72	0x00, 0x00, 0x70, 0x9E, 0x17
73};
74
75static 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
88static 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
136int 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
194static 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	/* 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
258static 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
290static 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
309static 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
320static 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
341static 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
388static 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
412static 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
427static 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, 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
550out:
551	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
552	return error;
553}
554
555static int cyapa_gen6_initialize(struct cyapa *cyapa)
556{
557	return 0;
558}
559
560static 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) - 2, &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(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
605static 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 = sysfs_emit(buf, "%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 += sysfs_emit_at(buf, size, "%d ", data[i]);
652	size += sysfs_emit_at(buf, size, "\n");
653
654resume_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
665static 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, 0, 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, 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(cyapa->product_id, 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
720out:
721	return error;
722}
723
724const 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