/*
- Cypress APA trackpad with I2C interface
- Copyright © 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;
}
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;
}
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 Boo