/* * Cypress APA trackpad with I2C interface * * Author: Dudley Du <dudl@cypress.com> * * Copyright (C) 2014-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.
*/
/* The offset only valid for retrieve PWC and panel scan commands */ #define GEN5_RESP_DATA_STRUCTURE_OFFSET 10 #define GEN5_PWC_DATA_ELEMENT_SIZE_MASK 0x07
struct cyapa_pip_touch_record { /* * Bit 7 - 3: reserved * Bit 2 - 0: touch type; * 0 : standard finger; * 1 : proximity (Start supported in Gen5 TP). * 2 : finger hover (defined, but not used yet.) * 3 - 15 : reserved.
*/
u8 touch_type;
/* * Bit 7: indicates touch liftoff status. * 0 : touch is currently on the panel. * 1 : touch record indicates a liftoff. * Bit 6 - 5: indicates an event associated with this touch instance * 0 : no event * 1 : touchdown * 2 : significant displacement (> active distance) * 3 : liftoff (record reports last known coordinates) * Bit 4 - 0: An arbitrary ID tag associated with a finger * to allow tracking a touch as it moves around the panel.
*/
u8 touch_tip_event_id;
/* Bit 7 - 0 of X-axis coordinate of the touch in pixel. */
u8 x_lo;
/* Bit 15 - 8 of X-axis coordinate of the touch in pixel. */
u8 x_hi;
/* Bit 7 - 0 of Y-axis coordinate of the touch in pixel. */
u8 y_lo;
/* Bit 15 - 8 of Y-axis coordinate of the touch in pixel. */
u8 y_hi;
/* * The meaning of this value is different when touch_type is different. * For standard finger type: * Touch intensity in counts, pressure value. * For proximity type (Start supported in Gen5 TP): * The distance, in surface units, between the contact and * the surface.
**/
u8 z;
/* * The length of the major axis of the ellipse of contact between * the finger and the panel (ABS_MT_TOUCH_MAJOR).
*/
u8 major_axis_len;
/* * The length of the minor axis of the ellipse of contact between * the finger and the panel (ABS_MT_TOUCH_MINOR).
*/
u8 minor_axis_len;
/* * The length of the major axis of the approaching tool. * (ABS_MT_WIDTH_MAJOR)
*/
u8 major_tool_len;
/* * The length of the minor axis of the approaching tool. * (ABS_MT_WIDTH_MINOR)
*/
u8 minor_tool_len;
/* * The angle between the panel vertical axis and * the major axis of the contact ellipse. This value is an 8-bit * signed integer. The range is -127 to +127 (corresponding to * -90 degree and +90 degree respectively). * The positive direction is clockwise from the vertical axis. * If the ellipse of contact degenerates into a circle, * orientation is reported as 0.
*/
u8 orientation;
} __packed;
struct cyapa_tsg_bin_image_data_record {
u8 flash_array_id;
__be16 row_number; /* The number of bytes of flash data contained in this record. */
__be16 record_len; /* The flash program data. */
u8 record_data[CYAPA_TSG_FW_ROW_SIZE];
} __packed;
struct pip_bl_packet_start {
u8 sop; /* Start of packet, must be 01h */
u8 cmd_code;
__le16 data_length; /* Size of data parameter start from data[0] */
} __packed;
struct pip_bl_packet_end {
__le16 crc;
u8 eop; /* End of packet, must be 17h */
} __packed;
struct pip_bl_cmd_head {
__le16 addr; /* Output report register address, must be 0004h */ /* Size of packet not including output report register address */
__le16 length;
u8 report_id; /* Bootloader output report id, must be 40h */
u8 rsvd; /* Reserved, must be 0 */ struct pip_bl_packet_start packet_start;
u8 data[]; /* Command data variable based on commands */
} __packed;
struct tsg_bl_metadata_row_params {
__le16 size;
__le16 maximum_size;
__le32 app_start;
__le16 app_len;
__le16 app_crc;
__le32 app_entry;
__le32 upgrade_start;
__le16 upgrade_len;
__le16 entry_row_crc;
u8 padding[36]; /* Padding data must be 0 */
__le16 metadata_crc; /* CRC starts at offset of 60 */
} __packed;
/* Bootload program and verify row command data structure */ struct tsg_bl_flash_row_head {
u8 flash_array_id;
__le16 flash_row_id;
u8 flash_data[];
} __packed;
struct pip_app_cmd_head {
__le16 addr; /* Output report register address, must be 0004h */ /* Size of packet not including output report register address */
__le16 length;
u8 report_id; /* Application output report id, must be 2Fh */
u8 rsvd; /* Reserved, must be 0 */ /* * Bit 7: reserved, must be 0. * Bit 6-0: command code.
*/
u8 cmd_code;
u8 parameter_data[]; /* Parameter data variable based on cmd_code */
} __packed;
/* Indicates the pip->pm_stage is not valid. */
mutex_lock(&pip->pm_stage_lock);
pip->pm_stage = CYAPA_PM_DEACTIVE;
mutex_unlock(&pip->pm_stage_lock);
}
/* * This function is aimed to dump all not read data in Gen5 trackpad * before send any command, otherwise, the interrupt line will be blocked.
*/ int cyapa_empty_pip_output_data(struct cyapa *cyapa,
u8 *buf, int *len, cb_sort func)
{ struct input_dev *input = cyapa->input; struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; enum cyapa_pm_stage pm_stage = cyapa_get_pip_pm_state(cyapa); int length; int report_count; int empty_count; int buf_len; int error;
report_count = 8; /* max 7 pending data before command response data */
empty_count = 0; do { /* * Depending on testing in cyapa driver, there are max 5 "02 00" * packets between two valid buffered data report in firmware. * So in order to dump all buffered data out and * make interrupt line release for reassert again, * we must set the empty_count check value bigger than 5 to * make it work. Otherwise, in some situation, * the interrupt line may unable to reactive again, * which will cause trackpad device unable to * report data any more. * for example, it may happen in EFT and ESD testing.
*/ if (empty_count > 5) return 0;
staticbool cyapa_sort_pip_application_launch_data(struct cyapa *cyapa,
u8 *buf, int len)
{ if (buf == NULL || len < PIP_RESP_LENGTH_SIZE) returnfalse;
/* * After reset or power on, trackpad device always sets to 0x00 0x00 * to indicate a reset or power on event.
*/ if (buf[0] == 0 && buf[1] == 0) returntrue;
returnfalse;
}
staticbool cyapa_sort_gen5_hid_descriptor_data(struct cyapa *cyapa,
u8 *buf, int len)
{ int resp_len; int max_output_len;
/* Check hid descriptor. */ if (len != PIP_HID_DESCRIPTOR_SIZE) returnfalse;
staticint gen5_idle_state_parse(struct cyapa *cyapa)
{
u8 resp_data[PIP_HID_DESCRIPTOR_SIZE]; int max_output_len; int length;
u8 cmd[2]; int ret; int error;
/* * Dump all buffered data firstly for the situation * when the trackpad is just power on the cyapa go here.
*/
cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
memset(resp_data, 0, sizeof(resp_data));
ret = cyapa_i2c_pip_read(cyapa, resp_data, 3); if (ret != 3) return ret < 0 ? ret : -EIO;
length = get_unaligned_le16(&resp_data[PIP_RESP_LENGTH_OFFSET]); if (length == PIP_RESP_LENGTH_SIZE) { /* Normal state of Gen5 with no data to response */
cyapa->gen = CYAPA_GEN5;
staticint gen5_cmd_resp_header_parse(struct cyapa *cyapa, u8 *reg_data)
{ struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; int length; int ret;
/* * Must read report data through out, * otherwise Gen5 trackpad cannot response next command * or report any touch or button data.
*/
length = get_unaligned_le16(®_data[PIP_RESP_LENGTH_OFFSET]);
ret = cyapa_i2c_pip_read(cyapa, pip->empty_buf, length); if (ret != length) return ret < 0 ? ret : -EIO;
if (cyapa->gen == CYAPA_GEN5) { /* * Must read the content (e.g.: report description and so on) * from trackpad device throughout. Otherwise, * Gen5 trackpad cannot response to next command or * report any touch or button data later.
*/
cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
/* APP_INTEGRITY row is always the last row block */
data = image_records[records_num - 1].record_data;
memcpy(cmd_data->metadata_raw_parameter, data,
CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
/* Verify the firmware image not miss-used for Gen5 and Gen6. */ if (cyapa_pip_fw_head_check(cyapa,
(struct cyapa_tsg_bin_image_head *)fw->data)) {
dev_err(dev, "%s: firmware image not match TP device.\n",
__func__); return -EINVAL;
}
/* * The last flash row 0x01ff has been written through bl_initiate * command, so DO NOT write flash 0x01ff to trackpad device.
*/ for (i = 0; i < (flash_records_count - 1); i++) {
error = cyapa_pip_write_fw_block(cyapa, &image_records[i]); if (error) {
dev_err(dev, "%s: Gen5 FW update aborted: %d\n",
__func__, error); return error;
}
}
if (cyapa->state != CYAPA_STATE_GEN5_APP) return 0;
cyapa_set_pip_pm_state(cyapa, pm_stage);
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) if (cyapa_gen5_get_interval_time(cyapa,
GEN5_PARAMETER_LP_INTRVL_ID,
&cyapa->dev_sleep_time) != 0)
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. */ goto out;
}
}
if (power_mode == PWR_MODE_OFF) {
error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); if (error) {
dev_err(dev, "enter deep sleep fail: %d\n", error); goto out;
}
/* * 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_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); if (error) {
dev_err(dev, "deep sleep wake fail: %d\n", error); goto out;
}
}
if (power_mode == PWR_MODE_FULL_ACTIVE) {
error = cyapa_gen5_change_power_state(cyapa,
GEN5_POWER_STATE_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);
} elseif (power_mode == PWR_MODE_BTN_ONLY) {
error = cyapa_gen5_change_power_state(cyapa,
GEN5_POWER_STATE_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 { /* * Continue to change power mode even failed to set * interval time, it won't affect the power mode change. * except the sleep interval time is not correct.
*/ if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) ||
sleep_time != PIP_DEV_GET_SLEEP_TIME(cyapa)) if (cyapa_gen5_set_interval_time(cyapa,
GEN5_PARAMETER_LP_INTRVL_ID,
sleep_time) == 0)
PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
if (sleep_time <= GEN5_POWER_READY_MAX_INTRVL_TIME)
power_state = GEN5_POWER_STATE_READY; else
power_state = GEN5_POWER_STATE_IDLE;
error = cyapa_gen5_change_power_state(cyapa, power_state); if (error) {
dev_err(dev, "set power state to 0x%02x failed: %d\n",
power_state, error); goto out;
}
/* * Disable pip report for a little time, firmware will * re-enable it automatically. It's used to fix the issue * that trackpad unable to report signal to wake system up * in the special situation that system is in suspending, and * at the same time, user touch trackpad to wake system up. * This function can avoid the data to be buffered when system * is suspending which may cause interrupt line unable to be * asserted again.
*/ if (pm_stage == CYAPA_PM_SUSPEND)
cyapa_gen5_disable_pip_report(cyapa);
switch (data_size) { case 1:
value = buf[0]; break; case 2: if (big_endian)
value = get_unaligned_be16(buf); else
value = get_unaligned_le16(buf); break; case 4: if (big_endian)
value = get_unaligned_be32(buf); else
value = get_unaligned_le32(buf); break; default: /* Should not happen, just as default case here. */
value = 0; break;
}
if (!unsigned_type)
value = twos_complement_to_s32(value, data_size * 8);
/* * Read all the global mutual or self idac data or mutual or self local PWC * data based on the @idac_data_type. * If the input value of @data_size is 0, then means read global mutual or * self idac data. For read global mutual idac data, @idac_max, @idac_min and * @idac_ave are in order used to return the max value of global mutual idac * data, the min value of global mutual idac and the average value of the * global mutual idac data. For read global self idac data, @idac_max is used * to return the global self cap idac data in Rx direction, @idac_min is used * to return the global self cap idac data in Tx direction. @idac_ave is not * used. * If the input value of @data_size is not 0, than means read the mutual or * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to * return the max, min and average value of the mutual or self local PWC data. * Note, in order to read mutual local PWC data, must read invoke this function * to read the mutual global idac data firstly to set the correct Rx number * value, otherwise, the read mutual idac and PWC data may not correct.
*/ staticint cyapa_gen5_read_idac_data(struct cyapa *cyapa,
u8 cmd_code, u8 idac_data_type, int *data_size, int *idac_max, int *idac_min, int *idac_ave)
{ struct pip_app_cmd_head *cmd_head;
u8 cmd[12];
u8 resp_data[256]; int resp_len; int read_len; int value;
u16 offset; int read_elements; bool read_global_idac; int sum, count, max_element_cnt; int tmp_max, tmp_min, tmp_ave, tmp_sum, tmp_count; int electrodes_rx, electrodes_tx; int i; int error;
/* Read mutual global idac or local mutual/self PWC data. */
offset += read_len; for (i = 10; i < (read_len + GEN5_RESP_DATA_STRUCTURE_OFFSET);
i += *data_size) {
value = cyapa_parse_structure_data(resp_data[9],
&resp_data[i], *data_size);
*idac_min = min(value, *idac_min);
*idac_max = max(value, *idac_max);
if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
tmp_count < cyapa->aligned_electrodes_rx &&
read_global_idac) { /* * The value gap between global and local mutual * idac data must bigger than 50%. * Normally, global value bigger than 50, * local values less than 10.
*/ if (!tmp_ave || value > tmp_ave / 2) {
tmp_min = min(value, tmp_min);
tmp_max = max(value, tmp_max);
tmp_sum += value;
tmp_count++;
tmp_ave = tmp_sum / tmp_count;
}
}
sum += value;
count++;
if (count >= max_element_cnt) goto out;
}
} while (true);
out:
*idac_ave = count ? (sum / count) : 0;
if (read_global_idac &&
idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) { if (tmp_count == 0) return 0;
staticint cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa, int *gidac_mutual_max, int *gidac_mutual_min, int *gidac_mutual_ave, int *lidac_mutual_max, int *lidac_mutual_min, int *lidac_mutual_ave)
{ int data_size; int error;
staticint cyapa_gen5_read_self_idac_data(struct cyapa *cyapa, int *gidac_self_rx, int *gidac_self_tx, int *lidac_self_max, int *lidac_self_min, int *lidac_self_ave)
{ int data_size; int error;
staticint cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa,
u8 cmd_code, u8 raw_data_type, int raw_data_max_num, int *raw_data_max, int *raw_data_min, int *raw_data_ave,
u8 *buffer)
{ struct pip_app_cmd_head *app_cmd_head; struct gen5_retrieve_panel_scan_data *panel_sacn_data;
u8 cmd[12];
u8 resp_data[256]; /* Max bytes can transfer one time. */ int resp_len; int read_elements; int read_len;
u16 offset;
s32 value; int sum, count; int data_size;
s32 *intp; int i; int error;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.