Release 4.7 drivers/input/mouse/cyapa_gen6.c
  
  
/*
 * Cypress APA trackpad with I2C interface
 *
 * Author: Dudley Du <dudl@cypress.com>
 *
 * Copyright (C) 2015 Cypress Semiconductor, Inc.
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file COPYING in the main directory of this archive for
 * more details.
 */
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/mutex.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/crc-itu-t.h>
#include "cyapa.h"
#define GEN6_ENABLE_CMD_IRQ	0x41
#define GEN6_DISABLE_CMD_IRQ	0x42
#define GEN6_ENABLE_DEV_IRQ	0x43
#define GEN6_DISABLE_DEV_IRQ	0x44
#define GEN6_POWER_MODE_ACTIVE		0x01
#define GEN6_POWER_MODE_LP_MODE1	0x02
#define GEN6_POWER_MODE_LP_MODE2	0x03
#define GEN6_POWER_MODE_BTN_ONLY	0x04
#define GEN6_SET_POWER_MODE_INTERVAL	0x47
#define GEN6_GET_POWER_MODE_INTERVAL	0x48
#define GEN6_MAX_RX_NUM 14
#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC	0x00
#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM		0x12
struct pip_app_cmd_head {
	
__le16 addr;
	
__le16 length;
	
u8 report_id;
	
u8 resv;  /* Reserved, must be 0 */
	
u8 cmd_code;  /* bit7: resv, set to 0; bit6~0: command code.*/
} 
__packed;
struct pip_app_resp_head {
	
__le16 length;
	
u8 report_id;
	
u8 resv;  /* Reserved, must be 0 */
	
u8 cmd_code;  /* bit7: TGL; bit6~0: command code.*/
	/*
         * The value of data_status can be the first byte of data or
         * the command status or the unsupported command code depending on the
         * requested command code.
        */
	
u8 data_status;
} 
__packed;
struct pip_fixed_info {
	
u8 silicon_id_high;
	
u8 silicon_id_low;
	
u8 family_id;
};
static u8 pip_get_bl_info[] = {
	0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
	0x00, 0x00, 0x70, 0x9E, 0x17
};
static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
		u8 *buf, int len)
{
	if (len != PIP_HID_DESCRIPTOR_SIZE)
		return false;
	if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
		buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
		return true;
	return false;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 49 | 100.00% | 1 | 100.00% | 
 | Total | 49 | 100.00% | 1 | 100.00% | 
static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
		struct pip_fixed_info *pip_info, bool is_bootloader)
{
	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
	int resp_len;
	u16 product_family;
	int error;
	if (is_bootloader) {
		/* Read Bootloader Information to determine Gen5 or Gen6. */
		resp_len = sizeof(resp_data);
		error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
				pip_get_bl_info, sizeof(pip_get_bl_info),
				resp_data, &resp_len,
				2000, cyapa_sort_tsg_pip_bl_resp_data,
				false);
		if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
			return error ? error : -EIO;
		pip_info->family_id = resp_data[8];
		pip_info->silicon_id_low = resp_data[10];
		pip_info->silicon_id_high = resp_data[11];
		return 0;
	}
	/* Get App System Information to determine Gen5 or Gen6. */
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
			resp_data, &resp_len,
			2000, cyapa_pip_sort_system_info_data, false);
	if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
		return error ? error : -EIO;
	product_family = get_unaligned_le16(&resp_data[7]);
	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
		PIP_PRODUCT_FAMILY_TRACKPAD)
		return -EINVAL;
	pip_info->family_id = resp_data[19];
	pip_info->silicon_id_low = resp_data[21];
	pip_info->silicon_id_high = resp_data[22];
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 220 | 100.00% | 1 | 100.00% | 
 | Total | 220 | 100.00% | 1 | 100.00% | 
