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