/*
* Cypress APA trackpad with I2C interface
*
* Author: Dudley Du <dudl@cypress.com>
* Further cleanup and restructuring by:
* Daniel Kurtz <djkurtz@chromium.org>
* Benson Leung <bleung@chromium.org>
*
* Copyright (C) 2011-2015 Cypress Semiconductor, Inc.
* Copyright (C) 2011-2012 Google, 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/module.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include "cyapa.h"
#define GEN3_MAX_FINGERS 5
#define GEN3_FINGER_NUM(x) (((x) >> 4) & 0x07)
#define BLK_HEAD_BYTES 32
/* Macro for register map group offset. */
#define PRODUCT_ID_SIZE 16
#define QUERY_DATA_SIZE 27
#define REG_PROTOCOL_GEN_QUERY_OFFSET 20
#define REG_OFFSET_DATA_BASE 0x0000
#define REG_OFFSET_COMMAND_BASE 0x0028
#define REG_OFFSET_QUERY_BASE 0x002a
#define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE
#define OP_RECALIBRATION_MASK 0x80
#define OP_REPORT_BASELINE_MASK 0x40
#define REG_OFFSET_MAX_BASELINE 0x0026
#define REG_OFFSET_MIN_BASELINE 0x0027
#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1)
#define SET_POWER_MODE_DELAY 10000 /* Unit: us */
#define SET_POWER_MODE_TRIES 5
#define GEN3_BL_CMD_CHECKSUM_SEED 0xff
#define GEN3_BL_CMD_INITIATE_BL 0x38
#define GEN3_BL_CMD_WRITE_BLOCK 0x39
#define GEN3_BL_CMD_VERIFY_BLOCK 0x3a
#define GEN3_BL_CMD_TERMINATE_BL 0x3b
#define GEN3_BL_CMD_LAUNCH_APP 0xa5
/*
* CYAPA trackpad device states.
* Used in register 0x00, bit1-0, DeviceStatus field.
* Other values indicate device is in an abnormal state and must be reset.
*/
#define CYAPA_DEV_NORMAL 0x03
#define CYAPA_DEV_BUSY 0x01
#define CYAPA_FW_BLOCK_SIZE 64
#define CYAPA_FW_READ_SIZE 16
#define CYAPA_FW_HDR_START 0x0780
#define CYAPA_FW_HDR_BLOCK_COUNT 2
#define CYAPA_FW_HDR_BLOCK_START (CYAPA_FW_HDR_START / CYAPA_FW_BLOCK_SIZE)
#define CYAPA_FW_HDR_SIZE (CYAPA_FW_HDR_BLOCK_COUNT * \
CYAPA_FW_BLOCK_SIZE)
#define CYAPA_FW_DATA_START 0x0800
#define CYAPA_FW_DATA_BLOCK_COUNT 480
#define CYAPA_FW_DATA_BLOCK_START (CYAPA_FW_DATA_START / CYAPA_FW_BLOCK_SIZE)
#define CYAPA_FW_DATA_SIZE (CYAPA_FW_DATA_BLOCK_COUNT * \
CYAPA_FW_BLOCK_SIZE)
#define CYAPA_FW_SIZE (CYAPA_FW_HDR_SIZE + CYAPA_FW_DATA_SIZE)
#define CYAPA_CMD_LEN 16
#define GEN3_BL_IDLE_FW_MAJ_VER_OFFSET 0x0b
#define GEN3_BL_IDLE_FW_MIN_VER_OFFSET (GEN3_BL_IDLE_FW_MAJ_VER_OFFSET + 1)
struct cyapa_touch {
/*
* high bits or x/y position value
* bit 7 - 4: high 4 bits of x position value
* bit 3 - 0: high 4 bits of y position value
*/
u8 xy_hi;
u8 x_lo; /* low 8 bits of x position value. */
u8 y_lo; /* low 8 bits of y position value. */
u8 pressure;
/* id range is 1 - 15. It is incremented with every new touch. */
u8 id;
} __packed;
struct cyapa_reg_data {
/*
* bit 0 - 1: device status
* bit 3 - 2: power mode
* bit 6 - 4: reserved
* bit 7: interrupt valid bit
*/
u8 device_status;
/*
* bit 7 - 4: number of fingers currently touching pad
* bit 3: valid data check bit
* bit 2: middle mechanism button state if exists
* bit 1: right mechanism button state if exists
* bit 0: left mechanism button state if exists
*/
u8 finger_btn;
/* CYAPA reports up to 5 touches per packet. */
struct cyapa_touch touches[5];
} __packed;
struct gen3_write_block_cmd {
u8 checksum_seed; /* Always be 0xff */
u8 cmd_code; /* command code: 0x39 */
u8 key[8]; /* 8-byte security key */
__be16 block_num;
u8 block_data[CYAPA_FW_BLOCK_SIZE];
u8 block_checksum; /* Calculated using bytes 12 - 75 */
u8 cmd_checksum; /* Calculated using bytes 0-76 */
} __packed;
static const u8 security_key[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
static const u8 bl_activate[] = { 0x00, 0xff, 0x38, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07 };
static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03,
0x04, 0x05, 0x06, 0x07 };
static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07 };
/* for byte read/write command */
#define CMD_RESET 0
#define CMD_POWER_MODE 1
#define CMD_DEV_STATUS 2
#define CMD_REPORT_MAX_BASELINE 3
#define CMD_REPORT_MIN_BASELINE 4
#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1)
#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET)
#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE)
#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS)
#define CYAPA_SMBUS_MAX_BASELINE SMBUS_BYTE_CMD(CMD_REPORT_MAX_BASELINE)
#define CYAPA_SMBUS_MIN_BASELINE SMBUS_BYTE_CMD(CMD_REPORT_MIN_BASELINE)
/* for group registers read/write command */
#define REG_GROUP_DATA 0
#define REG_GROUP_CMD 2
#define REG_GROUP_QUERY 3
#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3))
#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA)
#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD)
#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY)
/* for register block read/write command */
#define CMD_BL_STATUS 0
#define CMD_BL_HEAD 1
#define CMD_BL_CMD 2
#define CMD_BL_DATA 3
#define CMD_BL_ALL 4