int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
{
	u8 cmd[] = { 0x01, 0x00};
	struct pip_fixed_info pip_info;
	u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
	int resp_len;
	bool is_bootloader;
	int error;
	cyapa->state = CYAPA_STATE_NO_DEVICE;
	/* Try to wake from it deep sleep state if it is. */
	cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
	/* Empty the buffer queue to get fresh data with later commands. */
	cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
	/*
         * Read description info from trackpad device to determine running in
         * APP mode or Bootloader mode.
         */
	resp_len = PIP_HID_DESCRIPTOR_SIZE;
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
			cmd, sizeof(cmd),
			resp_data, &resp_len,
			300,
			cyapa_sort_pip_hid_descriptor_data,
			false);
	if (error)
		return error;
	if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
		is_bootloader = true;
	else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
		is_bootloader = false;
	else
		return -EAGAIN;
	/* Get PIP fixed information to determine Gen5 or Gen6. */
	memset(&pip_info, 0, sizeof(struct pip_fixed_info));
	error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
	if (error)
		return error;
	if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
		cyapa->gen = CYAPA_GEN6;
		cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
					     : CYAPA_STATE_GEN6_APP;
	} else if (pip_info.family_id == 0x91 &&
		   pip_info.silicon_id_high == 0x02) {
		cyapa->gen = CYAPA_GEN5;
		cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
					     : CYAPA_STATE_GEN5_APP;
	}
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 243 | 100.00% | 1 | 100.00% | 
 | Total | 243 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
{
	u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
	int resp_len;
	u16 product_family;
	u8 rotat_align;
	int error;
	/* Get App System Information to determine Gen5 or Gen6. */
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
			pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
			resp_data, &resp_len,
			2000, cyapa_pip_sort_system_info_data, false);
	if (error || resp_len < sizeof(resp_data))
		return error ? error : -EIO;
	product_family = get_unaligned_le16(&resp_data[7]);
	if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
		PIP_PRODUCT_FAMILY_TRACKPAD)
		return -EINVAL;
	cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
			      PIP_BL_PLATFORM_VER_MASK;
	cyapa->fw_maj_ver = resp_data[9];
	cyapa->fw_min_ver = resp_data[10];
	cyapa->electrodes_x = resp_data[33];
	cyapa->electrodes_y = resp_data[34];
	cyapa->physical_size_x =  get_unaligned_le16(&resp_data[35]) / 100;
	cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
	cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
	cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
	cyapa->max_z = get_unaligned_le16(&resp_data[43]);
	cyapa->x_origin = resp_data[45] & 0x01;
	cyapa->y_origin = resp_data[46] & 0x01;
	cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
	memcpy(&cyapa->product_id[0], &resp_data[51], 5);
	cyapa->product_id[5] = '-';
	memcpy(&cyapa->product_id[6], &resp_data[56], 6);
	cyapa->product_id[12] = '-';
	memcpy(&cyapa->product_id[13], &resp_data[62], 2);
	cyapa->product_id[15] = '\0';
	/* Get the number of Rx electrodes. */
	rotat_align = resp_data[68];
	cyapa->electrodes_rx =
		rotat_align ? cyapa->electrodes_y : cyapa->electrodes_x;
	cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
	if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
		!cyapa->physical_size_x || !cyapa->physical_size_y ||
		!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
		return -EINVAL;
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 425 | 100.00% | 2 | 100.00% | 
 | Total | 425 | 100.00% | 2 | 100.00% | 
static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
{
	u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
	int resp_len;
	int error;
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
			pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
			resp_data, &resp_len,
			500, cyapa_sort_tsg_pip_bl_resp_data, false);
	if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
		!PIP_CMD_COMPLETE_SUCCESS(resp_data))
		return error ? error : -EIO;
	cyapa->fw_maj_ver = resp_data[8];
	cyapa->fw_min_ver = resp_data[9];
	cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
			      PIP_BL_PLATFORM_VER_MASK;
	memcpy(&cyapa->product_id[0], &resp_data[13], 5);
	cyapa->product_id[5] = '-';
	memcpy(&cyapa->product_id[6], &resp_data[18], 6);
	cyapa->product_id[12] = '-';
	memcpy(&cyapa->product_id[13], &resp_data[24], 2);
	cyapa->product_id[15] = '\0';
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 194 | 100.00% | 1 | 100.00% | 
 | Total | 194 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
{
	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
	u8 resp_data[6];
	int resp_len;
	int error;
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
			resp_data, &resp_len,
			500, cyapa_sort_tsg_pip_app_resp_data, false);
	if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
			!PIP_CMD_COMPLETE_SUCCESS(resp_data)
			)
		return error < 0 ? error : -EINVAL;
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 109 | 100.00% | 1 | 100.00% | 
 | Total | 109 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
{
	int error;
	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
	error = cyapa_pip_set_proximity(cyapa, enable);
	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
	return error;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 43 | 100.00% | 1 | 100.00% | 
 | Total | 43 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
{
	u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
	u8 resp_data[6];
	int resp_len;
	int error;
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
			resp_data, &resp_len,
			500, cyapa_sort_tsg_pip_app_resp_data, false);
	if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
		return error < 0 ? error : -EINVAL;
	/* New power state applied in device not match the set power state. */
	if (resp_data[5] != power_mode)
		return -EAGAIN;
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 119 | 100.00% | 1 | 100.00% | 
 | Total | 119 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
		struct gen6_interval_setting *interval_setting)
{
	struct gen6_set_interval_cmd {
		__le16 addr;
		__le16 length;
		u8 report_id;
		u8 rsvd;  /* Reserved, must be 0 */
		u8 cmd_code;
		__le16 active_interval;
		__le16 lp1_interval;
		__le16 lp2_interval;
	} __packed set_interval_cmd;
	u8 resp_data[11];
	int resp_len;
	int error;
	memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
	put_unaligned_le16(sizeof(set_interval_cmd) - 2,
			   &set_interval_cmd.length);
	set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
	set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
	put_unaligned_le16(interval_setting->active_interval,
			   &set_interval_cmd.active_interval);
	put_unaligned_le16(interval_setting->lp1_interval,
			   &set_interval_cmd.lp1_interval);
	put_unaligned_le16(interval_setting->lp2_interval,
			   &set_interval_cmd.lp2_interval);
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
			(u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
			resp_data, &resp_len,
			500, cyapa_sort_tsg_pip_app_resp_data, false);
	if (error ||
		!VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
		return error < 0 ? error : -EINVAL;
	/* Get the real set intervals from response. */
	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 248 | 100.00% | 1 | 100.00% | 
 | Total | 248 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
		struct gen6_interval_setting *interval_setting)
{
	u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
		     GEN6_GET_POWER_MODE_INTERVAL };
	u8 resp_data[11];
	int resp_len;
	int error;
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
			resp_data, &resp_len,
			500, cyapa_sort_tsg_pip_app_resp_data, false);
	if (error ||
		!VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
		return error < 0 ? error : -EINVAL;
	interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
	interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
	interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 144 | 100.00% | 1 | 100.00% | 
 | Total | 144 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
{
	u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
	if (state == PIP_DEEP_SLEEP_STATE_ON)
		/*
                 * Send ping command to notify device prepare for wake up
                 * when it's in deep sleep mode. At this time, device will
                 * response nothing except an I2C NAK.
                 */
		cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
	return cyapa_pip_deep_sleep(cyapa, state);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 61 | 100.00% | 1 | 100.00% | 
 | Total | 61 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
		u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage)
{
	struct device *dev = &cyapa->client->dev;
	struct gen6_interval_setting *interval_setting =
			&cyapa->gen6_interval_setting;
	u8 lp_mode;
	int error;
	if (cyapa->state != CYAPA_STATE_GEN6_APP)
		return 0;
	if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
		/*
                 * Assume TP in deep sleep mode when driver is loaded,
                 * avoid driver unload and reload command IO issue caused by TP
                 * has been set into deep sleep mode when unloading.
                 */
		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
	}
	if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
		PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
		PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
	if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
		if (power_mode == PWR_MODE_OFF ||
			power_mode == PWR_MODE_FULL_ACTIVE ||
			power_mode == PWR_MODE_BTN_ONLY ||
			PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
			/* Has in correct power mode state, early return. */
			return 0;
		}
	}
	if (power_mode == PWR_MODE_OFF) {
		cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
		if (error) {
			dev_err(dev, "enter deep sleep fail: %d\n", error);
			return error;
		}
		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
		return 0;
	}
	/*
         * When trackpad in power off mode, it cannot change to other power
         * state directly, must be wake up from sleep firstly, then
         * continue to do next power sate change.
         */
	if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
		error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
		if (error) {
			dev_err(dev, "deep sleep wake fail: %d\n", error);
			return error;
		}
	}
	/*
         * Disable device assert interrupts for command response to avoid
         * disturbing system suspending or hibernating process.
         */
	cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
	if (power_mode == PWR_MODE_FULL_ACTIVE) {
		error = cyapa_gen6_change_power_state(cyapa,
				GEN6_POWER_MODE_ACTIVE);
		if (error) {
			dev_err(dev, "change to active fail: %d\n", error);
			goto out;
		}
		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
		/* Sync the interval setting from device. */
		cyapa_gen6_get_interval_setting(cyapa, interval_setting);
	} else if (power_mode == PWR_MODE_BTN_ONLY) {
		error = cyapa_gen6_change_power_state(cyapa,
				GEN6_POWER_MODE_BTN_ONLY);
		if (error) {
			dev_err(dev, "fail to button only mode: %d\n", error);
			goto out;
		}
		PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
	} else {
		/*
                 * Gen6 internally supports to 2 low power scan interval time,
                 * so can help to switch power mode quickly.
                 * such as runtime suspend and system suspend.
                 */
		if (interval_setting->lp1_interval == sleep_time) {
			lp_mode = GEN6_POWER_MODE_LP_MODE1;
		} else if (interval_setting->lp2_interval == sleep_time) {
			lp_mode = GEN6_POWER_MODE_LP_MODE2;
		} else {
			if (interval_setting->lp1_interval == 0) {
				interval_setting->lp1_interval = sleep_time;
				lp_mode = GEN6_POWER_MODE_LP_MODE1;
			} else {
				interval_setting->lp2_interval = sleep_time;
				lp_mode = GEN6_POWER_MODE_LP_MODE2;
			}
			cyapa_gen6_set_interval_setting(cyapa,
							interval_setting);
		}
		error = cyapa_gen6_change_power_state(cyapa, lp_mode);
		if (error) {
			dev_err(dev, "set power state to 0x%02x failed: %d\n",
				lp_mode, error);
			goto out;
		}
		PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
		PIP_DEV_SET_PWR_STATE(cyapa,
			cyapa_sleep_time_to_pwr_cmd(sleep_time));
	}
out:
	cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
	return error;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 464 | 100.00% | 3 | 100.00% | 
 | Total | 464 | 100.00% | 3 | 100.00% | 
static int cyapa_gen6_initialize(struct cyapa *cyapa)
{
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 14 | 100.00% | 1 | 100.00% | 
 | Total | 14 | 100.00% | 1 | 100.00% | 
static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
		u16 read_offset, u16 read_len, u8 data_id,
		u8 *data, int *data_buf_lens)
{
	struct retrieve_data_struct_cmd {
		struct pip_app_cmd_head head;
		__le16 read_offset;
		__le16 read_length;
		u8 data_id;
	} __packed cmd;
	u8 resp_data[GEN6_MAX_RX_NUM + 10];
	int resp_len;
	int error;
	memset(&cmd, 0, sizeof(cmd));
	put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
	put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2);
	cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
	cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
	put_unaligned_le16(read_offset, &cmd.read_offset);
	put_unaligned_le16(read_len, &cmd.read_length);
	cmd.data_id = data_id;
	resp_len = sizeof(resp_data);
	error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
				(u8 *)&cmd, sizeof(cmd),
				resp_data, &resp_len,
				500, cyapa_sort_tsg_pip_app_resp_data,
				true);
	if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
		resp_data[6] != data_id ||
		!VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
		return (error < 0) ? error : -EAGAIN;
	read_len = get_unaligned_le16(&resp_data[7]);
	if (*data_buf_lens < read_len) {
		*data_buf_lens = read_len;
		return -ENOBUFS;
	}
	memcpy(data, &resp_data[10], read_len);
	*data_buf_lens = read_len;
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 270 | 100.00% | 1 | 100.00% | 
 | Total | 270 | 100.00% | 1 | 100.00% | 
static ssize_t cyapa_gen6_show_baseline(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct cyapa *cyapa = dev_get_drvdata(dev);
	u8 data[GEN6_MAX_RX_NUM];
	int data_len;
	int size = 0;
	int i;
	int error;
	int resume_error;
	if (!cyapa_is_pip_app_mode(cyapa))
		return -EBUSY;
	/* 1. Suspend Scanning*/
	error = cyapa_pip_suspend_scanning(cyapa);
	if (error)
		return error;
	/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
	data_len = sizeof(data);
	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
			GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
			data, &data_len);
	if (error)
		goto resume_scanning;
	size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
			data[0],  /* RX Attenuator Mutual */
			data[1],  /* IDAC Mutual */
			data[2],  /* RX Attenuator Self RX */
			data[3],  /* IDAC Self RX */
			data[4],  /* RX Attenuator Self TX */
			data[5]	  /* IDAC Self TX */
			);
	/* 3. Read Attenuator Trim. */
	data_len = sizeof(data);
	error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
			GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
			data, &data_len);
	if (error)
		goto resume_scanning;
	/* set attenuator trim values. */
	for (i = 0; i < data_len; i++)
		size += scnprintf(buf + size, PAGE_SIZE - size,	"%d ", data[i]);
	size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
resume_scanning:
	/* 4. Resume Scanning*/
	resume_error = cyapa_pip_resume_scanning(cyapa);
	if (resume_error || error) {
		memset(buf, 0, PAGE_SIZE);
		return resume_error ? resume_error : error;
	}
	return size;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 279 | 100.00% | 1 | 100.00% | 
 | Total | 279 | 100.00% | 1 | 100.00% | 
static int cyapa_gen6_operational_check(struct cyapa *cyapa)
{
	struct device *dev = &cyapa->client->dev;
	int error;
	if (cyapa->gen != CYAPA_GEN6)
		return -ENODEV;
	switch (cyapa->state) {
	case CYAPA_STATE_GEN6_BL:
		error = cyapa_pip_bl_exit(cyapa);
		if (error) {
			/* Try to update trackpad product information. */
			cyapa_gen6_bl_read_app_info(cyapa);
			goto out;
		}
		cyapa->state = CYAPA_STATE_GEN6_APP;
	case CYAPA_STATE_GEN6_APP:
		/*
                 * If trackpad device in deep sleep mode,
                 * the app command will fail.
                 * So always try to reset trackpad device to full active when
                 * the device state is required.
                 */
		error = cyapa_gen6_set_power_mode(cyapa,
				PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE);
		if (error)
			dev_warn(dev, "%s: failed to set power active mode.\n",
				__func__);
		/* By default, the trackpad proximity function is enabled. */
		error = cyapa_pip_set_proximity(cyapa, true);
		if (error)
			dev_warn(dev, "%s: failed to enable proximity.\n",
				__func__);
		/* Get trackpad product information. */
		error = cyapa_gen6_read_sys_info(cyapa);
		if (error)
			goto out;
		/* Only support product ID starting with CYTRA */
		if (memcmp(cyapa->product_id, product_id,
				strlen(product_id)) != 0) {
			dev_err(dev, "%s: unknown product ID (%s)\n",
				__func__, cyapa->product_id);
			error = -EINVAL;
		}
		break;
	default:
		error = -EINVAL;
	}
out:
	return error;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 196 | 100.00% | 4 | 100.00% | 
 | Total | 196 | 100.00% | 4 | 100.00% | 
const struct cyapa_dev_ops cyapa_gen6_ops = {
	.check_fw = cyapa_pip_check_fw,
	.bl_enter = cyapa_pip_bl_enter,
	.bl_initiate = cyapa_pip_bl_initiate,
	.update_fw = cyapa_pip_do_fw_update,
	.bl_activate = cyapa_pip_bl_activate,
	.bl_deactivate = cyapa_pip_bl_deactivate,
	.show_baseline = cyapa_gen6_show_baseline,
	.calibrate_store = cyapa_pip_do_calibrate,
	.initialize = cyapa_gen6_initialize,
	.state_parse = cyapa_pip_state_parse,
	.operational_check = cyapa_gen6_operational_check,
	.irq_handler = cyapa_pip_irq_handler,
	.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
	.sort_empty_output_data = cyapa_empty_pip_output_data,
	.set_power_mode = cyapa_gen6_set_power_mode,
	.set_proximity = cyapa_gen6_set_proximity,
};
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| dudley du | dudley du | 3342 | 100.00% | 5 | 100.00% | 
 | Total | 3342 | 100.00% | 5 | 100.00% | 
  
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.