summaryrefslogtreecommitdiff
path: root/tcm
diff options
context:
space:
mode:
authordavidycchen <davidycchen@google.com>2021-08-17 14:00:30 +0800
committerdavidycchen <davidycchen@google.com>2021-09-08 15:12:37 +0800
commitb709f4d97bb33d74ff5a14f8ff8c617b5f605c67 (patch)
tree1a431604d32160798b1b9791e81e9d9e172d13dc /tcm
parentdcf50a13512c25d57d413b1ce99f1770450e3d5f (diff)
downloadsynaptics_touch-b709f4d97bb33d74ff5a14f8ff8c617b5f605c67.tar.gz
synaptics: add initial driver
Initial driver provided by Synaptics. Bug: 198228556 Signed-off-by: davidycchen <davidycchen@google.com> Change-Id: I471eda97a2b2e06a4dd9328e69c71d5f8e8fdc93
Diffstat (limited to 'tcm')
-rw-r--r--tcm/synaptics_touchcom_core_dev.h1096
-rw-r--r--tcm/synaptics_touchcom_core_v1.c1078
-rw-r--r--tcm/synaptics_touchcom_core_v2.c1445
-rw-r--r--tcm/synaptics_touchcom_func_base.c1678
-rw-r--r--tcm/synaptics_touchcom_func_base.h418
-rw-r--r--tcm/synaptics_touchcom_func_base_flash.h569
-rw-r--r--tcm/synaptics_touchcom_func_reflash.c2074
-rw-r--r--tcm/synaptics_touchcom_func_reflash.h145
-rw-r--r--tcm/synaptics_touchcom_func_romboot.c1586
-rw-r--r--tcm/synaptics_touchcom_func_romboot.h155
-rw-r--r--tcm/synaptics_touchcom_func_touch.c877
-rw-r--r--tcm/synaptics_touchcom_func_touch.h235
12 files changed, 11356 insertions, 0 deletions
diff --git a/tcm/synaptics_touchcom_core_dev.h b/tcm/synaptics_touchcom_core_dev.h
new file mode 100644
index 0000000..9ac2ee8
--- /dev/null
+++ b/tcm/synaptics_touchcom_core_dev.h
@@ -0,0 +1,1096 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file: synaptics_touchcom_core_dev.h
+ *
+ * This file is the topmost header file for Synaptics TouchComm device, also
+ * defines the TouchComm device context structure which will be passed to
+ * all other functions that expect a device handle.
+ */
+
+#ifndef _SYNAPTICS_TOUCHCOM_CORE_DEV_H_
+#define _SYNAPTICS_TOUCHCOM_CORE_DEV_H_
+
+
+#include "syna_tcm2_platform.h"
+
+
+#define SYNA_TCM_CORE_LIB_VERSION 0x0111
+
+
+/**
+ * @section: Parameters pre-defined
+ *
+ * @brief: MAX_NUM_OBJECTS
+ * Maximum number of objects being detected
+ *
+ * @brief: MAX_SIZE_GESTURE_DATA
+ * Maximum size of gesture data
+ *
+ * @brief: MAX_SIZE_CONFIG_ID
+ * Maximum size of customer configuration ID
+ */
+#define MAX_NUM_OBJECTS (10)
+
+#define MAX_SIZE_GESTURE_DATA (8)
+
+#define MAX_SIZE_CONFIG_ID (16)
+
+/**
+ * @section: Command-handling relevant definitions
+ *
+ * @brief: MESSAGE_HEADER_SIZE
+ * The size of message header
+ *
+ * @brief: CMD_RESPONSE_TIMEOUT_MS
+ * Time frame for a command execution
+ *
+ * @brief: CMD_RESPONSE_POLLING_DELAY_MS
+ * Generic time frame to check the response in polling
+ *
+ * @brief: RD_RETRY_US
+ * For retry reading, delay time range in microsecond
+ *
+ * @brief: WR_DELAY_US
+ * For continued writes, delay time range in microsecond
+ *
+ * @brief: TAT_DELAY_US
+ * For bus turn-around, delay time range in microsecond
+ *
+ * @brief: FW_MODE_SWITCH_DELAY_MS
+ * The default time for fw mode switching
+ *
+ * @brief: RESET_DELAY_MS
+ * The default time after reset in case it's not set properly
+ *
+ * @brief: FORCE_ATTN_DRIVEN
+ * Special flag to read in resp packet in ISR function
+ */
+#define MESSAGE_HEADER_SIZE (4)
+
+#define CMD_RESPONSE_TIMEOUT_MS (3000)
+
+#define CMD_RESPONSE_POLLING_DELAY_MS (10)
+
+#define RD_RETRY_US_MIN (5000)
+#define RD_RETRY_US_MAX (10000)
+
+#define WR_DELAY_US_MIN (500)
+#define WR_DELAY_US_MAX (1000)
+
+#define TAT_DELAY_US_MIN (100)
+#define TAT_DELAY_US_MAX (200)
+
+#define FW_MODE_SWITCH_DELAY_MS (200)
+
+#define RESET_DELAY_MS (200)
+
+#define DEFAULT_FLASH_ERASE_DELAY (~0)
+#define DEFAULT_FLASH_WRITE_DELAY (~0)
+#define DEFAULT_FLASH_READ_DELAY (~0)
+
+#define RESP_IN_ATTN (0)
+#define RESP_IN_POLLING (CMD_RESPONSE_POLLING_DELAY_MS)
+
+/**
+ * @section: Macro to show string in log
+ */
+#define STR(x) #x
+
+/**
+ * @section: Helpers to check the device mode
+ */
+#define IS_APP_FW_MODE(mode) \
+ (mode == MODE_APPLICATION_FIRMWARE)
+
+#define IS_NOT_APP_FW_MODE(mode) \
+ (!IS_APP_FW_MODE(mode))
+
+#define IS_BOOTLOADER_MODE(mode) \
+ ((mode == MODE_BOOTLOADER) || \
+ (mode == MODE_TDDI_BOOTLOADER) || \
+ (mode == MODE_TDDI_HDL_BOOTLOADER) || \
+ (mode == MODE_MULTICHIP_TDDI_BOOTLOADER))
+
+#define IS_ROM_BOOTLOADER_MODE(mode) \
+ (mode == MODE_ROMBOOTLOADER)
+
+
+/**
+ * @section: Types for lower-level bus being used
+ */
+enum bus_connection {
+ BUS_TYPE_NONE,
+ BUS_TYPE_I2C,
+ BUS_TYPE_SPI,
+ BUS_TYPE_I3C,
+};
+
+/**
+ * @section: TouchComm Firmware Modes
+ *
+ * The current mode running is defined in Identify Info Packet.
+ */
+enum tcm_firmware_mode {
+ MODE_UNKNOWN = 0x00,
+ MODE_APPLICATION_FIRMWARE = 0x01,
+ MODE_HOSTDOWNLOAD_FIRMWARE = 0x02,
+ MODE_ROMBOOTLOADER = 0x04,
+ MODE_BOOTLOADER = 0x0b,
+ MODE_TDDI_BOOTLOADER = 0x0c,
+ MODE_TDDI_HDL_BOOTLOADER = 0x0d,
+ MODE_PRODUCTIONTEST_FIRMWARE = 0x0e,
+ MODE_MULTICHIP_TDDI_BOOTLOADER = 0xab,
+};
+
+/**
+ * @section: Status of Application Firmware
+ *
+ * The current status is defined in Application Info Packet.
+ */
+enum tcm_app_status {
+ APP_STATUS_OK = 0x00,
+ APP_STATUS_BOOTING = 0x01,
+ APP_STATUS_UPDATING = 0x02,
+ APP_STATUS_BAD_APP_CONFIG = 0xff,
+};
+
+/**
+ * @section: Field IDs in Dynamic Configuration
+ *
+ * The codes specify the generic dynamic configuration options.
+ */
+enum dynamic_tcm_config_id {
+ DC_UNKNOWN = 0x00,
+ DC_DISABLE_DOZE = 0x01,
+ DC_DISABLE_NOISE_MITIGATION = 0x02,
+ DC_DISABLE_FREQUENCY_SHIFT = 0x03,
+ DC_REQUEST_FREQUENCY_INDEX = 0x04,
+ DC_DISABLE_HSYNC = 0x05,
+ DC_REZERO_ON_EXIT_DEEP_SLEEP = 0x06,
+ DC_ENABLE_CHARGER_CONNECTED = 0x07,
+ DC_DISABLE_BASELINE_RELAXATION = 0x08,
+ DC_ENABLE_WAKEUP_GESTURE_MODE = 0x09,
+ DC_REQUEST_TESTING_FINGERS = 0x0a,
+ DC_ENABLE_GRIP_SUPPRESSION = 0x0b,
+ DC_ENABLE_THICK_GLOVE = 0x0c,
+ DC_ENABLE_GLOVE = 0x0d,
+ DC_ENABLE_FACE_DETECTION = 0x0e,
+ DC_INHIBIT_ACTIVE_GESTURE = 0x0f,
+ DC_DISABLE_PROXIMITY = 0x10,
+};
+
+/**
+ * @section: TouchComm Commands
+ *
+ * List the generic commands supported in TouchComm command-response protocol.
+ */
+enum tcm_command {
+ CMD_NONE = 0x00,
+ CMD_CONTINUE_WRITE = 0x01,
+ CMD_IDENTIFY = 0x02,
+ CMD_RESET = 0x04,
+ CMD_ENABLE_REPORT = 0x05,
+ CMD_DISABLE_REPORT = 0x06,
+ CMD_TCM2_ACK = 0x07,
+ CMD_TCM2_RETRY = 0x08,
+ CMD_TCM2_SET_MAX_READ_LENGTH = 0x09,
+ CMD_TCM2_GET_REPORT = 0x0a,
+ CMD_GET_BOOT_INFO = 0x10,
+ CMD_ERASE_FLASH = 0x11,
+ CMD_WRITE_FLASH = 0x12,
+ CMD_READ_FLASH = 0x13,
+ CMD_RUN_APPLICATION_FIRMWARE = 0x14,
+ CMD_SPI_MASTER_WRITE_THEN_READ = 0x15,
+ CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16,
+ CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f,
+ CMD_GET_APPLICATION_INFO = 0x20,
+ CMD_GET_STATIC_CONFIG = 0x21,
+ CMD_SET_STATIC_CONFIG = 0x22,
+ CMD_GET_DYNAMIC_CONFIG = 0x23,
+ CMD_SET_DYNAMIC_CONFIG = 0x24,
+ CMD_GET_TOUCH_REPORT_CONFIG = 0x25,
+ CMD_SET_TOUCH_REPORT_CONFIG = 0x26,
+ CMD_REZERO = 0x27,
+ CMD_COMMIT_CONFIG = 0x28,
+ CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29,
+ CMD_PRODUCTION_TEST = 0x2a,
+ CMD_SET_CONFIG_ID = 0x2b,
+ CMD_ENTER_DEEP_SLEEP = 0x2c,
+ CMD_EXIT_DEEP_SLEEP = 0x2d,
+ CMD_GET_TOUCH_INFO = 0x2e,
+ CMD_GET_DATA_LOCATION = 0x2f,
+ CMD_DOWNLOAD_CONFIG = 0x30,
+ CMD_ENTER_PRODUCTION_TEST_MODE = 0x31,
+ CMD_GET_FEATURES = 0x32,
+ CMD_GET_ROMBOOT_INFO = 0x40,
+ CMD_WRITE_PROGRAM_RAM = 0x41,
+ CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE = 0x42,
+ CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED = 0x43,
+ CMD_ENTER_IO_BRIDGE_MODE = 0x44,
+ CMD_ROMBOOT_DOWNLOAD = 0x45,
+};
+
+/**
+ * @section: TouchComm Status Codes
+ *
+ * Define the following status codes for all command responses.
+ * 0x00: (v1) no commands are pending and no reports are available.
+ * 0x01: (v1 & v2) the previous command succeeded.
+ * 0x03: (v1 & v2) the payload continues a previous response.
+ * 0x04: (v2) command was written, but no reports were available.
+ * 0x07: (v2) the previous write was successfully received.
+ * 0x08: (v2) the previous write was corrupt. The host should resend.
+ * 0x09: (v2) the previous command failed.
+ * 0x0c: (v1 & v2) write was larger than the device's receive buffer.
+ * 0x0d: (v1 & v2) a command was sent before the previous command completed.
+ * 0x0e: (v1 & v2) the requested command is not implemented.
+ * 0x0f: (v1 & v2) generic communication error, probably incorrect payload.
+ *
+ * 0xfe: self-defined status for a corrupted packet.
+ * 0xff: self-defined status for an invalid data.
+ */
+enum tcm_status_code {
+ STATUS_IDLE = 0x00,
+ STATUS_OK = 0x01,
+ STATUS_CONTINUED_READ = 0x03,
+ STATUS_NO_REPORT_AVAILABLE = 0x04,
+ STATUS_ACK = 0x07,
+ STATUS_RETRY_REQUESTED = 0x08,
+ STATUS_CMD_FAILED = 0x09,
+ STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c,
+ STATUS_PREVIOUS_COMMAND_PENDING = 0x0d,
+ STATUS_NOT_IMPLEMENTED = 0x0e,
+ STATUS_ERROR = 0x0f,
+ STATUS_PACKET_CORRUPTED = 0xfe,
+ STATUS_INVALID = 0xff,
+};
+
+/**
+ * @section: TouchComm Report Codes
+ *
+ * Define the following report codes generated by TouchComm firmware.
+ * 0x10: Identify Info Packet
+ * 0x11: Touch Report
+ * 0x12: Delta Cap. Image
+ * 0x13: Raw Cap. Image
+ */
+enum tcm_report_type {
+ REPORT_IDENTIFY = 0x10,
+ REPORT_TOUCH = 0x11,
+ REPORT_DELTA = 0x12,
+ REPORT_RAW = 0x13,
+};
+
+/**
+ * @section: States in Command Processing
+ *
+ * List the states in command processing.
+ */
+enum tcm_command_status {
+ CMD_STATE_IDLE = 0,
+ CMD_STATE_BUSY = 1,
+ CMD_STATE_ERROR = -1,
+};
+
+/**
+ * @section: Production Test Items
+ *
+ * List the generic production test items
+ */
+enum tcm_test_code {
+ TEST_NOT_IMPLEMENTED = 0x00,
+
+ TEST_PID01_TRX_TRX_SHORTS = 0x01,
+ TEST_PID02_TRX_SENSOR_OPENS = 0x02,
+ TEST_PID03_TRX_GROUND_SHORTS = 0x03,
+ TEST_PID05_FULL_RAW_CAP = 0x05,
+ TEST_PID06_EE_SHORT = 0x06,
+ TEST_PID07_DYNAMIC_RANGE = 0x07,
+ TEST_PID08_HIGH_RESISTANCE = 0x08,
+ TEST_PID10_DELTA_NOISE = 0x0a,
+ TEST_PID11_OPEN_DETECTION = 0x0b,
+ TEST_PID12 = 0x0c,
+ TEST_PID13 = 0x0d,
+ TEST_PID14_DOZE_DYNAMIC_RANGE = 0x0e,
+ TEST_PID15_DOZE_NOISE = 0x0f,
+ TEST_PID16_SENSOR_SPEED = 0x10,
+ TEST_PID17_ADC_RANGE = 0x11,
+ TEST_PID18_HYBRID_ABS_RAW = 0x12,
+ TEST_PID29_HYBRID_ABS_NOISE = 0x1D,
+
+ TEST_PID_MAX,
+};
+
+
+/**
+ * @section: Internal Buffer Structure
+ *
+ * This structure is taken as the internal common buffer.
+ */
+struct tcm_buffer {
+ unsigned char *buf;
+ unsigned int buf_size;
+ unsigned int data_length;
+ syna_pal_mutex_t buf_mutex;
+ unsigned char ref_cnt;
+};
+
+/**
+ * @section: TouchComm Identify Info Packet
+ * Ver.1: size is 24 (0x18) bytes
+ * Ver.2: size is extended to 32 (0x20) bytes
+ *
+ * The identify packet provides the basic TouchComm information and indicate
+ * that the device is ready to receive commands.
+ *
+ * The report is received whenever the device initially powers up, resets,
+ * or switches fw between bootloader and application modes.
+ */
+struct tcm_identification_info {
+ unsigned char version;
+ unsigned char mode;
+ unsigned char part_number[16];
+ unsigned char build_id[4];
+ unsigned char max_write_size[2];
+ /* extension in ver.2 */
+ unsigned char max_read_size[2];
+ unsigned char reserved[6];
+};
+
+
+/**
+ * @section: TouchComm Application Information Packet
+ *
+ * The application info packet provides the information about the application
+ * firmware as well as the touch controller.
+ */
+struct tcm_application_info {
+ unsigned char version[2];
+ unsigned char status[2];
+ unsigned char static_config_size[2];
+ unsigned char dynamic_config_size[2];
+ unsigned char app_config_start_write_block[2];
+ unsigned char app_config_size[2];
+ unsigned char max_touch_report_config_size[2];
+ unsigned char max_touch_report_payload_size[2];
+ unsigned char customer_config_id[MAX_SIZE_CONFIG_ID];
+ unsigned char max_x[2];
+ unsigned char max_y[2];
+ unsigned char max_objects[2];
+ unsigned char num_of_buttons[2];
+ unsigned char num_of_image_rows[2];
+ unsigned char num_of_image_cols[2];
+ unsigned char has_hybrid_data[2];
+ unsigned char num_of_force_elecs[2];
+};
+
+/**
+ * @section: TouchComm boot information packet
+ *
+ * The boot info packet provides the information of TouchBoot.
+ */
+struct tcm_boot_info {
+ unsigned char version;
+ unsigned char status;
+ unsigned char asic_id[2];
+ unsigned char write_block_size_words;
+ unsigned char erase_page_size_words[2];
+ unsigned char max_write_payload_size[2];
+ unsigned char last_reset_reason;
+ unsigned char pc_at_time_of_last_reset[2];
+ unsigned char boot_config_start_block[2];
+ unsigned char boot_config_size_blocks[2];
+ /* extension in ver.2 */
+ unsigned char display_config_start_block[4];
+ unsigned char display_config_length_blocks[2];
+ unsigned char backup_display_config_start_block[4];
+ unsigned char backup_display_config_length_blocks[2];
+ unsigned char custom_otp_start_block[2];
+ unsigned char custom_otp_length_blocks[2];
+};
+
+/**
+ * @section: TouchComm ROMboot information packet
+ *
+ * The ROMboot info packet provides the information of ROM bootloader.
+ */
+struct tcm_romboot_info {
+ unsigned char version;
+ unsigned char status;
+ unsigned char asic_id[2];
+ unsigned char write_block_size_words;
+ unsigned char max_write_payload_size[2];
+ unsigned char last_reset_reason;
+ unsigned char pc_at_time_of_last_reset[2];
+};
+
+/**
+ * @section: TouchComm features description packet
+ *
+ * The features description packet tells which features are supported.
+ */
+struct tcm_features_info {
+ unsigned char byte[16];
+};
+
+/**
+ * @section: Data blob for touch data reported
+ *
+ * Once receiving a touch report generated by firmware, the touched data
+ * will be parsed and converted to touch_data_blob structure as a data blob.
+ *
+ * @subsection: tcm_touch_data_blob
+ * The touch_data_blob contains all sorts of touched data entities.
+ *
+ * @subsection tcm_objects_data_blob
+ * The objects_data_blob includes the data for each active objects.
+ *
+ * @subsection tcm_gesture_data_blob
+ * The gesture_data_blob contains the gesture data if detected.
+ */
+struct tcm_objects_data_blob {
+ unsigned char status;
+ unsigned int x_pos;
+ unsigned int y_pos;
+ unsigned int x_width;
+ unsigned int y_width;
+ unsigned int z;
+ unsigned int tx_pos;
+ unsigned int rx_pos;
+};
+struct tcm_gesture_data_blob {
+ union {
+ struct {
+ unsigned char tap_x[2];
+ unsigned char tap_y[2];
+ };
+ struct {
+ unsigned char swipe_x[2];
+ unsigned char swipe_y[2];
+ unsigned char swipe_direction[2];
+ };
+ unsigned char data[MAX_SIZE_GESTURE_DATA];
+ };
+};
+struct tcm_touch_data_blob {
+
+ /* for each active objects */
+ unsigned int num_of_active_objects;
+ struct tcm_objects_data_blob object_data[MAX_NUM_OBJECTS];
+
+ /* for gesture */
+ unsigned int gesture_id;
+ struct tcm_gesture_data_blob gesture_data;
+
+ /* various data */
+ unsigned int timestamp;
+ unsigned int buttons_state;
+ unsigned int frame_rate;
+ unsigned int power_im;
+ unsigned int cid_im;
+ unsigned int rail_im;
+ unsigned int cid_variance_im;
+ unsigned int nsm_frequency;
+ unsigned int nsm_state;
+ unsigned int num_of_cpu_cycles;
+ unsigned int fd_data;
+ unsigned int force_data;
+ unsigned int fingerprint_area_meet;
+ unsigned int sensing_mode;
+};
+
+/**
+ * @section: Callback function used for custom entity parsing in touch report
+ *
+ * Allow to implement the custom handling for"new" custom entity in touch report
+ *
+ * @param
+ * [ in] code: the code of current touch entity
+ * [ in] config: the report configuration stored
+ * [in/out] config_offset: offset of current position in report config,
+ * the updated position should be returned.
+ * [ in] report: touch report given
+ * [in/out] report_offset: offset of current position in touch report,
+ * the updated position should be returned.
+ * [ in] report_size: size of given touch report
+ * [ in] callback_data: pointer to caller data passed to callback function
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+typedef int (*tcm_touch_data_parse_callback_t) (const unsigned char code,
+ const unsigned char *config, unsigned int *config_offset,
+ const unsigned char *report, unsigned int *report_offset,
+ unsigned int report_size, void *callback_data);
+
+/**
+ * @section: Callback function used for custom gesture parsing in touch report
+ *
+ * Allow to implement the custom handling for gesture data.
+ *
+ * @param
+ * [ in] code: the code of current touch entity
+ * [ in] config: the report configuration stored
+ * [in/out] config_offset: offset of current position in report config,
+ * the updated position should be returned.
+ * [ in] report: touch report given
+ * [in/out] report_offset: offset of current position in touch report,
+ * the updated position should be returned.
+ * [ in] report_size: size of given touch report
+ * [ in] callback_data: pointer to caller data passed to callback function
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+typedef int (*tcm_gesture_parse_callback_t) (const unsigned char code,
+ const unsigned char *config, unsigned int *config_offset,
+ const unsigned char *report, unsigned int *report_offset,
+ unsigned int report_size, void *callback_data);
+
+/**
+ * @section: TouchComm Message Handling Wrapper
+ *
+ * The structure contains the essential buffers and parameters to implement
+ * the command-response protocol for both TouchCom ver.1 and TouchCom ver.2.
+ */
+struct tcm_message_data_blob {
+
+ /* parameters for command processing */
+ syna_pal_atomic_t command_status;
+ unsigned char command;
+ unsigned char status_report_code;
+ unsigned int payload_length;
+ unsigned char response_code;
+ unsigned char report_code;
+ unsigned char seq_toggle;
+ unsigned int default_resp_reading;
+
+ /* completion event command processing */
+ syna_pal_completion_t cmd_completion;
+
+ /* internal buffers
+ * in : buffer storing the data being read 'in'
+ * out : buffer storing the data being sent 'out'
+ * temp: 'temp' buffer used for continued read operation
+ */
+ struct tcm_buffer in;
+ struct tcm_buffer out;
+ struct tcm_buffer temp;
+
+ /* mutex for the protection of command processing */
+ syna_pal_mutex_t cmd_mutex;
+
+ /* mutex for the read/write protection */
+ syna_pal_mutex_t rw_mutex;
+
+};
+
+/**
+ * @section: TouchComm core device context structure
+ *
+ * The device context contains parameters and internal buffers, that will
+ * be passed to all other functions that expect a device handle.
+ *
+ * Calling syna_tcm_allocate_device() can allocate this structure, and
+ * syna_tcm_remove_device() releases the structure if no longer needed.
+ */
+struct tcm_dev {
+
+ /* basic device information */
+ unsigned char dev_mode;
+ unsigned int packrat_number;
+ unsigned int max_x;
+ unsigned int max_y;
+ unsigned int max_objects;
+ unsigned int rows;
+ unsigned int cols;
+ unsigned char config_id[MAX_SIZE_CONFIG_ID];
+
+ /* capability of read/write transferred
+ * being assigned through syna_hw_interface
+ */
+ unsigned int max_wr_size;
+ unsigned int max_rd_size;
+
+ /* hardware-specific data structure
+ * defined in syna_touchcom_platform.h
+ */
+ struct syna_hw_interface *hw_if;
+
+ /* TouchComm defined structures */
+ struct tcm_identification_info id_info;
+ struct tcm_application_info app_info;
+ struct tcm_boot_info boot_info;
+
+ /* internal buffers
+ * report: record the TouchComm report to caller
+ * resp : record the command response to caller
+ */
+ struct tcm_buffer report_buf;
+ struct tcm_buffer resp_buf;
+ struct tcm_buffer external_buf;
+
+ /* touch report configuration */
+ struct tcm_buffer touch_config;
+
+ /* TouchComm message handling wrapper */
+ struct tcm_message_data_blob msg_data;
+
+ /* indicate that fw update is on-going */
+ syna_pal_atomic_t firmware_flashing;
+
+ /* abstraction to read a TouchComm message from device.
+ * Function will be assigned by syna_tcm_detect_device().
+ *
+ * After read_message() returned, the retrieved data will be available
+ * and stored either in buffer.report or buffer.resp based on the
+ * code returned.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] status_report_code: status code or report code received
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+ int (*read_message)(struct tcm_dev *tcm_dev,
+ unsigned char *status_report_code);
+
+ /* abstraction to write a TouchComm message to device and retrieve the
+ * response to command.
+ * Function will be assigned by syna_tcm_detect_device().
+ *
+ * After calling write_message(), the response code is returned
+ * and the response data is stored in buffer.resp.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: TouchComm command to write
+ * [ in] payload: data payload, if any
+ * [ in] payload_length: length of data payload, if any
+ * [out] resp_code: response code returned
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the polling time;
+ * or, set '0' (RESP_IN_ATTN) for ATTN driven
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+ int (*write_message)(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_length, unsigned char *resp_code,
+ unsigned int delay_ms_resp);
+
+
+ /* callbacks
+ * custom_touch_data_parse_func: custom touch data entity parsing
+ * custom_gesture_parse_func : custom gesture data entity parsing
+ */
+ tcm_touch_data_parse_callback_t custom_touch_data_parse_func;
+ void *cbdata_touch_data_parse;
+ tcm_gesture_parse_callback_t custom_gesture_parse_func;
+ void *cbdata_gesture_parse;
+
+};
+/* end of structure syna_tcm_dev */
+
+
+/**
+ * @section: Protocol detection
+ *
+ * @brief: syna_tcm_v1_detect
+ * Check whether TouchComm ver.1 firmware is running
+ *
+ * @brief: syna_tcm_v2_detect
+ * Check whether TouchComm ver.2 firmware is running
+ */
+
+/* syna_tcm_v1_detect()
+ *
+ * Check whether TouchComm ver.1 firmware is running.
+ * Function is implemented in synaptics_tcm2_core_v1.c.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: raw 4-byte data
+ * [ in] size: length of input data in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_v1_detect(struct tcm_dev *tcm_dev, unsigned char *data,
+ unsigned int data_len);
+
+/* syna_tcm_v2_detect()
+ *
+ * Check whether TouchComm ver.2 firmware is running.
+ * Function is implemented in synaptics_tcm2_core_v2.c.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: raw 4-byte data
+ * [ in] size: length of input data in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_v2_detect(struct tcm_dev *tcm_dev, unsigned char *data,
+ unsigned int data_len);
+
+/**
+ * @section: Buffers Management helpers
+ *
+ * @brief: syna_tcm_buf_alloc
+ * Allocate the requested memory space for the buffer structure
+ *
+ * @brief: syna_tcm_buf_realloc
+ * Extend the requested memory space for the buffer structure
+ *
+ * @brief: syna_tcm_buf_init
+ * Initialize the buffer structure
+ *
+ * @brief: syna_tcm_buf_lock
+ * Protect the access of current buffer structure
+ *
+ * @brief: syna_tcm_buf_unlock
+ * Open the access of current buffer structure
+ *
+ * @brief: syna_tcm_buf_release
+ * Release the buffer structure
+ */
+
+/**
+ * syna_tcm_buf_alloc()
+ *
+ * Allocate the requested memory space for the given buffer only if
+ * the existed buffer is not enough for the requirement.
+ *
+ * @param
+ * [ in] pbuf: pointer to an internal buffer
+ * [ in] size: required size to be allocated
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static inline int syna_tcm_buf_alloc(struct tcm_buffer *pbuf,
+ unsigned int size)
+{
+ if (!pbuf) {
+ LOGE("Invalid buffer structure\n");
+ return -1;
+ }
+
+ if (size > pbuf->buf_size) {
+ if (pbuf->buf)
+ syna_pal_mem_free((void *)pbuf->buf);
+
+ pbuf->buf = syna_pal_mem_alloc(size, sizeof(unsigned char));
+ if (!(pbuf->buf)) {
+ LOGE("Fail to allocate memory (size = %d)\n",
+ (int)(size*sizeof(unsigned char)));
+ pbuf->buf_size = 0;
+ pbuf->data_length = 0;
+ return -1;
+ }
+ pbuf->buf_size = size;
+ }
+
+ syna_pal_mem_set(pbuf->buf, 0x00, pbuf->buf_size);
+ pbuf->data_length = 0;
+
+ return 0;
+}
+
+/**
+ * syna_tcm_buf_realloc()
+ *
+ * Extend the requested memory space for the given buffer only if
+ * the existed buffer is not enough for the requirement.
+ * Then, move the content to the new memory space.
+ *
+ * @param
+ * [ in] pbuf: pointer to an internal buffer
+ * [ in] size: required size to be extended
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static inline int syna_tcm_buf_realloc(struct tcm_buffer *pbuf,
+ unsigned int size)
+{
+ int retval;
+ unsigned char *temp_src;
+ unsigned int temp_size = 0;
+
+ if (!pbuf) {
+ LOGE("Invalid buffer structure\n");
+ return -1;
+ }
+
+ if (size > pbuf->buf_size) {
+ temp_src = pbuf->buf;
+ temp_size = pbuf->buf_size;
+
+ pbuf->buf = syna_pal_mem_alloc(size, sizeof(unsigned char));
+ if (!(pbuf->buf)) {
+ LOGE("Fail to allocate memory (size = %d)\n",
+ (int)(size * sizeof(unsigned char)));
+ syna_pal_mem_free((void *)temp_src);
+ pbuf->buf_size = 0;
+ return -1;
+ }
+
+ retval = syna_pal_mem_cpy(pbuf->buf,
+ size,
+ temp_src,
+ temp_size,
+ temp_size);
+ if (retval < 0) {
+ LOGE("Fail to copy data\n");
+ syna_pal_mem_free((void *)temp_src);
+ syna_pal_mem_free((void *)pbuf->buf);
+ pbuf->buf_size = 0;
+ return retval;
+ }
+
+ syna_pal_mem_free((void *)temp_src);
+ pbuf->buf_size = size;
+ }
+
+ return 0;
+}
+/**
+ * syna_tcm_buf_init()
+ *
+ * Initialize the buffer structure.
+ *
+ * @param
+ * [ in] pbuf: pointer to an internal buffer
+ *
+ * @return
+ * none
+ */
+static inline void syna_tcm_buf_init(struct tcm_buffer *pbuf)
+{
+ pbuf->buf_size = 0;
+ pbuf->data_length = 0;
+ pbuf->ref_cnt = 0;
+ pbuf->buf = NULL;
+ syna_pal_mutex_alloc(&pbuf->buf_mutex);
+}
+/**
+ * syna_tcm_buf_lock()
+ *
+ * Protect the access of current buffer structure.
+ *
+ * @param
+ * [ in] pbuf: pointer to an internal buffer
+ *
+ * @return
+ * none
+ */
+static inline void syna_tcm_buf_lock(struct tcm_buffer *pbuf)
+{
+ if (pbuf->ref_cnt != 0) {
+ LOGE("Buffer access out-of balance, %d\n", pbuf->ref_cnt);
+ return;
+ }
+
+ syna_pal_mutex_lock(&pbuf->buf_mutex);
+ pbuf->ref_cnt++;
+}
+/**
+ * syna_tcm_buf_unlock()
+ *
+ * Open the access of current buffer structure.
+ *
+ * @param
+ * [ in] pbuf: pointer to an internal buffer
+ *
+ * @return
+ * none
+ */
+static inline void syna_tcm_buf_unlock(struct tcm_buffer *pbuf)
+{
+ if (pbuf->ref_cnt != 1) {
+ LOGE("Buffer access out-of balance, %d\n", pbuf->ref_cnt);
+ return;
+ }
+
+ pbuf->ref_cnt--;
+ syna_pal_mutex_unlock(&pbuf->buf_mutex);
+}
+/**
+ * syna_tcm_buf_release()
+ *
+ * Release the buffer structure.
+ *
+ * @param
+ * [ in] pbuf: pointer to an internal buffer
+ *
+ * @return
+ * none
+ */
+static inline void syna_tcm_buf_release(struct tcm_buffer *pbuf)
+{
+ if (pbuf->ref_cnt != 0)
+ LOGE("Buffer access hold, %d\n", pbuf->ref_cnt);
+
+ syna_pal_mutex_free(&pbuf->buf_mutex);
+ syna_pal_mem_free((void *)pbuf->buf);
+ pbuf->buf_size = 0;
+ pbuf->data_length = 0;
+ pbuf->ref_cnt = 0;
+}
+/**
+ * syna_tcm_buf_copy()
+ *
+ * Helper to copy data from the source buffer to the destination buffer.
+ * The size of destination buffer may be reallocated, if the size is
+ * smaller than the actual data size to be copied.
+ *
+ * @param
+ * [out] dest: pointer to an internal buffer
+ * [ in] src: required size to be extended
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static inline int syna_tcm_buf_copy(struct tcm_buffer *dest,
+ struct tcm_buffer *src)
+{
+ int retval = 0;
+
+ if (dest->buf_size < src->data_length) {
+ retval = syna_tcm_buf_alloc(dest, src->data_length + 1);
+ if (retval < 0) {
+ LOGE("Fail to reallocate the given buffer, size: %d\n",
+ src->data_length + 1);
+ return retval;
+ }
+ }
+
+ /* copy data content to the destination */
+ retval = syna_pal_mem_cpy(dest->buf,
+ dest->buf_size,
+ src->buf,
+ src->buf_size,
+ src->data_length);
+ if (retval < 0) {
+ LOGE("Fail to copy data to caller, size: %d\n",
+ src->data_length);
+ return retval;
+ }
+
+ dest->data_length = src->data_length;
+
+ return retval;
+}
+/**
+ * @section: Reads / Writes Abstraction Function
+ *
+ * @brief: syna_tcm_read
+ * Read the data from bus directly
+ *
+ * @brief: syna_tcm_write
+ * Write the data to bus directly
+ */
+
+/**
+ * syna_tcm_read()
+ *
+ * The bare read function, reading in the requested data bytes
+ * from bus directly.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] rd_data: buffer for storing data retrieved from device
+ * [ in] rd_len: length of reading data in bytes
+ *
+ * @return
+ * the number of data bytes retrieved;
+ * otherwise, negative value, on error.
+ */
+static inline int syna_tcm_read(struct tcm_dev *tcm_dev,
+ unsigned char *rd_data, unsigned int rd_len)
+{
+ struct syna_hw_interface *hw_if;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ hw_if = tcm_dev->hw_if;
+ if (!hw_if->ops_read_data) {
+ LOGE("Invalid hw ops_read function\n");
+ return _ENODEV;
+ }
+
+ return hw_if->ops_read_data(hw_if, rd_data, rd_len);
+}
+
+/**
+ * syna_tcm_write()
+ *
+ * The bare write function, writing the given data bytes to bus directly.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] wr_data: written data
+ * [ in] wr_len: length of written data in bytes
+ *
+ * @return
+ * the number of data bytes retrieved;
+ * otherwise, negative value, on error.
+ */
+static inline int syna_tcm_write(struct tcm_dev *tcm_dev,
+ unsigned char *wr_data, unsigned int wr_len)
+{
+ struct syna_hw_interface *hw_if;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ hw_if = tcm_dev->hw_if;
+ if (!hw_if->ops_write_data) {
+ LOGE("Invalid hw ops_write function\n");
+ return _ENODEV;
+ }
+
+ return hw_if->ops_write_data(hw_if, wr_data, wr_len);
+}
+
+
+#endif /* end of _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ */
diff --git a/tcm/synaptics_touchcom_core_v1.c b/tcm/synaptics_touchcom_core_v1.c
new file mode 100644
index 0000000..d10380f
--- /dev/null
+++ b/tcm/synaptics_touchcom_core_v1.c
@@ -0,0 +1,1078 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TCM touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_core_v1.c
+ *
+ * This file implements the TouchComm version 1 command-response protocol.
+ */
+
+#include "synaptics_touchcom_core_dev.h"
+
+#define TCM_V1_MESSAGE_MARKER 0xa5
+#define TCM_V1_MESSAGE_PADDING 0x5a
+
+/**
+ * @section: Header of TouchComm v1 Message Packet
+ *
+ * The 4-byte header in the TouchComm v1 packet
+ */
+struct tcm_v1_message_header {
+ union {
+ struct {
+ unsigned char marker;
+ unsigned char code;
+ unsigned char length[2];
+ };
+ unsigned char data[MESSAGE_HEADER_SIZE];
+ };
+};
+
+/**
+ * syna_tcm_v1_parse_idinfo()
+ *
+ * Copy the given data to the identification info structure
+ * and parse the basic information, e.g. fw build id.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: data buffer
+ * [ in] size: size of given data buffer
+ * [ in] data_len: length of actual data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v1_parse_idinfo(struct tcm_dev *tcm_dev,
+ unsigned char *data, unsigned int size, unsigned int data_len)
+{
+ int retval;
+ unsigned int wr_size = 0;
+ unsigned int build_id = 0;
+ struct tcm_identification_info *id_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!data) || (data_len == 0)) {
+ LOGE("Invalid given data buffer\n");
+ return _EINVAL;
+ }
+
+ id_info = &tcm_dev->id_info;
+
+ retval = syna_pal_mem_cpy((unsigned char *)id_info,
+ sizeof(struct tcm_identification_info),
+ data,
+ size,
+ MIN(sizeof(*id_info), data_len));
+ if (retval < 0) {
+ LOGE("Fail to copy identification info\n");
+ return retval;
+ }
+
+ build_id = syna_pal_le4_to_uint(id_info->build_id);
+
+ wr_size = syna_pal_le2_to_uint(id_info->max_write_size);
+ tcm_dev->max_wr_size = MIN(wr_size, WR_CHUNK_SIZE);
+ if (tcm_dev->max_wr_size == 0) {
+ tcm_dev->max_wr_size = wr_size;
+ LOGD("max_wr_size = %d\n", tcm_dev->max_wr_size);
+ }
+
+ LOGI("TCM Fw mode: 0x%02x\n", id_info->mode);
+
+ if (tcm_dev->packrat_number != build_id)
+ tcm_dev->packrat_number = build_id;
+
+ tcm_dev->dev_mode = id_info->mode;
+
+ return 0;
+}
+
+/**
+ * syna_tcm_v1_dispatch_report()
+ *
+ * Handle the TouchCom report packet being received.
+ *
+ * If it's an identify report, parse the identification packet and signal
+ * the command completion just in case.
+ * Otherwise, copy the data from internal buffer.in to internal buffer.report
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+static void syna_tcm_v1_dispatch_report(struct tcm_dev *tcm_dev)
+{
+ int retval;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ tcm_msg->report_code = tcm_msg->status_report_code;
+
+ if (tcm_msg->payload_length == 0) {
+ tcm_dev->report_buf.data_length = 0;
+ goto exit;
+ }
+
+ /* The identify report may be resulted from reset or fw mode switching
+ */
+ if (tcm_msg->report_code == REPORT_IDENTIFY) {
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_tcm_v1_parse_idinfo(tcm_dev,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to identify device\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return;
+ }
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ /* in case, the identify info packet is caused by the command */
+ if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) {
+ switch (tcm_msg->command) {
+ case CMD_RESET:
+ LOGD("Reset by CMD_RESET\n");
+ case CMD_REBOOT_TO_ROM_BOOTLOADER:
+ case CMD_RUN_BOOTLOADER_FIRMWARE:
+ case CMD_RUN_APPLICATION_FIRMWARE:
+ case CMD_ENTER_PRODUCTION_TEST_MODE:
+ case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE:
+ tcm_msg->status_report_code = STATUS_OK;
+ tcm_msg->response_code = STATUS_OK;
+ ATOMIC_SET(tcm_msg->command_status,
+ CMD_STATE_IDLE);
+ syna_pal_completion_complete(cmd_completion);
+ goto exit;
+ default:
+ LOGN("Device has been reset\n");
+ ATOMIC_SET(tcm_msg->command_status,
+ CMD_STATE_ERROR);
+ syna_pal_completion_complete(cmd_completion);
+ goto exit;
+ }
+ }
+ }
+
+ /* store the received report into the internal buffer.report */
+ syna_tcm_buf_lock(&tcm_dev->report_buf);
+
+ retval = syna_tcm_buf_alloc(&tcm_dev->report_buf,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.report\n");
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+ goto exit;
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_pal_mem_cpy(tcm_dev->report_buf.buf,
+ tcm_dev->report_buf.buf_size,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to copy payload to buf_report\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+ goto exit;
+ }
+
+ tcm_dev->report_buf.data_length = tcm_msg->payload_length;
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+
+exit:
+ return;
+}
+
+/**
+ * syna_tcm_v1_dispatch_response()
+ *
+ * Handle the response packet.
+ *
+ * Copy the data from internal buffer.in to internal buffer.resp,
+ * and then signal the command completion.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+static void syna_tcm_v1_dispatch_response(struct tcm_dev *tcm_dev)
+{
+ int retval;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ tcm_msg->response_code = tcm_msg->status_report_code;
+
+ if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_BUSY)
+ return;
+
+ if (tcm_msg->payload_length == 0) {
+ tcm_dev->resp_buf.data_length = tcm_msg->payload_length;
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+ goto exit;
+ }
+
+ /* copy the received resp data into the internal buffer.resp */
+ syna_tcm_buf_lock(&tcm_dev->resp_buf);
+
+ retval = syna_tcm_buf_alloc(&tcm_dev->resp_buf,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.resp\n");
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ goto exit;
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_pal_mem_cpy(tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to copy payload to internal resp_buf\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ goto exit;
+ }
+
+ tcm_dev->resp_buf.data_length = tcm_msg->payload_length;
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+
+exit:
+ syna_pal_completion_complete(cmd_completion);
+}
+
+
+/**
+ * syna_tcm_v1_read()
+ *
+ * Read in a TouchCom packet from device.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] rd_length: number of reading bytes;
+ * '0' means to read the message header only
+ * [in/out] buf: pointer to a buffer which is stored the retrieved data
+ * [out] buf_size: size of the buffer pointed
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v1_read(struct tcm_dev *tcm_dev, unsigned int rd_length,
+ unsigned char *buf, unsigned int buf_size)
+{
+ int retval;
+ unsigned int max_rd_size;
+ int retry;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (rd_length == 0)
+ return 0;
+
+ if (rd_length > buf_size) {
+ LOGE("Invalid read length, len: %d, buf_size: %d\n",
+ rd_length, buf_size);
+ return _EINVAL;
+ }
+
+ max_rd_size = tcm_dev->max_rd_size;
+
+ if ((max_rd_size != 0) && (rd_length > max_rd_size)) {
+ LOGE("Invalid read length, len: %d, max_rd_size: %d\n",
+ rd_length, max_rd_size);
+ return _EINVAL;
+ }
+
+ /* read in the message header from device
+ * will do retry if the packet is not expected
+ */
+ for (retry = 0; retry < 10; retry++) {
+ retval = syna_tcm_read(tcm_dev,
+ buf,
+ rd_length
+ );
+ if (retval < 0) {
+ LOGE("Fail to read %d bytes to device\n", rd_length);
+ goto exit;
+ }
+
+ /* check the message header */
+ if (buf[0] == TCM_V1_MESSAGE_MARKER)
+ break;
+
+ LOGE("Incorrect header marker, 0x%02x (retry:%d)\n",
+ buf[0], retry);
+
+ retval = _EIO;
+ syna_pal_sleep_us(RD_RETRY_US_MIN, RD_RETRY_US_MAX);
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_v1_write()
+ *
+ * Construct the TouchCom v1 packet and send it to device.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: command code
+ * [ in] payload: data payload if any
+ * [ in] payload_len: length of data payload if have any
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v1_write(struct tcm_dev *tcm_dev, unsigned char command,
+ unsigned char *payload, unsigned int payload_len)
+{
+ int retval = 0;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ int size;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+
+ syna_tcm_buf_lock(&tcm_msg->out);
+
+ /* allocate the space storing the written data */
+ retval = syna_tcm_buf_alloc(&tcm_msg->out, payload_len + 3);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.out\n");
+ goto exit;
+ }
+
+ if (command != CMD_CONTINUE_WRITE) {
+ /* construct the command packet
+ * size = 1-byte command + 2-byte length + payload
+ */
+ size = payload_len + 3;
+
+ tcm_msg->out.buf[0] = command;
+ tcm_msg->out.buf[1] = (unsigned char)payload_len;
+ tcm_msg->out.buf[2] = (unsigned char)(payload_len >> 8);
+
+ if (payload_len > 0) {
+ retval = syna_pal_mem_cpy(&tcm_msg->out.buf[3],
+ tcm_msg->out.buf_size - 3,
+ payload,
+ payload_len,
+ payload_len
+ );
+ if (retval < 0) {
+ LOGE("Fail to copy payload\n");
+ goto exit;
+ }
+ }
+ } else {
+ /* construct the continued writes packet
+ * size = 1-byte continued write + payload
+ */
+ size = payload_len + 1;
+
+ tcm_msg->out.buf[0] = CMD_CONTINUE_WRITE;
+
+ retval = syna_pal_mem_cpy(&tcm_msg->out.buf[1],
+ tcm_msg->out.buf_size - 1,
+ payload,
+ payload_len,
+ payload_len
+ );
+ if (retval < 0) {
+ LOGE("Fail to copy continued write\n");
+ goto exit;
+ }
+ }
+
+ /* write command packet to the device */
+ retval = syna_tcm_write(tcm_dev,
+ tcm_msg->out.buf,
+ size
+ );
+ if (retval < 0) {
+ LOGE("Fail to write %d bytes to device\n", size);
+ goto exit;
+ }
+
+exit:
+ syna_tcm_buf_unlock(&tcm_msg->out);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v1_continued_read()
+ *
+ * The remaining data payload is read in continuously until the end of data.
+ * All the retrieved data is appended to the internal buffer.in.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] length: length of payload data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v1_continued_read(struct tcm_dev *tcm_dev,
+ unsigned int length)
+{
+ int retval = 0;
+ unsigned char code;
+ unsigned int idx;
+ unsigned int offset;
+ unsigned int chunks;
+ unsigned int chunk_space;
+ unsigned int xfer_length;
+ unsigned int total_length;
+ unsigned int remaining_length;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+
+ /* continued read packet contains the header, payload, and a padding */
+ total_length = MESSAGE_HEADER_SIZE + length + 1;
+ remaining_length = total_length - MESSAGE_HEADER_SIZE;
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ /* in case the current buf.in is smaller than requested size */
+ retval = syna_tcm_buf_realloc(&tcm_msg->in,
+ total_length + 1);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf_in\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return retval;
+ }
+
+ /* available chunk space for payload =
+ * total chunk size - (header marker byte + header status byte)
+ */
+ if (tcm_dev->max_rd_size == 0)
+ chunk_space = remaining_length;
+ else
+ chunk_space = tcm_dev->max_rd_size - 2;
+
+ chunks = syna_pal_ceil_div(remaining_length, chunk_space);
+ chunks = chunks == 0 ? 1 : chunks;
+
+ offset = MESSAGE_HEADER_SIZE;
+
+ syna_tcm_buf_lock(&tcm_msg->temp);
+
+ for (idx = 0; idx < chunks; idx++) {
+ if (remaining_length > chunk_space)
+ xfer_length = chunk_space;
+ else
+ xfer_length = remaining_length;
+
+ if (xfer_length == 1) {
+ tcm_msg->in.buf[offset] = TCM_V1_MESSAGE_PADDING;
+ offset += xfer_length;
+ remaining_length -= xfer_length;
+ continue;
+ }
+
+ /* allocate the internal temp buffer */
+ retval = syna_tcm_buf_alloc(&tcm_msg->temp,
+ xfer_length + 2);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.temp\n");
+ goto exit;
+ }
+ /* retrieve data from the bus
+ * data should include header marker and status code
+ */
+ retval = syna_tcm_v1_read(tcm_dev,
+ xfer_length + 2,
+ tcm_msg->temp.buf,
+ tcm_msg->temp.buf_size
+ );
+ if (retval < 0) {
+ LOGE("Fail to read %d bytes from device\n",
+ xfer_length + 2);
+ goto exit;
+ }
+
+ /* check the data content */
+ code = tcm_msg->temp.buf[1];
+
+ if (code != STATUS_CONTINUED_READ) {
+ LOGE("Incorrect status code 0x%02x at %d out of %d\n",
+ code, idx, chunks);
+ retval = _EIO;
+ goto exit;
+ }
+
+ /* copy data from internal buffer.temp to buffer.in */
+ retval = syna_pal_mem_cpy(&tcm_msg->in.buf[offset],
+ tcm_msg->in.buf_size - offset,
+ &tcm_msg->temp.buf[2],
+ tcm_msg->temp.buf_size - 2,
+ xfer_length);
+ if (retval < 0) {
+ LOGE("Fail to copy payload\n");
+ goto exit;
+ }
+
+ offset += xfer_length;
+ remaining_length -= xfer_length;
+ }
+
+exit:
+ syna_tcm_buf_unlock(&tcm_msg->temp);
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v1_read_message()
+ *
+ * Read in a TouchCom packet from device.
+ * The packet including its payload is read in from device and stored in
+ * the internal buffer.resp or buffer.report based on the code received.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] status_report_code: status code or report code received
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static int syna_tcm_v1_read_message(struct tcm_dev *tcm_dev,
+ unsigned char *status_report_code)
+{
+ int retval = 0;
+ struct tcm_v1_message_header *header;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_mutex_t *rw_mutex = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ rw_mutex = &tcm_msg->rw_mutex;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ if (status_report_code)
+ *status_report_code = STATUS_INVALID;
+
+ syna_pal_mutex_lock(rw_mutex);
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ /* read in the message header from device */
+ retval = syna_tcm_v1_read(tcm_dev,
+ MESSAGE_HEADER_SIZE,
+ tcm_msg->in.buf,
+ tcm_msg->in.buf_size
+ );
+ if (retval < 0) {
+ LOGE("Fail to read message header from device\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ tcm_msg->status_report_code = STATUS_INVALID;
+ tcm_msg->payload_length = 0;
+ goto exit;
+ }
+
+ /* check the message header */
+ header = (struct tcm_v1_message_header *)tcm_msg->in.buf;
+
+ tcm_msg->status_report_code = header->code;
+
+ tcm_msg->payload_length = syna_pal_le2_to_uint(header->length);
+
+ if (tcm_msg->status_report_code != STATUS_IDLE)
+ LOGD("Status code: 0x%02x, length: %d (%02x %02x %02x %02x)\n",
+ tcm_msg->status_report_code, tcm_msg->payload_length,
+ header->data[0], header->data[1], header->data[2],
+ header->data[3]);
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ if ((tcm_msg->status_report_code <= STATUS_ERROR) ||
+ (tcm_msg->status_report_code == STATUS_INVALID)) {
+ switch (tcm_msg->status_report_code) {
+ case STATUS_OK:
+ break;
+ case STATUS_CONTINUED_READ:
+ LOGE("Out-of-sync continued read\n");
+ case STATUS_IDLE:
+ tcm_msg->payload_length = 0;
+ retval = 0;
+ goto exit;
+ default:
+ LOGE("Incorrect Status code, 0x%02x\n",
+ tcm_msg->status_report_code);
+ tcm_msg->payload_length = 0;
+ goto do_dispatch;
+ }
+ }
+
+ if (tcm_msg->payload_length == 0)
+ goto do_dispatch;
+
+ /* retrieve the remaining data, if any */
+ retval = syna_tcm_v1_continued_read(tcm_dev,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to do continued read\n");
+ goto exit;
+ }
+
+ /* refill the header for dispatching */
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ tcm_msg->in.buf[0] = TCM_V1_MESSAGE_MARKER;
+ tcm_msg->in.buf[1] = tcm_msg->status_report_code;
+ tcm_msg->in.buf[2] = (unsigned char)tcm_msg->payload_length;
+ tcm_msg->in.buf[3] = (unsigned char)(tcm_msg->payload_length >> 8);
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+do_dispatch:
+ /* duplicate the data to external buffer */
+ syna_tcm_buf_lock(&tcm_dev->external_buf);
+ if (tcm_msg->payload_length > 0) {
+ retval = syna_tcm_buf_alloc(&tcm_dev->external_buf,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory, external_buf invalid\n");
+ } else {
+ retval = syna_pal_mem_cpy(&tcm_dev->external_buf.buf[0],
+ tcm_msg->payload_length,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0)
+ LOGE("Fail to copy data to external buffer\n");
+ }
+ }
+ tcm_dev->external_buf.data_length = tcm_msg->payload_length;
+ syna_tcm_buf_unlock(&tcm_dev->external_buf);
+
+ /* process the retrieved packet */
+ if (tcm_msg->status_report_code >= REPORT_IDENTIFY)
+ syna_tcm_v1_dispatch_report(tcm_dev);
+ else
+ syna_tcm_v1_dispatch_response(tcm_dev);
+
+ /* copy the status report code to caller */
+ if (status_report_code)
+ *status_report_code = tcm_msg->status_report_code;
+
+ retval = 0;
+
+exit:
+ /* raise the completion event when errors out */
+ if (retval < 0) {
+ if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) {
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR);
+ syna_pal_completion_complete(cmd_completion);
+ }
+ }
+
+ syna_pal_mutex_unlock(rw_mutex);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v1_write_message()
+ *
+ * Write message including command and its payload to TouchCom device.
+ * Then, the response of the command generated by the device will be
+ * read in and stored in internal buffer.resp.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: TouchComm command
+ * [ in] payload: data payload, if any
+ * [ in] payload_len: length of data payload, if any
+ * [out] resp_code: response code returned
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static int syna_tcm_v1_write_message(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_len, unsigned char *resp_code,
+ unsigned int delay_ms_resp)
+{
+ int retval = 0;
+ unsigned int idx;
+ unsigned int chunks;
+ unsigned int chunk_space;
+ unsigned int xfer_length;
+ unsigned int remaining_length;
+ int timeout = 0;
+ int polling_ms = 0;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_mutex_t *cmd_mutex = NULL;
+ syna_pal_mutex_t *rw_mutex = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+ bool has_irq_ctrl = false;
+ bool in_polling = false;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ cmd_mutex = &tcm_msg->cmd_mutex;
+ rw_mutex = &tcm_msg->rw_mutex;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ if (resp_code)
+ *resp_code = STATUS_INVALID;
+
+ /* indicate which mode is used */
+ in_polling = (delay_ms_resp != RESP_IN_ATTN);
+
+ /* irq control is enabled only when the operations is implemented
+ * and the current status of irq is enabled.
+ * do not enable irq if it is disabled by someone.
+ */
+ has_irq_ctrl = (bool)(tcm_dev->hw_if->ops_enable_irq != NULL);
+ has_irq_ctrl &= tcm_dev->hw_if->bdata_attn.irq_enabled;
+
+ /* disable irq when using polling mode */
+ if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq)
+ tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, false);
+
+ syna_pal_mutex_lock(cmd_mutex);
+
+ syna_pal_mutex_lock(rw_mutex);
+
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY);
+
+ /* reset the command completion */
+ syna_pal_completion_reset(cmd_completion);
+
+ tcm_msg->command = command;
+
+ remaining_length = payload_len;
+
+ /* available space for payload = total size - command byte */
+ if (tcm_dev->max_wr_size == 0)
+ chunk_space = remaining_length;
+ else
+ chunk_space = tcm_dev->max_wr_size - 1;
+
+ chunks = syna_pal_ceil_div(remaining_length, chunk_space);
+ chunks = chunks == 0 ? 1 : chunks;
+
+ LOGD("Command: 0x%02x, payload len: %d\n", command, payload_len);
+
+ /* send out command packets
+ *
+ * separate into several sub-packets if the overall size is over
+ * than the maximum write size.
+ */
+ for (idx = 0; idx < chunks; idx++) {
+ if (remaining_length > chunk_space)
+ xfer_length = chunk_space;
+ else
+ xfer_length = remaining_length;
+
+ if (idx == 0) {
+ retval = syna_tcm_v1_write(tcm_dev,
+ tcm_msg->command,
+ &payload[0],
+ xfer_length);
+ } else {
+ retval = syna_tcm_v1_write(tcm_dev,
+ CMD_CONTINUE_WRITE,
+ &payload[idx * chunk_space],
+ xfer_length);
+ }
+
+ if (retval < 0) {
+ LOGE("Fail to write %d bytes to device\n",
+ xfer_length);
+ syna_pal_mutex_unlock(rw_mutex);
+ goto exit;
+ }
+
+ remaining_length -= xfer_length;
+
+ if (chunks > 1)
+ syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX);
+ }
+
+ syna_pal_mutex_unlock(rw_mutex);
+
+ /* handle the command response
+ *
+ * assuming to select the polling mode, the while-loop below will
+ * repeatedly read in the respose data based on the given polling
+ * time; otherwise, wait until receiving a completion event from
+ * interupt thread.
+ */
+ timeout = 0;
+ if (!in_polling)
+ polling_ms = CMD_RESPONSE_TIMEOUT_MS;
+ else
+ polling_ms = delay_ms_resp;
+
+ do {
+ /* wait for the completion event triggered by read_message */
+ retval = syna_pal_completion_wait_for(cmd_completion,
+ polling_ms);
+ /* reset the status when times out and keep in polling */
+ if (retval < 0)
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY);
+
+ /* break when geting a valid resp; otherwise, keep in polling */
+ if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE)
+ goto check_response;
+
+ if (in_polling) {
+ /* retrieve the message packet back */
+ retval = syna_tcm_v1_read_message(tcm_dev, NULL);
+ /* keep in polling if still not having a valid resp */
+ if (retval < 0)
+ syna_pal_completion_reset(cmd_completion);
+ }
+
+ timeout += polling_ms + 10;
+
+ } while (timeout < CMD_RESPONSE_TIMEOUT_MS);
+
+ /* check the status of response data
+ * according to the touchcomm spec, each command message
+ * should have an associated reponse message.
+ */
+check_response:
+ if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_IDLE) {
+ if (timeout >= CMD_RESPONSE_TIMEOUT_MS) {
+ LOGE("Timed out wait for response of command 0x%02x\n",
+ command);
+ retval = _ETIMEDOUT;
+ goto exit;
+ } else {
+ LOGE("Fail to get valid response of command 0x%02x\n",
+ command);
+ retval = _EIO;
+ goto exit;
+ }
+ }
+
+ /* copy response code to the caller */
+ if (resp_code)
+ *resp_code = tcm_msg->response_code;
+
+ if (tcm_msg->response_code != STATUS_OK) {
+ LOGE("Error code 0x%02x of command 0x%02x\n",
+ tcm_msg->response_code, tcm_msg->command);
+ retval = _EIO;
+ } else {
+ retval = 0;
+ }
+
+exit:
+ tcm_msg->command = CMD_NONE;
+
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+
+ syna_pal_mutex_unlock(cmd_mutex);
+
+ /* recovery the irq if using polling mode */
+ if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq)
+ tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, true);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v1_detect()
+ *
+ * For TouchCom v1 protocol, the given raw data must start with a specific
+ * maker code. If so, read the remaining packet from TouchCom device.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: raw 4-byte data
+ * [ in] size: length of input data in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_v1_detect(struct tcm_dev *tcm_dev, unsigned char *data,
+ unsigned int size)
+{
+ int retval;
+ struct tcm_v1_message_header *header;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ unsigned int payload_length = 0;
+ unsigned char resp_code = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!data) || (size < MESSAGE_HEADER_SIZE)) {
+ LOGE("Invalid parameters\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+
+ header = (struct tcm_v1_message_header *)data;
+
+ if (header->marker != TCM_V1_MESSAGE_MARKER)
+ return _ENODEV;
+
+ /* after initially powering on, the identify report should be the
+ * first packet
+ */
+ if (header->code == REPORT_IDENTIFY) {
+
+ payload_length = syna_pal_le2_to_uint(header->length);
+
+ /* retrieve the identify info packet */
+ retval = syna_tcm_v1_continued_read(tcm_dev,
+ payload_length);
+ if (retval < 0) {
+ LOGE("Fail to read in identify info packet\n");
+ return retval;
+ }
+ } else {
+ /* if not, send an identify command instead */
+ retval = syna_tcm_v1_write_message(tcm_dev,
+ CMD_IDENTIFY,
+ NULL,
+ 0,
+ &resp_code,
+ RESP_IN_POLLING);
+ if (retval < 0) {
+ /* in case the identify command is not working,
+ * send a rest command as the workaround
+ */
+ retval = syna_tcm_v1_write_message(tcm_dev,
+ CMD_RESET,
+ NULL,
+ 0,
+ &resp_code,
+ RESET_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to identify the device\n");
+ return _ENODEV;
+ }
+ }
+
+ payload_length = tcm_msg->payload_length;
+ }
+
+ /* parse the identify info packet */
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_tcm_v1_parse_idinfo(tcm_dev,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ payload_length);
+ if (retval < 0) {
+ LOGE("Fail to identify device\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return retval;
+ }
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ /* expose the read / write operations */
+ tcm_dev->read_message = syna_tcm_v1_read_message;
+ tcm_dev->write_message = syna_tcm_v1_write_message;
+
+ return retval;
+}
diff --git a/tcm/synaptics_touchcom_core_v2.c b/tcm/synaptics_touchcom_core_v2.c
new file mode 100644
index 0000000..c3f0d56
--- /dev/null
+++ b/tcm/synaptics_touchcom_core_v2.c
@@ -0,0 +1,1445 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TCM touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_core_v2.c
+ *
+ * This file implements the TouchComm version 2 command-response protocol
+ */
+
+#include "synaptics_touchcom_core_dev.h"
+
+#define BITS_IN_MESSAGE_HEADER (MESSAGE_HEADER_SIZE * 8)
+
+#define HOST_PRIMARY (0)
+
+#define COMMAND_RETRY_TIMES (5)
+
+#define CHECK_PACKET_CRC
+
+/**
+ * @section: Header of TouchComm v2 Message Packet
+ *
+ * The 4-byte header in the TouchComm v2 packet
+ */
+struct tcm_v2_message_header {
+ union {
+ struct {
+ unsigned char code;
+ unsigned char length[2];
+ unsigned char byte3;
+ };
+ unsigned char data[MESSAGE_HEADER_SIZE];
+ };
+};
+
+/* helper to execute a tcm v2 command
+ */
+static int syna_tcm_v2_execute_cmd_request(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_length);
+
+/**
+ * @section: Lookup table for checksum calculation
+ *
+ * @subsection: crc6_table
+ * lookup table for crc6 calculation
+ *
+ * @subsection: crc16_table
+ * lookup table for crc16 calculation
+ */
+static unsigned short crc6_table[16] = {
+ 0, 268, 536, 788, 1072, 1340, 1576, 1828,
+ 2144, 2412, 2680, 2932, 3152, 3420, 3656, 3908
+};
+
+static unsigned short crc16_table[256] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
+ 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
+ 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
+ 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
+ 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
+ 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
+ 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
+ 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
+ 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
+ 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
+ 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
+ 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
+ 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
+ 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
+ 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
+ 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
+ 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
+ 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
+ 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
+ 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
+ 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
+ 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
+ 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
+};
+
+/**
+ * syna_tcm_v2_crc6()
+ *
+ * Calculate the crc-6 with polynomial for TouchCom v2 header.
+ *
+ * @param
+ * [ in] p: byte array for the calculation
+ * [ in] bits: number of bits
+ *
+ * @return
+ * the crc-6 value
+ */
+static unsigned char syna_tcm_v2_crc6(unsigned char *p, unsigned int bits)
+{
+ unsigned short r = 0x003F << 2;
+ unsigned short x;
+
+ for (; bits > 8; bits -= 8) {
+ r ^= *p++;
+ r = (r << 4) ^ crc6_table[r >> 4];
+ r = (r << 4) ^ crc6_table[r >> 4];
+ }
+
+ if (bits > 0) {
+ x = *p;
+ while (bits--) {
+ if (x & 0x80)
+ r ^= 0x80;
+
+ x <<= 1;
+ r <<= 1;
+ if (r & 0x100)
+ r ^= (0x03 << 2);
+ }
+ }
+
+ return (unsigned char)((r >> 2) & 0x3F);
+}
+/**
+ * syna_tcm_v2_crc6()
+ *
+ * Calculate the crc-16 for TouchCom v2 packet.
+ *
+ * @param
+ * [ in] p: byte array for the calculation
+ * [ in] len: length in bytes
+ *
+ * @return
+ * the crc-16 value
+ */
+static unsigned short syna_tcm_v2_crc16(unsigned char *p, unsigned int len)
+{
+ unsigned short r = 0xFFFF;
+
+ while (len--)
+ r = (r << 8) ^ crc16_table[(r >> 8) ^ *p++];
+
+ return r;
+}
+
+/**
+ * syna_tcm_v2_set_max_read_size()
+ *
+ * Configure the max length for message reading.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+static int syna_tcm_v2_set_max_read_size(struct tcm_dev *tcm_dev)
+{
+ int retval;
+ unsigned int rd_size;
+ struct tcm_identification_info *id_info;
+ unsigned char data[2] = { 0 };
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ id_info = &tcm_dev->id_info;
+
+ rd_size = syna_pal_le2_to_uint(id_info->max_read_size);
+
+ if (rd_size == 0) {
+ LOGE("Invalid max_read_length: %d\n", rd_size);
+ return 0;
+ }
+
+ if (rd_size == tcm_dev->max_rd_size)
+ return 0;
+
+ tcm_dev->max_rd_size = MIN(rd_size, tcm_dev->max_rd_size);
+
+ LOGD("max_rd_size = %d\n", tcm_dev->max_rd_size);
+
+ data[0] = (unsigned char)tcm_dev->max_rd_size;
+ data[1] = (unsigned char)(tcm_dev->max_rd_size >> 8);
+
+ retval = syna_tcm_v2_execute_cmd_request(tcm_dev,
+ CMD_TCM2_SET_MAX_READ_LENGTH,
+ data,
+ sizeof(data));
+ if (retval < 0) {
+ LOGE("Fail to set max_read_length\n");
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+ * syna_tcm_v2_parse_idinfo()
+ *
+ * Copy the given data to the identification info structure
+ * and parse the basic information, e.g. fw build id.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: data buffer
+ * [ in] size: size of given data buffer
+ * [ in] data_len: length of actual data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v2_parse_idinfo(struct tcm_dev *tcm_dev,
+ unsigned char *data, unsigned int size, unsigned int data_len)
+{
+ int retval;
+ unsigned int wr_size = 0;
+ unsigned int build_id = 0;
+ struct tcm_identification_info *id_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!data) || (data_len == 0)) {
+ LOGE("Invalid given data buffer\n");
+ return _EINVAL;
+ }
+
+ id_info = &tcm_dev->id_info;
+
+ retval = syna_pal_mem_cpy((unsigned char *)id_info,
+ sizeof(struct tcm_identification_info),
+ data,
+ size,
+ MIN(sizeof(*id_info), data_len));
+ if (retval < 0) {
+ LOGE("Fail to copy identification info\n");
+ return retval;
+ }
+
+ build_id = syna_pal_le4_to_uint(id_info->build_id);
+
+ wr_size = syna_pal_le2_to_uint(id_info->max_write_size);
+ tcm_dev->max_wr_size = MIN(wr_size, WR_CHUNK_SIZE);
+ if (tcm_dev->max_wr_size == 0) {
+ tcm_dev->max_wr_size = wr_size;
+ LOGD("max_wr_size = %d\n", tcm_dev->max_wr_size);
+ }
+
+ LOGI("TCM Fw mode: 0x%02x\n", id_info->mode);
+
+ if (tcm_dev->packrat_number != build_id)
+ tcm_dev->packrat_number = build_id;
+
+ /* set up the max. reading length */
+ retval = syna_tcm_v2_set_max_read_size(tcm_dev);
+ if (retval < 0) {
+ LOGE("Fail to setup the max reading length\n");
+ return retval;
+ }
+
+ tcm_dev->dev_mode = id_info->mode;
+
+ return 0;
+}
+
+/**
+ * syna_tcm_v2_dispatch_report()
+ *
+ * Handle the TouchCom report packet being received.
+ *
+ * If it's an identify report, parse the identification packet and signal
+ * the command completion just in case.
+ * Otherwise, copy the data from internal buffer.in to internal buffer.report
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+static void syna_tcm_v2_dispatch_report(struct tcm_dev *tcm_dev)
+{
+ int retval;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ tcm_msg->report_code = tcm_msg->status_report_code;
+
+ if (tcm_msg->payload_length == 0) {
+ tcm_dev->resp_buf.data_length = tcm_msg->payload_length;
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+ goto exit;
+ }
+
+ /* The identify report may be resulted from reset or fw mode switching
+ */
+ if (tcm_msg->report_code == REPORT_IDENTIFY) {
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_tcm_v2_parse_idinfo(tcm_dev,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to identify device\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return;
+ }
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ /* in case, the identify info packet is caused by the command */
+ if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) {
+ switch (tcm_msg->command) {
+ case CMD_RESET:
+ LOGD("Reset by CMD_RESET\n");
+ case CMD_REBOOT_TO_ROM_BOOTLOADER:
+ case CMD_RUN_BOOTLOADER_FIRMWARE:
+ case CMD_RUN_APPLICATION_FIRMWARE:
+ case CMD_ENTER_PRODUCTION_TEST_MODE:
+ case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE:
+ tcm_msg->status_report_code = STATUS_OK;
+ tcm_msg->response_code = STATUS_OK;
+ ATOMIC_SET(tcm_msg->command_status,
+ CMD_STATE_IDLE);
+ syna_pal_completion_complete(cmd_completion);
+ goto exit;
+ default:
+ LOGN("Device has been reset\n");
+ ATOMIC_SET(tcm_msg->command_status,
+ CMD_STATE_ERROR);
+ syna_pal_completion_complete(cmd_completion);
+ goto exit;
+ }
+ }
+ }
+
+ /* store the received report into the internal buffer.report */
+ syna_tcm_buf_lock(&tcm_dev->report_buf);
+
+ retval = syna_tcm_buf_alloc(&tcm_dev->report_buf,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.report\n");
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+ goto exit;
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_pal_mem_cpy(tcm_dev->report_buf.buf,
+ tcm_dev->report_buf.buf_size,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to copy payload to buf_report\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+ goto exit;
+ }
+
+ tcm_dev->report_buf.data_length = tcm_msg->payload_length;
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+
+exit:
+ return;
+}
+
+/**
+ * syna_tcm_v2_dispatch_response()
+ *
+ * Handle the response packet.
+ *
+ * Copy the data from internal buffer.in to internal buffer.resp,
+ * and then signal the command completion.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+static void syna_tcm_v2_dispatch_response(struct tcm_dev *tcm_dev)
+{
+ int retval;
+ unsigned int resp_data_length;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_BUSY)
+ return;
+
+ resp_data_length = tcm_msg->payload_length;
+
+ if (resp_data_length == 0) {
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+ goto exit;
+ }
+
+ /* store the received report into the temporary buffer */
+ syna_tcm_buf_lock(&tcm_dev->resp_buf);
+
+ retval = syna_tcm_buf_alloc(&tcm_dev->resp_buf,
+ resp_data_length + 1);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.resp\n");
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR);
+ goto exit;
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_pal_mem_cpy(tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ resp_data_length);
+ if (retval < 0) {
+ LOGE("Fail to copy payload to internal resp_buf\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR);
+ goto exit;
+ }
+
+ tcm_dev->resp_buf.data_length = resp_data_length;
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+
+exit:
+ syna_pal_completion_complete(cmd_completion);
+}
+
+/**
+ * syna_tcm_v2_read()
+ *
+ * Read in a TouchCom packet from device.
+ * Checking the CRC is necessary to ensure a valid message received.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] rd_length: number of reading bytes;
+ * '0' means to read the message header only
+ * [out] buf: pointer to a buffer which is stored the retrieved data
+ * [out] buf_size: size of the buffer pointed
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v2_read(struct tcm_dev *tcm_dev, unsigned int rd_length,
+ unsigned char **buf, unsigned int *buf_size)
+{
+ int retval;
+ struct tcm_v2_message_header *header;
+ int max_rd_size;
+ int xfer_len;
+ unsigned char crc6 = 0;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ max_rd_size = tcm_dev->max_rd_size;
+
+ /* continued packet crc if containing payload data */
+ xfer_len = (rd_length > 0) ? (rd_length + 2) : rd_length;
+ xfer_len += sizeof(struct tcm_v2_message_header);
+
+ if ((max_rd_size != 0) && (xfer_len > max_rd_size)) {
+ LOGE("Invalid xfer length, len: %d, max_rd_size: %d\n",
+ xfer_len, max_rd_size);
+ tcm_msg->status_report_code = STATUS_INVALID;
+ return _EINVAL;
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->temp);
+
+ /* allocate the internal temp buffer */
+ retval = syna_tcm_buf_alloc(&tcm_msg->temp, xfer_len);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.temp\n");
+ goto exit;
+ }
+ /* read data from the bus */
+ retval = syna_tcm_read(tcm_dev,
+ tcm_msg->temp.buf,
+ xfer_len);
+ if (retval < 0) {
+ LOGE("Fail to read from device\n");
+ goto exit;
+ }
+
+ header = (struct tcm_v2_message_header *)tcm_msg->temp.buf;
+
+ /* check header crc always */
+ crc6 = syna_tcm_v2_crc6(header->data, BITS_IN_MESSAGE_HEADER);
+ if (crc6 != 0) {
+ LOGE("Invalid header crc: 0x%02x\n", (header->byte3 & 0x3f));
+
+ tcm_msg->status_report_code = STATUS_PACKET_CORRUPTED;
+ goto exit;
+ }
+
+#ifdef CHECK_PACKET_CRC
+ /* check packet crc */
+ if (rd_length > 0) {
+ if (syna_tcm_v2_crc16(&tcm_msg->temp.buf[0], xfer_len) != 0) {
+ LOGE("Invalid packet crc: %02x %02x\n",
+ tcm_msg->temp.buf[xfer_len - 2],
+ tcm_msg->temp.buf[xfer_len - 1]);
+
+ tcm_msg->status_report_code = STATUS_PACKET_CORRUPTED;
+ goto exit;
+ }
+ }
+#endif
+
+ tcm_msg->status_report_code = header->code;
+
+ tcm_msg->payload_length = syna_pal_le2_to_uint(header->length);
+
+ if (tcm_msg->status_report_code != STATUS_IDLE)
+ LOGD("Status code: 0x%02x, length: %d (%02x %02x %02x %02x)\n",
+ tcm_msg->status_report_code, tcm_msg->payload_length,
+ header->data[0], header->data[1], header->data[2],
+ header->data[3]);
+
+ *buf = tcm_msg->temp.buf;
+ *buf_size = tcm_msg->temp.buf_size;
+
+exit:
+ syna_tcm_buf_unlock(&tcm_msg->temp);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_write()
+ *
+ * Construct the TouchCom v2 packet and send it to device.
+ * Add 4-byte header at the beginning of a message and appended crc if needed.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: command code
+ * [ in] payload: data payload if any
+ * [ in] payload_len: length of data payload if have any
+ * [ in] resend: flag for re-sending the packet
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v2_write(struct tcm_dev *tcm_dev, unsigned char command,
+ unsigned char *payload, unsigned int payload_len, bool resend)
+{
+ int retval;
+ struct tcm_v2_message_header *header;
+ unsigned char bits = BITS_IN_MESSAGE_HEADER - 6;
+ int xfer_len;
+ int size = MESSAGE_HEADER_SIZE + payload_len;
+ unsigned short crc16;
+ int max_wr_size;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ max_wr_size = tcm_dev->max_wr_size;
+
+ /* continued packet crc if containing payload data */
+ xfer_len = (payload_len > 0) ? (payload_len + 2) : payload_len;
+ xfer_len += sizeof(struct tcm_v2_message_header);
+
+ if ((max_wr_size != 0) && (xfer_len > max_wr_size)) {
+ LOGE("Invalid xfer length, len: %d, max_wr_size: %d\n",
+ xfer_len, max_wr_size);
+ tcm_msg->status_report_code = STATUS_INVALID;
+ return _EINVAL;
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->out);
+
+ /* allocate the internal out buffer */
+ retval = syna_tcm_buf_alloc(&tcm_msg->out, xfer_len);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf.out\n");
+ goto exit;
+ }
+
+ /* construct packet header */
+ header = (struct tcm_v2_message_header *)tcm_msg->out.buf;
+
+ if (resend)
+ tcm_msg->seq_toggle -= 1;
+
+ header->code = command;
+ header->length[0] = (unsigned char)payload_len;
+ header->length[1] = (unsigned char)(payload_len >> 8);
+ header->byte3 = ((HOST_PRIMARY & 0x01) << 7);
+ header->byte3 |= ((tcm_msg->seq_toggle++ & 0x01) << 6);
+ header->byte3 |= syna_tcm_v2_crc6(header->data, bits);
+
+ /* copy payload, if any */
+ if (payload_len) {
+ retval = syna_pal_mem_cpy(
+ &tcm_msg->out.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->out.buf_size - MESSAGE_HEADER_SIZE,
+ payload,
+ payload_len,
+ payload_len);
+ if (retval < 0) {
+ LOGE("Fail to copy payload data\n");
+ goto exit;
+ }
+
+ /* append packet crc */
+ crc16 = syna_tcm_v2_crc16(&tcm_msg->out.buf[0], size);
+ tcm_msg->out.buf[size] = (unsigned char)((crc16 >> 8) & 0xFF);
+ tcm_msg->out.buf[size + 1] = (unsigned char)(crc16 & 0xFF);
+ }
+
+ /* write command packet to the bus */
+ retval = syna_tcm_write(tcm_dev,
+ tcm_msg->out.buf,
+ xfer_len);
+ if (retval < 0) {
+ LOGE("Fail to write to device\n");
+ goto exit;
+ }
+
+exit:
+ syna_tcm_buf_unlock(&tcm_msg->out);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_continued_read()
+ *
+ * Write a CMD_ACK to read in the remaining data payload continuously
+ * until the end of data. All the retrieved data is appended to the
+ * internal buffer.in.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] length: remaining data length in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v2_continued_read(struct tcm_dev *tcm_dev,
+ unsigned int length)
+{
+ int retval;
+ unsigned char *tmp_buf;
+ unsigned int tmp_buf_size;
+ int retry_cnt = 0;
+ unsigned int idx;
+ unsigned int offset;
+ unsigned int chunks;
+ unsigned int chunk_space;
+ unsigned int xfer_length;
+ unsigned int total_length;
+ unsigned int remaining_length;
+ unsigned char command;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+
+ /* continued read packet contains the header and its payload */
+ total_length = MESSAGE_HEADER_SIZE + tcm_msg->payload_length;
+
+ remaining_length = length;
+
+ offset = tcm_msg->payload_length - length;
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ /* extend the internal buf_in if needed */
+ retval = syna_tcm_buf_realloc(&tcm_msg->in,
+ total_length + 1);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal buf_in\n");
+ goto exit;
+ }
+
+ /* available space for payload = total chunk size - header - crc */
+ chunk_space = tcm_dev->max_rd_size;
+ if (chunk_space == 0)
+ chunk_space = remaining_length;
+ else
+ chunk_space = chunk_space - (MESSAGE_HEADER_SIZE + 2);
+
+ chunks = syna_pal_ceil_div(remaining_length, chunk_space);
+ chunks = chunks == 0 ? 1 : chunks;
+
+ offset += MESSAGE_HEADER_SIZE;
+
+ /* send CMD_ACK for a continued read */
+ command = CMD_TCM2_ACK;
+
+ for (idx = 0; idx < chunks; idx++) {
+retry:
+ LOGD("Command: 0x%02x\n", command);
+
+ /* construct the command packet */
+ retval = syna_tcm_v2_write(tcm_dev,
+ command,
+ NULL,
+ 0,
+ false);
+ if (retval < 0) {
+ LOGE("Fail to send CMD_TCM2_ACK in continued read\n");
+ goto exit;
+ }
+
+ if (remaining_length > chunk_space)
+ xfer_length = chunk_space;
+ else
+ xfer_length = remaining_length;
+
+ /* read in the requested size of data */
+ retval = syna_tcm_v2_read(tcm_dev,
+ xfer_length,
+ &tmp_buf,
+ &tmp_buf_size);
+ if (retval < 0) {
+ LOGE("Fail to read %d bytes from device\n",
+ xfer_length);
+ goto exit;
+ }
+
+ /* If see an error, retry the previous read transaction
+ * Send RETRY instead of CMD_ACK
+ */
+ if (tcm_msg->status_report_code == STATUS_PACKET_CORRUPTED) {
+ if (retry_cnt > COMMAND_RETRY_TIMES) {
+ LOGE("Continued read packet corrupted\n");
+ goto exit;
+ }
+
+ retry_cnt += 1;
+ command = CMD_TCM2_RETRY;
+
+ LOGW("Read corrupted, retry %d\n", retry_cnt);
+ goto retry;
+ }
+
+ retry_cnt = 0;
+ command = CMD_TCM2_ACK;
+
+ /* append data from temporary buffer to in_buf */
+ syna_tcm_buf_lock(&tcm_msg->temp);
+
+ /* copy data from internal buffer.temp to buffer.in */
+ retval = syna_pal_mem_cpy(&tcm_msg->in.buf[offset],
+ tcm_msg->in.buf_size - offset,
+ &tmp_buf[MESSAGE_HEADER_SIZE],
+ tmp_buf_size - MESSAGE_HEADER_SIZE,
+ xfer_length);
+ if (retval < 0) {
+ LOGE("Fail to copy payload to internal buf_in\n");
+ syna_tcm_buf_unlock(&tcm_msg->temp);
+ goto exit;
+ }
+
+ syna_tcm_buf_unlock(&tcm_msg->temp);
+
+ remaining_length -= xfer_length;
+
+ offset += xfer_length;
+ }
+
+ retval = 0;
+
+exit:
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_get_response()
+ *
+ * Read in the response packet from device.
+ * If containing payload data, use continued_read() function and read the
+ * remaining payload data.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v2_get_response(struct tcm_dev *tcm_dev)
+{
+ int retval;
+ struct tcm_v2_message_header *header;
+ unsigned char *tmp_buf;
+ unsigned int tmp_buf_size;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+
+ /* read in the message header at first */
+ retval = syna_tcm_v2_read(tcm_dev,
+ 0,
+ &tmp_buf,
+ &tmp_buf_size);
+ if (retval < 0) {
+ LOGE("Fail to read message header from device\n");
+ return retval;
+ }
+
+ /* error out once the response packet is corrupted */
+ if (tcm_msg->status_report_code == STATUS_PACKET_CORRUPTED)
+ return 0;
+
+
+ /* allocate the required space = header + payload */
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ retval = syna_tcm_buf_alloc(&tcm_msg->in,
+ MESSAGE_HEADER_SIZE + tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to reallocate memory for internal buf.in\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return retval;
+ }
+
+ retval = syna_pal_mem_cpy(tcm_msg->in.buf,
+ tcm_msg->in.buf_size,
+ tmp_buf,
+ tmp_buf_size,
+ MESSAGE_HEADER_SIZE);
+ if (retval < 0) {
+ LOGE("Fail to copy data to internal buf_in\n");
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return retval;
+ }
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ /* read in payload, if any */
+ if (tcm_msg->payload_length > 0) {
+
+ retval = syna_tcm_v2_continued_read(tcm_dev,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to read in payload data, size: %d)\n",
+ tcm_msg->payload_length);
+ return retval;
+ }
+ }
+
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ header = (struct tcm_v2_message_header *)tcm_msg->in.buf;
+
+ tcm_msg->payload_length = syna_pal_le2_to_uint(header->length);
+ tcm_msg->status_report_code = header->code;
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_send_cmd()
+ *
+ * Forward the given command and payload to syna_tcm_v2_write().
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: command code
+ * [ in] payload: data payload if any
+ * [ in] payload_len: length of data payload if have any
+ * [ in] resend: flag for re-sending the packet
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static inline int syna_tcm_v2_send_cmd(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int length, bool resend)
+{
+ int retval;
+
+ retval = syna_tcm_v2_write(tcm_dev,
+ command,
+ payload,
+ length,
+ resend);
+ if (retval < 0)
+ LOGE("Fail to write Command 0x%02x to device\n", command);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_execute_cmd_request()
+ *
+ * Process the command message.
+ * The helper is responsible for sending the given command and its payload,
+ * to device. Once the total size of message is over the wr_chunk, divide
+ * into continued writes
+ *
+ * In addition, the response to the command generated by the device will be
+ * read in immediately.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: command code
+ * [ in] payload: data payload if any
+ * [ in] payload_length: length of payload in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_v2_execute_cmd_request(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_length)
+{
+ int retval;
+ unsigned int idx;
+ unsigned int offset;
+ unsigned int chunks;
+ unsigned int xfer_length;
+ unsigned int remaining_length;
+ int retry_cnt = 0;
+ unsigned int chunk_space;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ chunk_space = tcm_dev->max_wr_size;
+
+ remaining_length = payload_length;
+
+ /* available space for payload = total size - header - crc */
+ if (chunk_space == 0)
+ chunk_space = remaining_length;
+ else
+ chunk_space = chunk_space - (MESSAGE_HEADER_SIZE + 2);
+
+ chunks = syna_pal_ceil_div(remaining_length, chunk_space);
+
+ chunks = chunks == 0 ? 1 : chunks;
+
+ offset = 0;
+
+ /* process the command message and handle the response
+ * to the command
+ */
+ for (idx = 0; idx < chunks; idx++) {
+ if (remaining_length > chunk_space)
+ xfer_length = chunk_space;
+ else
+ xfer_length = remaining_length;
+
+retry:
+ /* send command to device */
+ command = (idx == 0) ? command : CMD_CONTINUE_WRITE;
+
+ LOGD("Command: 0x%02x\n", command);
+
+ retval = syna_tcm_v2_send_cmd(tcm_dev,
+ command,
+ &payload[offset],
+ xfer_length,
+ (retry_cnt > 0));
+ if (retval < 0)
+ goto exit;
+
+ /* bus turnaround delay */
+ syna_pal_sleep_us(TAT_DELAY_US_MIN, TAT_DELAY_US_MAX);
+
+ /* get the response to the command immediately */
+ retval = syna_tcm_v2_get_response(tcm_dev);
+ if (retval < 0) {
+ LOGE("Fail to get the response to command 0x%02x\n",
+ command);
+ goto exit;
+ }
+
+ /* check the response code */
+ tcm_msg->response_code = tcm_msg->status_report_code;
+
+ LOGD("Response code: 0x%x\n", tcm_msg->response_code);
+
+ if (tcm_msg->status_report_code >= REPORT_IDENTIFY)
+ goto next;
+
+ switch (tcm_msg->status_report_code) {
+ case STATUS_NO_REPORT_AVAILABLE:
+ case STATUS_OK:
+ case STATUS_ACK:
+ retry_cnt = 0;
+ break;
+ case STATUS_PACKET_CORRUPTED:
+ case STATUS_RETRY_REQUESTED:
+ retry_cnt += 1;
+ break;
+ default:
+ LOGE("Incorrect status code 0x%02x of command 0x%02x\n",
+ tcm_msg->status_report_code, command);
+ goto exit;
+ }
+
+ if (retry_cnt > 0) {
+ if (command == CMD_RESET) {
+ LOGE("Command CMD_RESET corrupted, exit\n");
+ /* assume ACK and wait for interrupt assertion
+ * once the response of reset is corrupted
+ */
+ tcm_msg->response_code = STATUS_ACK;
+ goto exit;
+ } else if (retry_cnt > COMMAND_RETRY_TIMES) {
+ LOGE("Command 0x%02x corrupted\n", command);
+ goto exit;
+ }
+
+ LOGN("Command 0x%02x, retry %d\n", command, retry_cnt);
+ syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX);
+
+ goto retry;
+ }
+next:
+ offset += xfer_length;
+
+ remaining_length -= xfer_length;
+
+ if (chunks > 1)
+ syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX);
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_read_message()
+ *
+ * Send a CMD_GET_REPORT to acquire a TouchCom v2 report packet from device.
+ * Meanwhile, the retrieved data will be stored in the internal buffer.resp
+ * or buffer.report.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] status_report_code: status code or report code received
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static int syna_tcm_v2_read_message(struct tcm_dev *tcm_dev,
+ unsigned char *status_report_code)
+{
+ int retval;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_mutex_t *rw_mutex = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ rw_mutex = &tcm_msg->rw_mutex;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ if (status_report_code)
+ *status_report_code = STATUS_INVALID;
+
+ syna_pal_mutex_lock(rw_mutex);
+
+ /* request a command */
+ retval = syna_tcm_v2_execute_cmd_request(tcm_dev,
+ CMD_TCM2_GET_REPORT,
+ NULL,
+ 0);
+ if (retval < 0) {
+ LOGE("Fail to send command CMD_TCM2_GET_REPORT\n");
+
+ if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) {
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR);
+ syna_pal_completion_complete(cmd_completion);
+ }
+ goto exit;
+ }
+
+ /* duplicate the data to external buffer */
+ syna_tcm_buf_lock(&tcm_dev->external_buf);
+ if (tcm_msg->payload_length > 0) {
+ retval = syna_tcm_buf_alloc(&tcm_dev->external_buf,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory, external_buf invalid\n");
+ syna_tcm_buf_unlock(&tcm_dev->external_buf);
+ goto exit;
+ } else {
+ retval = syna_pal_mem_cpy(&tcm_dev->external_buf.buf[0],
+ tcm_msg->payload_length,
+ &tcm_msg->in.buf[MESSAGE_HEADER_SIZE],
+ tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE,
+ tcm_msg->payload_length);
+ if (retval < 0) {
+ LOGE("Fail to copy data to external buffer\n");
+ syna_tcm_buf_unlock(&tcm_dev->external_buf);
+ goto exit;
+ }
+ }
+ }
+ tcm_dev->external_buf.data_length = tcm_msg->payload_length;
+ syna_tcm_buf_unlock(&tcm_dev->external_buf);
+
+ if (tcm_msg->response_code == STATUS_NO_REPORT_AVAILABLE)
+ goto exit;
+
+ /* process the retrieved packet */
+ if (tcm_msg->status_report_code >= REPORT_IDENTIFY)
+ syna_tcm_v2_dispatch_report(tcm_dev);
+ else
+ syna_tcm_v2_dispatch_response(tcm_dev);
+
+ /* copy the status report code to caller */
+ if (status_report_code)
+ *status_report_code = tcm_msg->status_report_code;
+
+exit:
+ syna_pal_mutex_unlock(rw_mutex);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_v2_write_message()
+ *
+ * Write message including command and its payload to TouchCom device.
+ * Then, the response of the command generated by the device will be
+ * read in and stored in internal buffer.resp.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: TouchComm command
+ * [ in] payload: data payload, if any
+ * [ in] payload_length: length of data payload, if any
+ * [out] resp_code: response code returned
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ *
+ * @return
+ * 0 or positive value on success; otherwise, on error.
+ */
+static int syna_tcm_v2_write_message(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_length, unsigned char *resp_code,
+ unsigned int delay_ms_resp)
+{
+ int retval;
+ int timeout = 0;
+ int polling_ms = 0;
+ struct tcm_message_data_blob *tcm_msg = NULL;
+ syna_pal_mutex_t *cmd_mutex = NULL;
+ syna_pal_mutex_t *rw_mutex = NULL;
+ syna_pal_completion_t *cmd_completion = NULL;
+ bool has_irq_ctrl = false;
+ bool in_polling = false;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_msg = &tcm_dev->msg_data;
+ cmd_mutex = &tcm_msg->cmd_mutex;
+ rw_mutex = &tcm_msg->rw_mutex;
+ cmd_completion = &tcm_msg->cmd_completion;
+
+ if (resp_code)
+ *resp_code = STATUS_INVALID;
+
+ /* indicate which mode is used */
+ in_polling = (delay_ms_resp != RESP_IN_ATTN);
+
+ /* irq control is enabled only when the operations is implemented
+ * and the current status of irq is enabled.
+ * do not enable irq if it is disabled by someone.
+ */
+ has_irq_ctrl = (bool)(tcm_dev->hw_if->ops_enable_irq != NULL);
+ has_irq_ctrl &= tcm_dev->hw_if->bdata_attn.irq_enabled;
+
+ /* disable irq when using polling mode */
+ if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq)
+ tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, false);
+
+ LOGD("write command: 0x%02x, payload size: %d\n",
+ command, payload_length);
+
+ syna_pal_mutex_lock(cmd_mutex);
+
+ syna_pal_mutex_lock(rw_mutex);
+
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY);
+
+ /* reset the command completion */
+ syna_pal_completion_reset(cmd_completion);
+
+ tcm_msg->command = command;
+
+ /* request a command execution */
+ retval = syna_tcm_v2_execute_cmd_request(tcm_dev,
+ command,
+ payload,
+ payload_length);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x to device\n", command);
+ goto exit;
+ }
+
+ syna_pal_mutex_unlock(rw_mutex);
+
+ /* waiting for the resp data only at STATUS_ACK */
+ if (tcm_msg->response_code != STATUS_ACK) {
+ syna_tcm_v2_dispatch_response(tcm_dev);
+
+ goto check_response;
+ }
+
+ /* handle the report generated by the command
+ *
+ * assuming to select the polling mode, the while-loop below will
+ * repeatedly read in the respose data based on the given polling
+ * time; otherwise, wait until receiving a completion event from
+ * interupt thread.
+ */
+ timeout = 0;
+ if (!in_polling)
+ polling_ms = CMD_RESPONSE_TIMEOUT_MS;
+ else
+ polling_ms = delay_ms_resp;
+
+ do {
+ /* wait for the completion event triggered by read_message */
+ retval = syna_pal_completion_wait_for(cmd_completion,
+ polling_ms);
+ /* reset the status when times out and keep in polling */
+ if (retval < 0)
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY);
+
+ /* break when geting a valid resp; otherwise, keep in polling */
+ if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE)
+ goto check_response;
+
+ if (in_polling) {
+ /* retrieve the message packet back */
+ retval = syna_tcm_v2_read_message(tcm_dev, NULL);
+ /* keep in polling if still not having a valid resp */
+ if (retval < 0)
+ syna_pal_completion_reset(cmd_completion);
+ }
+
+ timeout += polling_ms + 10;
+
+ } while (timeout < CMD_RESPONSE_TIMEOUT_MS);
+
+ /* check the status of response data
+ * according to the touchcomm spec, each command message
+ * should have an associated reponse message.
+ */
+check_response:
+ if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_IDLE) {
+ if (timeout >= CMD_RESPONSE_TIMEOUT_MS) {
+ LOGE("Timed out wait for response of command 0x%02x\n",
+ command);
+ retval = _ETIMEDOUT;
+ goto exit;
+ } else {
+ LOGE("Fail to get valid response of command 0x%02x\n",
+ command);
+ retval = _EIO;
+ goto exit;
+ }
+ }
+
+ /* copy response code to the caller */
+ if (resp_code)
+ *resp_code = tcm_msg->response_code;
+
+ if (tcm_msg->response_code != STATUS_OK) {
+ LOGE("Error code 0x%02x of command 0x%02x\n",
+ tcm_msg->response_code, tcm_msg->command);
+ retval = _EIO;
+ } else {
+ retval = 0;
+ }
+
+exit:
+ tcm_msg->command = CMD_NONE;
+
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+
+ syna_pal_mutex_unlock(cmd_mutex);
+
+ /* recovery the irq if using polling mode */
+ if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq)
+ tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, true);
+
+ return retval;
+}
+
+ /**
+ * syna_tcm_v2_detect()
+ *
+ * For TouchCom v2 protocol, the given data must have a valid crc-6 at the end.
+ * If so, send an identify command to identify the device.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: raw 4-byte data
+ * [ in] size: length of input data in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_v2_detect(struct tcm_dev *tcm_dev, unsigned char *data,
+ unsigned int size)
+{
+ int retval;
+ unsigned char resp_code = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!data) || (size < MESSAGE_HEADER_SIZE)) {
+ LOGE("Invalid parameters\n");
+ return _EINVAL;
+ }
+
+ if (syna_tcm_v2_crc6(data, BITS_IN_MESSAGE_HEADER) != 0)
+ return _ENODEV;
+
+ /* send an identify command to identify the device */
+ retval = syna_tcm_v2_write_message(tcm_dev,
+ CMD_IDENTIFY,
+ NULL,
+ 0,
+ &resp_code,
+ RESP_IN_POLLING);
+ if (retval < 0) {
+ LOGE("Fail to get identification info from device\n");
+ return retval;
+ }
+
+ /* expose the read / write operations */
+ tcm_dev->read_message = syna_tcm_v2_read_message;
+ tcm_dev->write_message = syna_tcm_v2_write_message;
+
+ return retval;
+}
diff --git a/tcm/synaptics_touchcom_func_base.c b/tcm/synaptics_touchcom_func_base.c
new file mode 100644
index 0000000..8e83c15
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_base.c
@@ -0,0 +1,1678 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_base.c
+ *
+ * This file implements generic and foundational functions supported in
+ * Synaptics TouchComm communication protocol.
+ *
+ * The declarations are in synaptics_touchcom_func_base.h.
+ */
+
+#include "synaptics_touchcom_func_base.h"
+#include "synaptics_touchcom_func_touch.h"
+
+
+/**
+ * syna_tcm_change_resp_read()
+ *
+ * Helper to change the default resp reading method, which was previously set
+ * when calling syna_tcm_allocate_device
+ *
+ * @param
+ * [in] tcm_dev: the device handle
+ * [in] request: resp reading method to change
+ * set '0' or 'RESP_IN_ATTN' for ATTN-driven; otherwise,
+ * assign a positive value standing for the polling time
+ * @return
+ * none.
+ */
+void syna_tcm_change_resp_read(struct tcm_dev *tcm_dev, unsigned int request)
+{
+ if (request == RESP_IN_ATTN) {
+ tcm_dev->msg_data.default_resp_reading = RESP_IN_ATTN;
+
+ LOGI("Change default resp reading method by attn\n");
+ } else {
+ if (request < RESP_IN_POLLING)
+ request = RESP_IN_POLLING;
+
+ tcm_dev->msg_data.default_resp_reading = request;
+
+ LOGI("Change default resp reading method by polling (%dms)\n",
+ tcm_dev->msg_data.default_resp_reading);
+ }
+}
+
+/**
+ * syna_tcm_init_message_wrap()
+ *
+ * Initialize internal buffers and related structures for command processing.
+ * The function must be called to prepare all essential structures for
+ * command wrapper.
+ *
+ * @param
+ * [in] tcm_msg: message wrapper structure
+ * [in] resp_reading: default method to retrieve the resp data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_init_message_wrap(struct tcm_message_data_blob *tcm_msg,
+ unsigned int resp_reading)
+{
+ /* initialize internal buffers */
+ syna_tcm_buf_init(&tcm_msg->in);
+ syna_tcm_buf_init(&tcm_msg->out);
+ syna_tcm_buf_init(&tcm_msg->temp);
+
+ /* allocate the completion event for command processing */
+ if (syna_pal_completion_alloc(&tcm_msg->cmd_completion) < 0) {
+ LOGE("Fail to allocate cmd completion event\n");
+ return _EINVAL;
+ }
+
+ /* allocate the cmd_mutex for command protection */
+ if (syna_pal_mutex_alloc(&tcm_msg->cmd_mutex) < 0) {
+ LOGE("Fail to allocate cmd_mutex\n");
+ return _EINVAL;
+ }
+
+ /* allocate the rw_mutex for rw protection */
+ if (syna_pal_mutex_alloc(&tcm_msg->rw_mutex) < 0) {
+ LOGE("Fail to allocate rw_mutex\n");
+ return _EINVAL;
+ }
+
+ /* set default state of command_status */
+ ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE);
+
+ /* allocate the internal buffer.in at first */
+ syna_tcm_buf_lock(&tcm_msg->in);
+
+ if (syna_tcm_buf_alloc(&tcm_msg->in, MESSAGE_HEADER_SIZE) < 0) {
+ LOGE("Fail to allocate memory for buf.in (size = %d)\n",
+ MESSAGE_HEADER_SIZE);
+ tcm_msg->in.buf_size = 0;
+ tcm_msg->in.data_length = 0;
+ syna_tcm_buf_unlock(&tcm_msg->in);
+ return _EINVAL;
+ }
+ tcm_msg->in.buf_size = MESSAGE_HEADER_SIZE;
+
+ syna_tcm_buf_unlock(&tcm_msg->in);
+
+ tcm_msg->default_resp_reading = resp_reading;
+
+ LOGI("Resp reading method (default): %s\n",
+ (resp_reading == RESP_IN_ATTN) ? "attn" : "polling");
+
+ return 0;
+}
+
+/**
+ * syna_tcm_del_message_wrap()
+ *
+ * Remove message wrapper interface and internal buffers.
+ * Call the function once the message wrapper is no longer needed.
+ *
+ * @param
+ * [in] tcm_msg: message wrapper structure
+ *
+ * @return
+ * none.
+ */
+static void syna_tcm_del_message_wrap(struct tcm_message_data_blob *tcm_msg)
+{
+ /* release the mutex */
+ syna_pal_mutex_free(&tcm_msg->rw_mutex);
+ syna_pal_mutex_free(&tcm_msg->cmd_mutex);
+
+ /* release the completion event */
+ syna_pal_completion_free(&tcm_msg->cmd_completion);
+
+ /* release internal buffers */
+ syna_tcm_buf_release(&tcm_msg->temp);
+ syna_tcm_buf_release(&tcm_msg->out);
+ syna_tcm_buf_release(&tcm_msg->in);
+}
+
+/**
+ * syna_tcm_allocate_device()
+ *
+ * Create the TouchCom core device handle.
+ * This function must be called in order to allocate the main device handle,
+ * structure syna_tcm_dev, which will be passed to all other operations and
+ * functions within the entire source code.
+ *
+ * Meanwhile, caller has to prepare specific syna_tcm_hw_interface structure,
+ * so that all the implemented functions can access hardware components
+ * through syna_tcm_hw_interface.
+ *
+ * @param
+ * [out] ptcm_dev_ptr: a pointer to the device handle returned
+ * [ in] hw_if: hardware-specific data on target platform
+ * [ in] resp_reading: default resp reading method
+ * set 'RESP_IN_ATTN' to apply ATTN-driven method;
+ * set 'RESP_IN_POLLING' to read in resp by polling
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_allocate_device(struct tcm_dev **ptcm_dev_ptr,
+ struct syna_hw_interface *hw_if, unsigned int resp_reading)
+{
+ int retval = 0;
+ struct tcm_dev *tcm_dev = NULL;
+
+ if (!hw_if) {
+ LOGE("Invalid parameter of hw_if\n");
+ return _EINVAL;
+ }
+
+ if ((!hw_if->ops_read_data) || (!hw_if->ops_write_data)) {
+ LOGE("Invalid hw read write operation\n");
+ return _EINVAL;
+ }
+
+ *ptcm_dev_ptr = NULL;
+
+ /* allocate the core device handle */
+ tcm_dev = (struct tcm_dev *)syna_pal_mem_alloc(
+ 1,
+ sizeof(struct tcm_dev));
+ if (!tcm_dev) {
+ LOGE("Fail to create tcm device handle\n");
+ return _ENOMEM;
+ }
+
+ /* link to the given hardware data */
+ tcm_dev->hw_if = hw_if;
+
+ tcm_dev->max_rd_size = hw_if->bdata_io.rd_chunk_size;
+ tcm_dev->max_wr_size = hw_if->bdata_io.wr_chunk_size;
+
+ tcm_dev->write_message = NULL;
+ tcm_dev->read_message = NULL;
+
+ /* allocate internal buffers */
+ syna_tcm_buf_init(&tcm_dev->report_buf);
+ syna_tcm_buf_init(&tcm_dev->resp_buf);
+ syna_tcm_buf_init(&tcm_dev->external_buf);
+ syna_tcm_buf_init(&tcm_dev->touch_config);
+
+ /* initialize the command wrapper interface */
+ retval = syna_tcm_init_message_wrap(&tcm_dev->msg_data,
+ resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to initialize command interface\n");
+ goto err_init_message_wrap;
+ }
+
+ /* return the created device handle */
+ *ptcm_dev_ptr = tcm_dev;
+
+ LOGI("TouchComm core module created, ver.: %d.%02d\n",
+ (unsigned char)(SYNA_TCM_CORE_LIB_VERSION >> 8),
+ (unsigned char)SYNA_TCM_CORE_LIB_VERSION & 0xff);
+
+ LOGI("Capability: wr_chunk(%d), rd_chunk(%d), irq_control(%s)\n",
+ tcm_dev->max_wr_size, tcm_dev->max_rd_size,
+ (hw_if->ops_enable_irq) ? "yes" : "no");
+
+ return 0;
+
+err_init_message_wrap:
+ syna_tcm_buf_release(&tcm_dev->touch_config);
+ syna_tcm_buf_release(&tcm_dev->external_buf);
+ syna_tcm_buf_release(&tcm_dev->report_buf);
+ syna_tcm_buf_release(&tcm_dev->resp_buf);
+
+ tcm_dev->hw_if = NULL;
+
+ syna_pal_mem_free((void *)tcm_dev);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_remove_device()
+ *
+ * Remove the TouchCom core device handler.
+ * This function must be invoked when the device is no longer needed.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+void syna_tcm_remove_device(struct tcm_dev *tcm_dev)
+{
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return;
+ }
+
+ /* release the command interface */
+ syna_tcm_del_message_wrap(&tcm_dev->msg_data);
+
+ /* release buffers */
+ syna_tcm_buf_release(&tcm_dev->touch_config);
+ syna_tcm_buf_release(&tcm_dev->external_buf);
+ syna_tcm_buf_release(&tcm_dev->report_buf);
+ syna_tcm_buf_release(&tcm_dev->resp_buf);
+
+ tcm_dev->hw_if = NULL;
+
+ /* release the device handle */
+ syna_pal_mem_free((void *)tcm_dev);
+
+ LOGI("tcm device handle removed\n");
+}
+
+/**
+ * syna_tcm_detect_protocol()
+ *
+ * Helper to distinguish which TouchCom firmware is running.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] data: raw data from device
+ * [ in] data_len: length of input data in bytes
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_detect_protocol(struct tcm_dev *tcm_dev,
+ unsigned char *data, unsigned int data_len)
+{
+ int retval;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = syna_tcm_v2_detect(tcm_dev, data, data_len);
+ if (retval < 0)
+ retval = syna_tcm_v1_detect(tcm_dev, data, data_len);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_detect_device()
+ *
+ * Determine the type of device being connected, and distinguish which
+ * version of TouchCom firmware running on the device.
+ * This function must be called before using this TouchComm core library.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, the current mode running on the device is returned;
+ * otherwise, negative value on error.
+ */
+int syna_tcm_detect_device(struct tcm_dev *tcm_dev)
+{
+ int retval = 0;
+ unsigned char data[4] = { 0 };
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_dev->dev_mode = MODE_UNKNOWN;
+
+ /* get the bare data from the bus directly */
+ data[0] = 0x07;
+ retval = syna_tcm_write(tcm_dev, &data[0], 1);
+ if (retval < 0) {
+ LOGE("Fail to write magic to bus\n");
+ return _EIO;
+ }
+
+ retval = syna_tcm_read(tcm_dev,
+ data, (unsigned int)sizeof(data));
+ if (retval < 0) {
+ LOGE("Fail to retrieve 4-byte data from bus\n");
+ return _EIO;
+ }
+
+ LOGD("bare data: %02x %02x %02x %02x\n",
+ data[0], data[1], data[2], data[3]);
+
+ /* distinguish which tcm version running on the device */
+ retval = syna_tcm_detect_protocol(tcm_dev,
+ data, (unsigned int)sizeof(data));
+ if (retval < 0) {
+ LOGE("Fail to detect TouchCom device, %02x %02x %02x %02x\n",
+ data[0], data[1], data[2], data[3]);
+ return retval;
+ }
+
+ if ((!tcm_dev->write_message) || (!tcm_dev->read_message)) {
+ LOGE("Invalid TouchCom rw operations\n");
+ return _ENODEV;
+ }
+
+ /* check the running mode */
+ switch (tcm_dev->dev_mode) {
+ case MODE_APPLICATION_FIRMWARE:
+ LOGI("Device in Application FW, build id: %d, %s\n",
+ tcm_dev->packrat_number,
+ tcm_dev->id_info.part_number);
+ break;
+ case MODE_BOOTLOADER:
+ case MODE_TDDI_BOOTLOADER:
+ LOGI("Device in Bootloader\n");
+ break;
+ case MODE_ROMBOOTLOADER:
+ LOGI("Device in ROMBoot uBL\n");
+ break;
+ case MODE_MULTICHIP_TDDI_BOOTLOADER:
+ LOGI("Device in multi-chip TDDI Bootloader\n");
+ break;
+ default:
+ LOGW("Found TouchCom device, but unsupported mode: 0x%02x\n",
+ tcm_dev->dev_mode);
+ break;
+ }
+
+ retval = tcm_dev->dev_mode;
+ return retval;
+}
+
+/**
+ * syna_tcm_get_event_data()
+ *
+ * Helper to read TouchComm messages when ATTN signal is asserted.
+ * After returning, the ATTN signal should be no longer asserted.
+ *
+ * The 'code' returned will guide the caller on the next action.
+ * For example, do touch reporting once returned code is equal to REPORT_TOUCH.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] code: received report code
+ * [out] data: a user buffer for data returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_event_data(struct tcm_dev *tcm_dev,
+ unsigned char *code, struct tcm_buffer *data)
+{
+ int retval = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!code) {
+ LOGE("Invalid parameter\n");
+ return _EINVAL;
+ }
+
+ /* retrieve the event data */
+ retval = tcm_dev->read_message(tcm_dev,
+ code);
+ if (retval < 0) {
+ LOGE("Fail to read messages\n");
+ return retval;
+ }
+
+ /* exit if no buffer provided */
+ if (!data)
+ goto exit;
+
+ /* if gathering a report, copy to the user buffer */
+ if ((*code >= REPORT_IDENTIFY) && (*code != STATUS_INVALID)) {
+ if (tcm_dev->report_buf.data_length == 0)
+ goto exit;
+
+ syna_tcm_buf_lock(&tcm_dev->report_buf);
+
+ retval = syna_tcm_buf_copy(data, &tcm_dev->report_buf);
+ if (retval < 0) {
+ LOGE("Fail to copy data, report type: %x\n", *code);
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+ goto exit;
+ }
+
+ syna_tcm_buf_unlock(&tcm_dev->report_buf);
+ }
+
+ /* if gathering a response, copy to the user buffer */
+ if ((*code > STATUS_IDLE) && (*code <= STATUS_ERROR)) {
+ if (tcm_dev->resp_buf.data_length == 0)
+ goto exit;
+
+ syna_tcm_buf_lock(&tcm_dev->resp_buf);
+
+ retval = syna_tcm_buf_copy(data, &tcm_dev->resp_buf);
+ if (retval < 0) {
+ LOGE("Fail to copy data, status code: %x\n", *code);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ goto exit;
+ }
+
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_identify()
+ *
+ * Implement the standard command code, which is used to request
+ * an IDENTIFY report packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] id_info: the identification info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_identify(struct tcm_dev *tcm_dev,
+ struct tcm_identification_info *id_info)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_IDENTIFY,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n", CMD_IDENTIFY);
+ goto exit;
+ }
+
+ tcm_dev->dev_mode = tcm_dev->id_info.mode;
+
+ if (id_info == NULL)
+ goto show_info;
+
+ /* copy identify info to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)id_info,
+ sizeof(struct tcm_identification_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ MIN(sizeof(*id_info), tcm_dev->resp_buf.data_length));
+ if (retval < 0) {
+ LOGE("Fail to copy identify info to caller\n");
+ goto exit;
+ }
+
+show_info:
+ LOGI("TCM Fw mode: 0x%02x, TCM ver.: %d\n",
+ tcm_dev->id_info.mode, tcm_dev->id_info.version);
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_reset()
+ *
+ * Implement the standard command code, which is used to perform a sw reset
+ * immediately. After a successful reset, an IDENTIFY report to indicate that
+ * device is ready.
+ *
+ * Caller shall be aware that the firmware will be reloaded after reset.
+ * Therefore, if expecting that a different firmware version is loaded, please
+ * do app firmware setup after reset.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_reset(struct tcm_dev *tcm_dev)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int board_setting;
+ unsigned int resp_handling;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ resp_handling = tcm_dev->msg_data.default_resp_reading;
+
+ /* gather the board settings of reset delay time */
+ board_setting = tcm_dev->hw_if->bdata_rst.reset_delay_ms;
+ if (board_setting == 0)
+ board_setting = RESET_DELAY_MS;
+
+ /* select the proper period to handle the resp of reset */
+ if (resp_handling != RESP_IN_ATTN) {
+ if (board_setting > resp_handling) {
+ resp_handling = board_setting;
+ LOGI("Use board settings %dms to poll resp of reset\n",
+ resp_handling);
+ }
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_RESET,
+ NULL,
+ 0,
+ &resp_code,
+ resp_handling);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n", CMD_RESET);
+ goto exit;
+ }
+
+ /* current device mode is expected to be updated
+ * because identification report will be received after reset
+ */
+ tcm_dev->dev_mode = tcm_dev->id_info.mode;
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGI("Device mode 0x%02X running after reset\n",
+ tcm_dev->dev_mode);
+ }
+
+ retval = 0;
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_enable_report()
+ *
+ * Implement the application fw command code to enable or disable a report.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] report_code: the requested report code being generated
+ * [ in] en: '1' for enabling; '0' for disabling
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_enable_report(struct tcm_dev *tcm_dev,
+ unsigned char report_code, bool en)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned char command;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ command = (en) ? CMD_ENABLE_REPORT : CMD_DISABLE_REPORT;
+
+ retval = tcm_dev->write_message(tcm_dev,
+ command,
+ &report_code,
+ 1,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x to %s 0x%02x report\n",
+ command, (en)?"enable":"disable", report_code);
+ goto exit;
+ }
+
+ if (resp_code != STATUS_OK) {
+ LOGE("Fail to %s 0x%02x report, resp_code:%x\n",
+ (en) ? "enable" : "disable", report_code, resp_code);
+ } else {
+ LOGD("Report 0x%x %s\n", report_code,
+ (en) ? "enabled" : "disabled");
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_run_rom_bootloader_fw()
+ *
+ * Requests that the rombootloader firmware be run.
+ * Once the rombootloader firmware has finished starting, an IDENTIFY report
+ * to indicate that it is in the new mode.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] fw_switch_delay: delay time for fw mode switching.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_run_rom_bootloader_fw(struct tcm_dev *tcm_dev,
+ unsigned int fw_switch_delay)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_REBOOT_TO_ROM_BOOTLOADER,
+ NULL,
+ 0,
+ &resp_code,
+ fw_switch_delay);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_REBOOT_TO_ROM_BOOTLOADER);
+ goto exit;
+ }
+
+ if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGE("Fail to enter rom bootloader, mode: %x\n",
+ tcm_dev->dev_mode);
+ retval = _ENODEV;
+ goto exit;
+ }
+
+ LOGI("ROM Bootloader (mode 0x%x) activated\n",
+ tcm_dev->dev_mode);
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_run_bootloader_fw()
+ *
+ * Requests that the bootloader firmware be run.
+ * Once the bootloader firmware has finished starting, an IDENTIFY report
+ * to indicate that it is in the new mode.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] fw_switch_delay: delay time for fw mode switching.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_run_bootloader_fw(struct tcm_dev *tcm_dev,
+ unsigned int fw_switch_delay)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_RUN_BOOTLOADER_FIRMWARE,
+ NULL,
+ 0,
+ &resp_code,
+ fw_switch_delay);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_RUN_BOOTLOADER_FIRMWARE);
+ goto exit;
+ }
+
+ if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGE("Fail to enter bootloader, mode: %x\n",
+ tcm_dev->dev_mode);
+ retval = _ENODEV;
+ goto exit;
+ }
+
+ LOGI("Bootloader Firmware (mode 0x%x) activated\n",
+ tcm_dev->dev_mode);
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_run_application_fw()
+ *
+ * Requests that the application firmware be run.
+ * Once the application firmware has finished starting, an IDENTIFY report
+ * to indicate that it is in the new mode.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] fw_switch_delay: delay time for fw mode switching.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_run_application_fw(struct tcm_dev *tcm_dev,
+ unsigned int fw_switch_delay)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_RUN_APPLICATION_FIRMWARE,
+ NULL,
+ 0,
+ &resp_code,
+ fw_switch_delay);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_RUN_APPLICATION_FIRMWARE);
+ goto exit;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGW("Fail to enter application fw, mode: %x\n",
+ tcm_dev->dev_mode);
+ retval = _ENODEV;
+ goto exit;
+ }
+
+ LOGI("Application Firmware (mode 0x%x) activated\n",
+ tcm_dev->dev_mode);
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_switch_fw_mode()
+ *
+ * Implement the command code to switch the firmware mode.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] mode: target firmware mode
+ * [ in] fw_switch_delay: delay time for fw mode switching.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_switch_fw_mode(struct tcm_dev *tcm_dev,
+ unsigned char mode, unsigned int fw_switch_delay)
+{
+ int retval = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ switch (mode) {
+ case MODE_APPLICATION_FIRMWARE:
+ retval = syna_tcm_run_application_fw(tcm_dev,
+ fw_switch_delay);
+ if (retval < 0) {
+ LOGE("Fail to switch to application mode\n");
+ goto exit;
+ }
+ break;
+ case MODE_BOOTLOADER:
+ case MODE_TDDI_BOOTLOADER:
+ case MODE_TDDI_HDL_BOOTLOADER:
+ case MODE_MULTICHIP_TDDI_BOOTLOADER:
+ retval = syna_tcm_run_bootloader_fw(tcm_dev,
+ fw_switch_delay);
+ if (retval < 0) {
+ LOGE("Fail to switch to bootloader mode\n");
+ goto exit;
+ }
+ break;
+ case MODE_ROMBOOTLOADER:
+ retval = syna_tcm_run_rom_bootloader_fw(tcm_dev,
+ fw_switch_delay);
+ if (retval < 0) {
+ LOGE("Fail to switch to rom bootloader mode\n");
+ goto exit;
+ }
+ break;
+ default:
+ LOGE("Invalid firmware mode requested\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_get_boot_info()
+ *
+ * Implement the bootloader command code, which is used to request a
+ * boot information packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] boot_info: the boot info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_boot_info(struct tcm_dev *tcm_dev,
+ struct tcm_boot_info *boot_info)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int resp_data_len = 0;
+ unsigned int copy_size;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_BOOT_INFO,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_GET_BOOT_INFO);
+ goto exit;
+ }
+
+ resp_data_len = tcm_dev->resp_buf.data_length;
+ copy_size = MIN(sizeof(struct tcm_boot_info), resp_data_len);
+
+ /* save the boot_info */
+ retval = syna_pal_mem_cpy((unsigned char *)&tcm_dev->boot_info,
+ sizeof(struct tcm_boot_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ copy_size);
+ if (retval < 0) {
+ LOGE("Fail to copy boot info\n");
+ goto exit;
+ }
+
+ if (boot_info == NULL)
+ goto exit;
+
+ /* copy boot_info to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)boot_info,
+ sizeof(struct tcm_boot_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ copy_size);
+ if (retval < 0) {
+ LOGE("Fail to copy boot info to caller\n");
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_get_app_info()
+ *
+ * Implement the application fw command code to request an application
+ * info packet from device.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] app_info: the application info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_app_info(struct tcm_dev *tcm_dev,
+ struct tcm_application_info *app_info)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int app_status;
+ unsigned int resp_data_len = 0;
+ unsigned int copy_size;
+ struct tcm_application_info *info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_APPLICATION_INFO,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_GET_APPLICATION_INFO);
+ goto exit;
+ }
+
+ resp_data_len = tcm_dev->resp_buf.data_length;
+ copy_size = MIN(sizeof(tcm_dev->app_info), resp_data_len);
+
+ info = &tcm_dev->app_info;
+
+ /* save the app_info */
+ retval = syna_pal_mem_cpy((unsigned char *)info,
+ sizeof(struct tcm_application_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ copy_size);
+ if (retval < 0) {
+ LOGE("Fail to copy application info\n");
+ goto exit;
+ }
+
+ if (app_info == NULL)
+ goto show_info;
+
+ /* copy app_info to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)app_info,
+ sizeof(struct tcm_application_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ copy_size);
+ if (retval < 0) {
+ LOGE("Fail to copy application info to caller\n");
+ goto exit;
+ }
+
+show_info:
+ app_status = syna_pal_le2_to_uint(tcm_dev->app_info.status);
+
+ if (app_status == APP_STATUS_BAD_APP_CONFIG) {
+ LOGE("Bad application firmware, status: 0x%x\n", app_status);
+ retval = _ENODEV;
+ goto exit;
+ } else if (app_status != APP_STATUS_OK) {
+ LOGE("Incorrect application status, 0x%x\n", app_status);
+ retval = _ENODEV;
+ goto exit;
+ }
+
+ tcm_dev->max_objects = syna_pal_le2_to_uint(info->max_objects);
+ tcm_dev->max_x = syna_pal_le2_to_uint(info->max_x);
+ tcm_dev->max_y = syna_pal_le2_to_uint(info->max_y);
+
+ tcm_dev->cols = syna_pal_le2_to_uint(info->num_of_image_cols);
+ tcm_dev->rows = syna_pal_le2_to_uint(info->num_of_image_rows);
+ syna_pal_mem_cpy((unsigned char *)tcm_dev->config_id,
+ MAX_SIZE_CONFIG_ID,
+ info->customer_config_id,
+ MAX_SIZE_CONFIG_ID,
+ MAX_SIZE_CONFIG_ID);
+
+ LOGD("App info version: %d, status: %d\n",
+ syna_pal_le2_to_uint(info->version), app_status);
+ LOGD("App info: max_objs: %d, max_x:%d, max_y: %d, img: %dx%d\n",
+ tcm_dev->max_objects, tcm_dev->max_x, tcm_dev->max_y,
+ tcm_dev->rows, tcm_dev->cols);
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_get_static_config()
+ *
+ * Implement the application fw command code to retrieve the contents of
+ * the static configuration.
+ *
+ * The size of static configuration is available in app info packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] buf: buffer stored the static configuration
+ * [ in] buf_size: the size of given buffer
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_static_config(struct tcm_dev *tcm_dev,
+ unsigned char *buf, unsigned int buf_size)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int size;
+ struct tcm_application_info *app_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ app_info = &tcm_dev->app_info;
+
+ size = syna_pal_le2_to_uint(app_info->static_config_size);
+
+ if (size > buf_size) {
+ LOGE("Invalid buffer input, given size: %d (actual: %d)\n",
+ buf_size, size);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_STATIC_CONFIG,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_GET_STATIC_CONFIG);
+ goto exit;
+ }
+
+ if (buf == NULL)
+ goto exit;
+
+ /* copy app_info to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)buf,
+ buf_size,
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ tcm_dev->resp_buf.data_length);
+ if (retval < 0) {
+ LOGE("Fail to copy static config data to caller\n");
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_set_static_config()
+ *
+ * Implement the application fw command code to set the contents of
+ * the static configuration. When the write is completed, the device will
+ * restart touch sensing with the new settings.
+ *
+ * The size of static configuration is available in app info packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] config_data: the data of static configuration
+ * [ in] config_data_size: the size of given data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_static_config(struct tcm_dev *tcm_dev,
+ unsigned char *config_data, unsigned int config_data_size)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int size;
+ struct tcm_application_info *app_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ app_info = &tcm_dev->app_info;
+
+ size = syna_pal_le2_to_uint(app_info->static_config_size);
+
+ if (size != config_data_size) {
+ LOGE("Invalid static config size, given: %d (actual: %d)\n",
+ config_data_size, size);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_SET_STATIC_CONFIG,
+ config_data,
+ config_data_size,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_SET_STATIC_CONFIG);
+ goto exit;
+ }
+
+ retval = 0;
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_get_dynamic_config()
+ *
+ * Implement the application fw command code to get the value from the a single
+ * field of the dynamic configuration.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] id: target field id
+ * [out] value: the value returned
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_dynamic_config(struct tcm_dev *tcm_dev,
+ unsigned char id, unsigned short *value,
+ unsigned int delay_ms_resp)
+{
+ int retval = 0;
+ unsigned char out;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ out = (unsigned char)id;
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_DYNAMIC_CONFIG,
+ &out,
+ sizeof(out),
+ &resp_code,
+ delay_ms_resp);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x to get dynamic field 0x%x\n",
+ CMD_GET_DYNAMIC_CONFIG, (unsigned char)id);
+ goto exit;
+ }
+
+ /* return dynamic config data */
+ if (tcm_dev->resp_buf.data_length < 2) {
+ LOGE("Invalid resp data size, %d\n",
+ tcm_dev->resp_buf.data_length);
+ goto exit;
+ }
+
+ *value = (unsigned short)syna_pal_le2_to_uint(tcm_dev->resp_buf.buf);
+
+ LOGD("Get %d from dynamic field 0x%x\n", *value, id);
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_set_dynamic_config()
+ *
+ * Implement the application fw command code to set the specified value to
+ * the selected field of the dynamic configuration.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] id: target field id
+ * [ in] value: the value to the selected field
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_dynamic_config(struct tcm_dev *tcm_dev,
+ unsigned char id, unsigned short value,
+ unsigned int delay_ms_resp)
+{
+ int retval = 0;
+ unsigned char out[3];
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ LOGD("Set %d to dynamic field 0x%x\n", value, id);
+
+ out[0] = (unsigned char)id;
+ out[1] = (unsigned char)value;
+ out[2] = (unsigned char)(value >> 8);
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_SET_DYNAMIC_CONFIG,
+ out,
+ sizeof(out),
+ &resp_code,
+ delay_ms_resp);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x to set %d to field 0x%x\n",
+ CMD_SET_DYNAMIC_CONFIG, value, (unsigned char)id);
+ goto exit;
+ }
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_rezero()
+ *
+ * Implement the application fw command code to force the device to rezero its
+ * baseline estimate.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_rezero(struct tcm_dev *tcm_dev)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_REZERO,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_REZERO);
+ goto exit;
+ }
+
+ retval = 0;
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_set_config_id()
+ *
+ * Implement the application fw command code to set the 16-byte config id,
+ * which can be read in the app info packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] config_id: config id to be set
+ * [ in] size: size of input data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_config_id(struct tcm_dev *tcm_dev,
+ unsigned char *config_id, unsigned int size)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int config_id_len = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ config_id_len = sizeof(tcm_dev->app_info.customer_config_id);
+
+ if (size != config_id_len) {
+ LOGE("Invalid config id input, given size: %d (%d)\n",
+ size, config_id_len);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_SET_CONFIG_ID,
+ config_id,
+ size,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_SET_CONFIG_ID);
+ goto exit;
+ }
+
+ retval = 0;
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_sleep()
+ *
+ * Implement the application fw command code to put the device into low power
+ * deep sleep mode or set to normal active mode.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] en: '1' to low power deep sleep mode; '0' to active mode
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_sleep(struct tcm_dev *tcm_dev, bool en)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned char command;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ command = (en) ? CMD_ENTER_DEEP_SLEEP : CMD_EXIT_DEEP_SLEEP;
+
+ retval = tcm_dev->write_message(tcm_dev,
+ command,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%x\n", command);
+ goto exit;
+ }
+
+ retval = 0;
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_get_features()
+ *
+ * Implement the application fw command code to query the supported features.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] info: the features description packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_features(struct tcm_dev *tcm_dev,
+ struct tcm_features_info *info)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_FEATURES,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_GET_FEATURES);
+ goto exit;
+ }
+
+ if (info == NULL)
+ goto exit;
+
+ /* copy features_info to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)info,
+ sizeof(struct tcm_features_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ MIN(sizeof(*info), tcm_dev->resp_buf.data_length));
+ if (retval < 0) {
+ LOGE("Fail to copy features_info to caller\n");
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_run_production_test()
+ *
+ * Implement the appplication fw command code to request the device to run
+ * the production test.
+ *
+ * Production tests are listed at enum test_code (PID$).
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] test_item: the requested testing item
+ * [out] tdata: testing data returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_run_production_test(struct tcm_dev *tcm_dev,
+ unsigned char test_item, struct tcm_buffer *tdata)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned char test_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in application fw mode, mode: %x\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ test_code = (unsigned char)test_item;
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_PRODUCTION_TEST,
+ &test_code,
+ 1,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_PRODUCTION_TEST);
+ goto exit;
+ }
+
+ if (tdata == NULL)
+ goto exit;
+
+ /* copy testing data to caller */
+ retval = syna_tcm_buf_copy(tdata, &tcm_dev->resp_buf);
+ if (retval < 0) {
+ LOGE("Fail to copy testing data\n");
+ goto exit;
+ }
+exit:
+ return retval;
+}
+/**
+ * syna_tcm_send_command()
+ *
+ * Helper to forward the custom commnd to the device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: TouchComm command
+ * [ in] payload: data payload, if any
+ * [ in] payload_length: length of data payload, if any
+ * [out] resp_code: response code returned
+ * [out] resp: buffer to store the response data
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_send_command(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_length, unsigned char *resp_code,
+ struct tcm_buffer *resp, unsigned int delay_ms_resp)
+{
+ int retval = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!resp_code) {
+ LOGE("Invalid parameter\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ command,
+ payload,
+ payload_length,
+ resp_code,
+ delay_ms_resp);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ command);
+ goto exit;
+ }
+
+ /* response data returned */
+ if ((resp != NULL) && (tcm_dev->resp_buf.data_length > 0)) {
+ retval = syna_tcm_buf_copy(resp, &tcm_dev->resp_buf);
+ if (retval < 0) {
+ LOGE("Fail to copy resp data\n");
+ goto exit;
+ }
+ }
+
+exit:
+ return retval;
+}
+
diff --git a/tcm/synaptics_touchcom_func_base.h b/tcm/synaptics_touchcom_func_base.h
new file mode 100644
index 0000000..d0a155b
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_base.h
@@ -0,0 +1,418 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_base.h
+ *
+ * This file declares generic and foundational APIs being used to communicate
+ * with Synaptics touch controller through TouchComm communication protocol.
+ */
+
+#ifndef _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_
+#define _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_
+
+#include "synaptics_touchcom_core_dev.h"
+
+/**
+ * syna_tcm_allocate_device()
+ *
+ * Create the TouchCom core device handle.
+ * This function must be called in order to allocate the main device handle,
+ * structure syna_tcm_dev, which will be passed to all other operations and
+ * functions within the entire source code.
+ *
+ * Meanwhile, caller has to prepare specific syna_tcm_hw_interface structure,
+ * so that all the implemented functions can access hardware components
+ * through syna_tcm_hw_interface.
+ *
+ * @param
+ * [out] ptcm_dev_ptr: a pointer to the device handle returned
+ * [ in] hw_if: hardware-specific data on target platform
+ * [ in] resp_reading: default resp reading method
+ * set 'RESP_IN_ATTN' to apply ATTN-driven method;
+ * set 'RESP_IN_POLLING' to read in resp by polling
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_allocate_device(struct tcm_dev **ptcm_dev_ptr,
+ struct syna_hw_interface *hw_if, unsigned int resp_reading);
+
+/**
+ * syna_tcm_remove_device()
+ *
+ * Remove the TouchCom core device handle.
+ * This function must be invoked when the device is no longer needed.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * none.
+ */
+void syna_tcm_remove_device(struct tcm_dev *tcm_dev);
+
+/**
+ * syna_tcm_detect_device()
+ *
+ * Determine the type of device being connected and distinguish which
+ * version of TouchCom firmware running on the device.
+ * This function should be called before using this TouchComm core library.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, the current mode running on the device is returned;
+ * otherwise, negative value on error.
+ */
+int syna_tcm_detect_device(struct tcm_dev *tcm_dev);
+
+/**
+ * syna_tcm_get_event_data()
+ *
+ * Helper to read TouchComm messages when ATTN signal is asserted.
+ * After returning, the ATTN signal should be no longer asserted.
+ *
+ * The 'code' returned will guide the caller on the next action.
+ * For example, do touch reporting once returned code is equal to REPORT_TOUCH.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] code: received report code
+ * [out] report: report data returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_event_data(struct tcm_dev *tcm_dev,
+ unsigned char *code,
+ struct tcm_buffer *report);
+
+/**
+ * syna_tcm_change_resp_read()
+ *
+ * Helper to change the default resp reading method, which was previously set
+ * when calling syna_tcm_allocate_device
+ *
+ * @param
+ * [in] tcm_dev: the device handle
+ * [in] request: resp reading method to change
+ * set '0' or 'RESP_IN_ATTN' for ATTN-driven; otherwise,
+ * assign a positive value standing for the polling time
+ * @return
+ * none.
+ */
+void syna_tcm_change_resp_read(struct tcm_dev *tcm_dev, unsigned int request);
+
+/**
+ * syna_tcm_identify()
+ *
+ * Implement the standard command code, which is used to request
+ * an IDENTIFY report packet
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] id_info: the identification info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_identify(struct tcm_dev *tcm_dev,
+ struct tcm_identification_info *id_info);
+
+/**
+ * syna_tcm_reset()
+ *
+ * Implement the standard command code, which is used to perform a sw reset
+ * immediately. After a successful reset, an IDENTIFY report to indicate that
+ * device is ready.
+ *
+ * Caller shall be aware that the firmware will be reloaded after reset.
+ * Therefore, if expecting that a different firmware version is loaded, please
+ * do app firmware setup after reset.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_reset(struct tcm_dev *tcm_dev);
+
+/**
+ * syna_tcm_enable_report()
+ *
+ * Implement the application fw command code to enable or disable a report.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] report_code: the requested report code being generated
+ * [ in] en: '1' for enabling; '0' for disabling
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_enable_report(struct tcm_dev *tcm_dev,
+ unsigned char report_code, bool en);
+
+/**
+ * syna_tcm_switch_fw_mode()
+ *
+ * Implement the command code to switch the firmware mode.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] mode: target firmware mode
+ * [ in] fw_switch_delay: delay time for fw mode switching.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_switch_fw_mode(struct tcm_dev *tcm_dev,
+ unsigned char mode, unsigned int fw_switch_delay);
+
+/**
+ * syna_tcm_get_boot_info()
+ *
+ * Implement the bootloader command code, which is used to request a
+ * boot information packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] boot_info: the boot info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_boot_info(struct tcm_dev *tcm_dev,
+ struct tcm_boot_info *boot_info);
+
+/**
+ * syna_tcm_get_app_info()
+ *
+ * Implement the application fw command code to request an application
+ * info packet from device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] app_info: the application info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_app_info(struct tcm_dev *tcm_dev,
+ struct tcm_application_info *app_info);
+
+/**
+ * syna_tcm_get_static_config()
+ *
+ * Implement the application fw command code to retrieve the contents of
+ * the static configuration.
+ *
+ * The size of static configuration is available in app info packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] buf: buffer stored the static configuration
+ * [ in] buf_size: the size of given buffer
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_static_config(struct tcm_dev *tcm_dev,
+ unsigned char *buf, unsigned int buf_size);
+
+/**
+ * syna_tcm_set_static_config()
+ *
+ * Implement the application fw command code to set the contents of
+ * the static configuration. When the write is completed, the device will
+ * restart touch sensing with the new settings
+ *
+ * The size of static configuration is available in app info packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] config_data: the data of static configuration
+ * [ in] config_data_size: the size of given data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_static_config(struct tcm_dev *tcm_dev,
+ unsigned char *config_data, unsigned int config_data_size);
+
+/**
+ * syna_tcm_get_dynamic_config()
+ *
+ * Implement the application fw command code to get the value from the a single
+ * field of the dynamic configuration
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] id: target field id
+ * [out] value: the value returned
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_dynamic_config(struct tcm_dev *tcm_dev,
+ unsigned char id, unsigned short *value,
+ unsigned int delay_ms_resp);
+
+/**
+ * syna_tcm_set_dynamic_config()
+ *
+ * Implement the application fw command code to set the specified value to
+ * the selected field of the dynamic configuration
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] id: target field id
+ * [ in] value: the value to the selected field
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_dynamic_config(struct tcm_dev *tcm_dev,
+ unsigned char id, unsigned short value,
+ unsigned int delay_ms_resp);
+
+/**
+ * syna_tcm_rezero()
+ *
+ * Implement the application fw command code to force the device to rezero its
+ * baseline estimate.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_rezero(struct tcm_dev *tcm_dev);
+
+/**
+ * syna_tcm_set_config_id()
+ *
+ * Implement the application fw command code to set the 16-byte config id,
+ * which can be read in the app info packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] config_id: config id to be set
+ * [ in] size: size of input data
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_config_id(struct tcm_dev *tcm_dev,
+ unsigned char *config_id, unsigned int size);
+
+/**
+ * syna_tcm_sleep()
+ *
+ * Implement the application fw command code to put the device into low power
+ * deep sleep mode or set to normal active mode
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] en: '1' to low power deep sleep mode; '0' to active mode
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_sleep(struct tcm_dev *tcm_dev, bool en);
+
+/**
+ * syna_tcm_get_features()
+ *
+ * Implement the application fw command code to query the supported features.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] info: the features description packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_features(struct tcm_dev *tcm_dev,
+ struct tcm_features_info *info);
+
+/**
+ * syna_tcm_run_production_test()
+ *
+ * Implement the appplication fw command code to request the device to run
+ * the production test.
+ *
+ * Production tests are listed at enum test_code (PID$).
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] test_item: the requested testing item
+ * [out] tdata: testing data returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_run_production_test(struct tcm_dev *tcm_dev,
+ unsigned char test_item, struct tcm_buffer *tdata);
+
+/**
+ * syna_tcm_send_command()
+ *
+ * Helper to forward the custom commnd to the device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: TouchComm command
+ * [ in] payload: data payload, if any
+ * [ in] payload_length: length of data payload, if any
+ * [out] resp_code: response code returned
+ * [out] resp: buffer to store the response data
+ * [ in] delay_ms_resp: delay time for response reading.
+ * a positive value presents the time for polling;
+ * or, set '0' or 'RESP_IN_ATTN' for ATTN driven
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_send_command(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_length, unsigned char *resp_code,
+ struct tcm_buffer *resp, unsigned int delay_ms_resp);
+
+
+#endif /* end of _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ */
diff --git a/tcm/synaptics_touchcom_func_base_flash.h b/tcm/synaptics_touchcom_func_base_flash.h
new file mode 100644
index 0000000..87d0a4e
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_base_flash.h
@@ -0,0 +1,569 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_base_flash.h
+ *
+ * This file declares the common functions and structures being used in relevant
+ * functions of fw update.
+ */
+
+#ifndef _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_
+#define _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_
+
+#include "synaptics_touchcom_core_dev.h"
+
+/**
+ * @section: Some specific definition in the firmware file
+ */
+#define ID_STRING_SIZE (32)
+
+#define SIZE_WORDS (8)
+
+#define IHEX_RECORD_SIZE (14)
+
+#define IHEX_MAX_BLOCKS (64)
+
+#define IMAGE_FILE_MAGIC_VALUE (0x4818472b)
+
+#define FLASH_AREA_MAGIC_VALUE (0x7c05e516)
+
+
+/**
+ * @section: Helper macros for firmware file parsing
+ */
+#define CRC32(data, length) \
+ (syna_pal_crc32(~0, data, length) ^ ~0)
+
+#define VALUE(value) \
+ (syna_pal_le2_to_uint(value))
+
+#define AREA_ID_STR(area) \
+ (syna_tcm_get_flash_area_string(area))
+
+
+/**
+ * @section: Area Partitions in firmware
+ */
+enum flash_area {
+ AREA_NONE = 0,
+ /* please add the declarations below */
+
+ AREA_BOOT_CODE,
+ AREA_BOOT_CONFIG,
+ AREA_APP_CODE,
+ AREA_APP_CODE_COPRO,
+ AREA_APP_CONFIG,
+ AREA_PROD_TEST,
+ AREA_DISP_CONFIG,
+ AREA_F35_APP_CODE,
+ AREA_FORCE_TUNING,
+ AREA_GAMMA_TUNING,
+ AREA_TEMPERATURE_GAMM_TUNING,
+ AREA_CUSTOM_LCM,
+ AREA_LOOKUP,
+ AREA_CUSTOM_OEM,
+ AREA_OPEN_SHORT_TUNING,
+ AREA_CUSTOM_OTP,
+ AREA_PPDT,
+ AREA_ROMBOOT_APP_CODE,
+ AREA_TOOL_BOOT_CONFIG,
+
+ /* please add the declarations above */
+ AREA_MAX,
+};
+/**
+ * @section: String of Area Partitions in firmware
+ */
+static char *flash_area_str[] = {
+ NULL,
+ /* please add the declarations below */
+
+ "BOOT_CODE", /* AREA_BOOT_CODE */
+ "BOOT_CONFIG", /* AREA_BOOT_CONFIG */
+ "APP_CODE", /* AREA_APP_CODE */
+ "APP_CODE_COPRO", /* AREA_APP_CODE_COPRO */
+ "APP_CONFIG", /* AREA_APP_CONFIG */
+ "APP_PROD_TEST", /* AREA_PROD_TEST */
+ "DISPLAY", /* AREA_DISP_CONFIG */
+ "F35_APP_CODE", /* AREA_F35_APP_CODE */
+ "FORCE", /* AREA_FORCE_TUNING */
+ "GAMMA", /* AREA_GAMMA_TUNING */
+ "TEMPERATURE_GAMM",/* AREA_TEMPERATURE_GAMM_TUNING */
+ "LCM", /* AREA_CUSTOM_LCM */
+ "LOOKUP", /* AREA_LOOKUP */
+ "OEM", /* AREA_CUSTOM_OEM */
+ "OPEN_SHORT", /* AREA_OPEN_SHORT_TUNING */
+ "OTP", /* AREA_CUSTOM_OTP */
+ "PPDT", /* AREA_PPDT */
+ "ROMBOOT_APP_CODE",/* AREA_ROMBOOT_APP_CODE */
+ "TOOL_BOOT_CONFIG",/* AREA_TOOL_BOOT_CONFIG */
+
+ /* please add the declarations above */
+ NULL
+};
+/**
+ * @section: Header Content of app config defined
+ * in firmware file
+ */
+struct app_config_header {
+ unsigned short magic_value[4];
+ unsigned char checksum[4];
+ unsigned char length[2];
+ unsigned char build_id[4];
+ unsigned char customer_config_id[16];
+};
+/**
+ * @section: The Partition Descriptor defined
+ * in firmware file
+ */
+struct area_descriptor {
+ unsigned char magic_value[4];
+ unsigned char id_string[16];
+ unsigned char flags[4];
+ unsigned char flash_addr_words[4];
+ unsigned char length[4];
+ unsigned char checksum[4];
+};
+/**
+ * @section: Structure for the Data Block defined
+ * in firmware file
+ */
+struct block_data {
+ bool available;
+ const unsigned char *data;
+ unsigned int size;
+ unsigned int flash_addr;
+ unsigned char id;
+};
+/**
+ * @section: Structure for the Parsed Image File
+ */
+struct image_info {
+ struct block_data data[AREA_MAX];
+};
+/**
+ * @section: Header of Image File
+ *
+ * Define the header of firmware image file
+ */
+struct image_header {
+ unsigned char magic_value[4];
+ unsigned char num_of_areas[4];
+};
+/**
+ * @section: Structure for the Parsed iHex File
+ */
+struct ihex_info {
+ unsigned int records;
+ unsigned char *bin;
+ unsigned int bin_size;
+ struct block_data block[IHEX_MAX_BLOCKS];
+};
+
+/**
+ * syna_tcm_get_flash_area_string()
+ *
+ * Return the string ID of target area in the flash memory
+ *
+ * @param
+ * [ in] area: target flash area
+ *
+ * @return
+ * the string ID
+ */
+static inline char *syna_tcm_get_flash_area_string(enum flash_area area)
+{
+ if (area < AREA_MAX)
+ return (char *)flash_area_str[area];
+ else
+ return "";
+}
+
+/**
+ * syna_tcm_save_flash_block_data()
+ *
+ * Save the block data of flash memory to the corresponding structure.
+ *
+ * @param
+ * [out] image_info: image info used for storing the block data
+ * [ in] area: target area
+ * [ in] content: content of data
+ * [ in] flash_addr: offset of block data
+ * [ in] size: size of block data
+ * [ in] checksum: checksum of block data
+ *
+ * @return
+ * on success, return 0; otherwise, negative value on error.
+ */
+static int syna_tcm_save_flash_block_data(struct image_info *image_info,
+ enum flash_area area, const unsigned char *content,
+ unsigned int offset, unsigned int size, unsigned int checksum)
+{
+ if (!image_info) {
+ LOGE("Invalid image_info\n");
+ return _EINVAL;
+ }
+
+ if (area >= AREA_MAX) {
+ LOGE("Invalid flash area\n");
+ return _EINVAL;
+ }
+
+ if (checksum != CRC32((const char *)content, size)) {
+ LOGE("%s checksum error, in image: 0x%x (0x%x)\n",
+ AREA_ID_STR(area), checksum,
+ CRC32((const char *)content, size));
+ return _EINVAL;
+ }
+ image_info->data[area].size = size;
+ image_info->data[area].data = content;
+ image_info->data[area].flash_addr = offset;
+ image_info->data[area].id = (unsigned char)area;
+ image_info->data[area].available = true;
+
+ LOGI("%s area - address:0x%08x (%d), size:%d\n",
+ AREA_ID_STR(area), offset, offset, size);
+
+ return 0;
+}
+
+/**
+ * syna_tcm_get_flash_area_id()
+ *
+ * Return the corresponding ID of flash area based on the given string
+ *
+ * @param
+ * [ in] str: string to look for
+ *
+ *
+ * @return
+ * if matching, return the corresponding ID; otherwise, return AREA_MAX.
+ */
+static enum flash_area syna_tcm_get_flash_area_id(char *str)
+{
+ int area;
+ char *target;
+ unsigned int len;
+
+ for (area = AREA_MAX - 1; area >= 0; area--) {
+ target = AREA_ID_STR(area);
+ len = syna_pal_str_len(target);
+
+ if (syna_pal_str_cmp(str, target, len) == 0)
+ return area;
+ }
+
+ LOGW("Un-defined area string, %s\n", str);
+ return AREA_MAX;
+}
+
+/**
+ * syna_tcm_parse_fw_image()
+ *
+ * Parse and analyze the information of each areas from the given
+ * firmware image.
+ *
+ * @param
+ * [ in] image: image file given
+ * [ in] image_info: data blob stored the parsed data from an image file
+ *
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static inline int syna_tcm_parse_fw_image(const unsigned char *image,
+ struct image_info *image_info)
+{
+ int retval = 0;
+ unsigned int idx;
+ unsigned int addr;
+ unsigned int offset;
+ unsigned int length;
+ unsigned int checksum;
+ unsigned int flash_addr;
+ unsigned int magic_value;
+ unsigned int num_of_areas;
+ struct image_header *header;
+ struct area_descriptor *descriptor;
+ const unsigned char *content;
+ enum flash_area target_area;
+
+ if (!image) {
+ LOGE("No image data\n");
+ return _EINVAL;
+ }
+
+ if (!image_info) {
+ LOGE("Invalid image_info blob\n");
+ return _EINVAL;
+ }
+
+ syna_pal_mem_set(image_info, 0x00, sizeof(struct image_info));
+
+ header = (struct image_header *)image;
+
+ magic_value = syna_pal_le4_to_uint(header->magic_value);
+ if (magic_value != IMAGE_FILE_MAGIC_VALUE) {
+ LOGE("Invalid image file magic value\n");
+ return _EINVAL;
+ }
+
+ offset = sizeof(struct image_header);
+ num_of_areas = syna_pal_le4_to_uint(header->num_of_areas);
+
+ for (idx = 0; idx < num_of_areas; idx++) {
+ addr = syna_pal_le4_to_uint(image + offset);
+ descriptor = (struct area_descriptor *)(image + addr);
+ offset += 4;
+
+ magic_value = syna_pal_le4_to_uint(descriptor->magic_value);
+ if (magic_value != FLASH_AREA_MAGIC_VALUE)
+ continue;
+
+ length = syna_pal_le4_to_uint(descriptor->length);
+ content = (unsigned char *)descriptor + sizeof(*descriptor);
+ flash_addr = syna_pal_le4_to_uint(descriptor->flash_addr_words);
+ flash_addr = flash_addr * 2;
+ checksum = syna_pal_le4_to_uint(descriptor->checksum);
+
+ target_area = syna_tcm_get_flash_area_id(
+ (char *)descriptor->id_string);
+
+ retval = syna_tcm_save_flash_block_data(image_info,
+ target_area,
+ content,
+ flash_addr,
+ length,
+ checksum);
+ if (retval < 0)
+ return _EINVAL;
+ }
+
+ return 0;
+}
+
+
+/**
+ * syna_tcm_parse_ihex_line()
+ *
+ * Parse a line in the ihex file and convert into an actual data
+ *
+ * @param
+ * [ in] line: a line of string stored in the ihex file
+ * [out] count: size of actual data
+ * [out] addr: address of data located
+ * [out] type: the type of data belonging
+ * [out] buf: a buffer to store the converted data
+ * [int] buf_size: size of buffer
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static inline int syna_tcm_parse_ihex_line(char *line, unsigned int *count,
+ unsigned int *addr, unsigned int *type, unsigned char *buf,
+ unsigned int buf_size)
+{
+ const int OFFSET_COUNT = 1;
+ const int SIZE_COUNT = 2;
+ const int OFFSET_ADDR = OFFSET_COUNT + SIZE_COUNT;
+ const int SIZE_ADDR = 4;
+ const int OFFSET_TYPE = OFFSET_ADDR + SIZE_ADDR;
+ const int SIZE_TYPE = 2;
+ const int OFFSET_DATA = OFFSET_TYPE + SIZE_TYPE;
+ const int SIZE_DATA = 2;
+ unsigned int pos;
+
+ if (!line) {
+ LOGE("No string line\n");
+ return _EINVAL;
+ }
+
+ if ((!buf) || (buf_size == 0)) {
+ LOGE("Invalid temporary data buffer\n");
+ return _EINVAL;
+ }
+
+ *count = syna_pal_hex_to_uint(
+ line + OFFSET_COUNT, 2);
+ *addr = syna_pal_hex_to_uint(
+ line + OFFSET_ADDR, SIZE_ADDR);
+ *type = syna_pal_hex_to_uint(
+ line + OFFSET_TYPE, SIZE_TYPE);
+
+ if (*count > buf_size) {
+ LOGE("Data size mismatched, required:%d, given:%d\n",
+ *count, buf_size);
+ return _EINVAL;
+ }
+
+ for (pos = 0; pos < *count; pos++)
+ buf[pos] = (unsigned char)syna_pal_hex_to_uint(
+ line + (((int)(pos << 1)) + OFFSET_DATA),
+ SIZE_DATA);
+
+ return 0;
+}
+
+/**
+ * syna_tcm_parse_fw_ihex()
+ *
+ * Based on the firmware ihex file given , parse and convert into a binary
+ * firmware data to update.
+ *
+ * @param
+ * [ in] ihex: original ihex file
+ * [ in] ihex_size: size of given file
+ * [ in] ihex_info: data blob stored the parsed data from an ihex file.
+ * assume the data buffer inside was allocated
+ * [ in] len_per_line: length for a useful data in a line
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static inline int syna_tcm_parse_fw_ihex(const char *ihex, int ihex_size,
+ struct ihex_info *ihex_info, const unsigned int len_per_line)
+{
+ int retval;
+ unsigned int pos;
+ unsigned int record;
+ char *tmp = NULL;
+ unsigned int count;
+ unsigned int type;
+ unsigned char data[32] = { 0 };
+ unsigned int addr;
+ unsigned int offset;
+ unsigned int prev_addr;
+ unsigned int block_idx = 0;
+
+ if (!ihex) {
+ LOGE("No ihex data\n");
+ return _EINVAL;
+ }
+
+ if (!ihex_info) {
+ LOGE("Invalid ihex_info blob\n");
+ return _EINVAL;
+ }
+
+ if ((!ihex_info->bin) || (ihex_info->bin_size == 0)) {
+ LOGE("Invalid ihex_info->data\n");
+ return _EINVAL;
+ }
+
+ tmp = syna_pal_mem_alloc(len_per_line + 1, sizeof(char));
+ if (!tmp) {
+ LOGE("Fail to allocate temporary buffer\n");
+ return _ENOMEM;
+ }
+
+ offset = 0;
+ addr = 0;
+ pos = 0;
+ prev_addr = 0;
+
+ ihex_info->records = ihex_size / len_per_line;
+ LOGD("records = %d\n", ihex_info->records);
+
+ for (record = 0; record < ihex_info->records; record++) {
+ pos = record * len_per_line;
+ if ((char)ihex[pos] != ':') {
+ LOGE("Invalid string maker at pos %d, marker:%c\n",
+ pos, (char)ihex[pos]);
+ goto exit;
+ }
+
+ retval = syna_pal_mem_cpy(tmp, len_per_line,
+ &ihex[pos], ihex_size - pos, len_per_line);
+ if (retval < 0) {
+ LOGE("Fail to copy a line at pos %d\n", pos);
+ goto exit;
+ }
+
+ retval = syna_tcm_parse_ihex_line(tmp, &count, &addr, &type,
+ data, sizeof(data));
+ if (retval < 0) {
+ LOGE("Fail to parse line at pos %d\n", pos);
+ goto exit;
+ }
+
+ if ((((prev_addr + 2) & 0xFFFF) != addr) && (type == 0x00)) {
+ block_idx = (record == 0) ? 0 : block_idx + 1;
+ if (block_idx >= IHEX_MAX_BLOCKS) {
+ LOGE("Invalid block index\n");
+ goto exit;
+ }
+
+ ihex_info->block[block_idx].flash_addr =
+ addr + offset;
+ ihex_info->block[block_idx].data =
+ &ihex_info->bin[addr + offset];
+ ihex_info->block[block_idx].available = true;
+ }
+
+ if (type == 0x00) {
+
+ prev_addr = addr;
+ addr += offset;
+
+ if (addr >= ihex_info->bin_size) {
+ LOGE("No enough size for data0 addr:0x%x(%d)\n",
+ addr, addr);
+ goto exit;
+ }
+ ihex_info->bin[addr++] = data[0];
+
+ if (addr >= ihex_info->bin_size) {
+ LOGE("No enough size for data1 addr:0x%x(%d)\n",
+ addr, addr);
+ goto exit;
+ }
+ ihex_info->bin[addr++] = data[1];
+
+ ihex_info->block[block_idx].size += 2;
+
+ } else if (type == 0x02) {
+ offset = (data[0] << 8) + data[1];
+ offset <<= 4;
+ }
+ }
+
+ ihex_info->bin_size = addr; /* the actual size after data reordering */
+ LOGN("Size of firmware binary data = %d\n", ihex_info->bin_size);
+
+exit:
+ syna_pal_mem_free((void *)tmp);
+
+ return 0;
+}
+
+
+#endif /* end of _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ */
diff --git a/tcm/synaptics_touchcom_func_reflash.c b/tcm/synaptics_touchcom_func_reflash.c
new file mode 100644
index 0000000..af67b4a
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_reflash.c
@@ -0,0 +1,2074 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TCM touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_reflash.c
+ *
+ * This file implements the fw reflash related functions of TouchBoot.
+ * The declarations are available in synaptics_touchcom_func_reflash.h.
+ */
+
+#include "synaptics_touchcom_func_base.h"
+#include "synaptics_touchcom_func_reflash.h"
+
+/**
+ * @section: Reflash relevant definitions
+ *
+ */
+#define FLASH_READ_DELAY_MS (10)
+#define FLASH_WRITE_DELAY_MS (20)
+#define FLASH_ERASE_DELAY_MS (500)
+
+#define BOOT_CONFIG_SIZE 8
+#define BOOT_CONFIG_SLOTS 16
+
+#define DO_NONE (0)
+#define DO_UPDATE (1)
+
+/**
+ * syna_tcm_set_up_flash_access()
+ *
+ * Enter the bootloader fw if not in the mode.
+ * Besides, get the necessary parameters in boot info.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] reflash_data: data blob for reflash
+ *
+ * @return
+ * Result of image file comparison
+ */
+static int syna_tcm_set_up_flash_access(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data)
+{
+ int retval;
+ unsigned int temp;
+ struct tcm_identification_info id_info;
+ struct tcm_boot_info *boot_info;
+ unsigned int wr_chunk;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ LOGI("Set up flash access\n");
+
+ retval = syna_tcm_identify(tcm_dev, &id_info);
+ if (retval < 0) {
+ LOGE("Fail to do identification\n");
+ return retval;
+ }
+
+ /* switch to bootloader mode */
+ if (IS_APP_FW_MODE(id_info.mode)) {
+ LOGI("Prepare to enter bootloader mode\n");
+
+ retval = syna_tcm_switch_fw_mode(tcm_dev,
+ MODE_BOOTLOADER,
+ FW_MODE_SWITCH_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to enter bootloader mode\n");
+ return retval;
+ }
+ }
+
+ if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGE("Fail to enter bootloader mode (current: 0x%x)\n",
+ tcm_dev->dev_mode);
+ return retval;
+ }
+
+ boot_info = &tcm_dev->boot_info;
+
+ /* get boot info to set up the flash access */
+ retval = syna_tcm_get_boot_info(tcm_dev, boot_info);
+ if (retval < 0) {
+ LOGE("Fail to get boot info at mode 0x%x\n",
+ id_info.mode);
+ return retval;
+ }
+
+ wr_chunk = tcm_dev->max_wr_size;
+
+ temp = boot_info->write_block_size_words;
+ reflash_data->write_block_size = temp * 2;
+
+ LOGI("Write block size: %d (words size: %d)\n",
+ reflash_data->write_block_size, temp);
+
+ temp = syna_pal_le2_to_uint(boot_info->erase_page_size_words);
+ reflash_data->page_size = temp * 2;
+
+ LOGI("Erase page size: %d (words size: %d)\n",
+ reflash_data->page_size, temp);
+
+ temp = syna_pal_le2_to_uint(boot_info->max_write_payload_size);
+ reflash_data->max_write_payload_size = temp;
+
+ LOGI("Max write flash data size: %d\n",
+ reflash_data->max_write_payload_size);
+
+ if (reflash_data->write_block_size > (wr_chunk - 9)) {
+ LOGE("Write block size, %d, greater than chunk space, %d\n",
+ reflash_data->write_block_size, (wr_chunk - 9));
+ return _EINVAL;
+ }
+
+ if (reflash_data->write_block_size == 0) {
+ LOGE("Invalid write block size %d\n",
+ reflash_data->write_block_size);
+ return _EINVAL;
+ }
+
+ if (reflash_data->page_size == 0) {
+ LOGE("Invalid erase page size %d\n",
+ reflash_data->page_size);
+ return _EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * syna_tcm_compare_image_id_info()
+ *
+ * Compare the ID information between device and the image file,
+ * and then determine the area to be updated.
+ * The function should be called after parsing the image file.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ *
+ * @return
+ * Blocks to be updated
+ */
+int syna_tcm_compare_image_id_info(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data)
+{
+ enum update_area result;
+ unsigned int idx;
+ unsigned int image_fw_id;
+ unsigned int device_fw_id;
+ unsigned char *image_config_id;
+ unsigned char *device_config_id;
+ struct app_config_header *header;
+ const unsigned char *app_config_data;
+ struct block_data *app_config;
+
+ result = UPDATE_NONE;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash_data\n");
+ return _EINVAL;
+ }
+
+ app_config = &reflash_data->image_info.data[AREA_APP_CONFIG];
+
+ if (app_config->size < sizeof(struct app_config_header)) {
+ LOGE("Invalid application config in image file\n");
+ return _EINVAL;
+ }
+
+ app_config_data = app_config->data;
+ header = (struct app_config_header *)app_config_data;
+
+ image_fw_id = syna_pal_le4_to_uint(header->build_id);
+ device_fw_id = tcm_dev->packrat_number;
+
+ LOGN("Device firmware ID: %d, image build id: %d\n",
+ device_fw_id, image_fw_id);
+
+ if (image_fw_id > device_fw_id) {
+ LOGN("Image build ID newer than device fw ID\n");
+ result = UPDATE_FIRMWARE_CONFIG;
+ goto exit;
+ } else {
+ result = UPDATE_NONE;
+ goto exit;
+ }
+
+ image_config_id = header->customer_config_id;
+ device_config_id = tcm_dev->app_info.customer_config_id;
+
+ for (idx = 0; idx < MAX_SIZE_CONFIG_ID; idx++) {
+ if (image_config_id[idx] != device_config_id[idx]) {
+ LOGN("Different Config ID\n");
+ result = UPDATE_CONFIG_ONLY;
+ goto exit;
+ }
+ }
+
+ result = UPDATE_NONE;
+
+exit:
+ switch (result) {
+ case UPDATE_FIRMWARE_CONFIG:
+ LOGN("Update firmware and config\n");
+ break;
+ case UPDATE_CONFIG_ONLY:
+ LOGN("Update config only\n");
+ break;
+ case UPDATE_NONE:
+ default:
+ LOGN("No need to do reflash\n");
+ break;
+ }
+
+ return (int)result;
+}
+
+/**
+ * syna_tcm_check_flash_boot_config()
+ *
+ * Check whether the same flash address of boot config in between the device
+ * and the image file.
+ *
+ * @param
+ * [ in] boot_config: block data of boot_config from image file
+ * [ in] boot_info: data of boot info
+ * [ in] block_size: max size of write block
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_boot_config(struct block_data *boot_config,
+ struct tcm_boot_info *boot_info, unsigned int block_size)
+{
+ unsigned int start_block;
+ unsigned int image_addr;
+ unsigned int device_addr;
+
+ if (!boot_config) {
+ LOGE("Invalid boot_config block data\n");
+ return _EINVAL;
+ }
+
+ if (!boot_info) {
+ LOGE("Invalid boot_info\n");
+ return _EINVAL;
+ }
+
+ if (boot_config->size < BOOT_CONFIG_SIZE) {
+ LOGE("No valid BOOT_CONFIG size, %d, in image file\n",
+ boot_config->size);
+ return _EINVAL;
+ }
+
+ image_addr = boot_config->flash_addr;
+
+ LOGD("Boot Config address in image file: 0x%x\n", image_addr);
+
+ start_block = VALUE(boot_info->boot_config_start_block);
+ device_addr = start_block * block_size;
+
+ LOGD("Boot Config address in device: 0x%x\n", device_addr);
+
+ return DO_NONE;
+}
+
+/**
+ * syna_tcm_check_flash_app_config()
+ *
+ * Check whether the same flash address of app config in between the
+ * device and the image file.
+ *
+ * @param
+ * [ in] app_config: block data of app_config from image file
+ * [ in] app_info: data of application info
+ * [ in] block_size: max size of write block
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_app_config(struct block_data *app_config,
+ struct tcm_application_info *app_info, unsigned int block_size)
+{
+ unsigned int temp;
+ unsigned int image_addr;
+ unsigned int image_size;
+ unsigned int device_addr;
+ unsigned int device_size;
+
+ if (!app_config) {
+ LOGE("Invalid app_config block data\n");
+ return _EINVAL;
+ }
+
+ if (!app_info) {
+ LOGE("Invalid app_info\n");
+ return _EINVAL;
+ }
+
+ if (app_config->size == 0) {
+ LOGD("No APP_CONFIG in image file\n");
+ return DO_NONE;
+ }
+
+ image_addr = app_config->flash_addr;
+ image_size = app_config->size;
+
+ LOGD("App Config address in image file: 0x%x, size: %d\n",
+ image_addr, image_size);
+
+ temp = VALUE(app_info->app_config_start_write_block);
+ device_addr = temp * block_size;
+ device_size = VALUE(app_info->app_config_size);
+
+ LOGD("App Config address in device: 0x%x, size: %d\n",
+ device_addr, device_size);
+
+ if (device_addr == 0 && device_size == 0)
+ return DO_UPDATE;
+
+ if (image_addr != device_addr)
+ LOGW("App Config address mismatch, image:0x%x, dev:0x%x\n",
+ image_addr, device_addr);
+
+ if (image_size != device_size)
+ LOGW("App Config address size mismatch, image:%d, dev:%d\n",
+ image_size, device_size);
+
+ return DO_UPDATE;
+}
+
+/**
+ * syna_tcm_check_flash_disp_config()
+ *
+ * Check whether the same flash address of display config in between the
+ * device and the image file.
+ *
+ * @param
+ * [ in] disp_config: block data of disp_config from image file
+ * [ in] boot_info: data of boot info
+ * [ in] block_size: max size of write block
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_disp_config(struct block_data *disp_config,
+ struct tcm_boot_info *boot_info, unsigned int block_size)
+{
+ unsigned int temp;
+ unsigned int image_addr;
+ unsigned int image_size;
+ unsigned int device_addr;
+ unsigned int device_size;
+
+ if (!disp_config) {
+ LOGE("Invalid disp_config block data\n");
+ return _EINVAL;
+ }
+
+ if (!boot_info) {
+ LOGE("Invalid boot_info\n");
+ return _EINVAL;
+ }
+
+ /* disp_config area may not be included in all product */
+ if (disp_config->size == 0) {
+ LOGD("No DISP_CONFIG in image file\n");
+ return DO_NONE;
+ }
+
+ image_addr = disp_config->flash_addr;
+ image_size = disp_config->size;
+
+ LOGD("Disp Config address in image file: 0x%x, size: %d\n",
+ image_addr, image_size);
+
+ temp = VALUE(boot_info->display_config_start_block);
+ device_addr = temp * block_size;
+
+ temp = VALUE(boot_info->display_config_length_blocks);
+ device_size = temp * block_size;
+
+ LOGD("Disp Config address in device: 0x%x, size: %d\n",
+ device_addr, device_size);
+
+ if (image_addr != device_addr)
+ LOGW("Disp Config address mismatch, image:0x%x, dev:0x%x\n",
+ image_addr, device_addr);
+
+ if (image_size != device_size)
+ LOGW("Disp Config address size mismatch, image:%d, dev:%d\n",
+ image_size, device_size);
+
+ return DO_UPDATE;
+}
+
+/**
+ * syna_tcm_check_flash_app_code()
+ *
+ * Check whether the valid size of app firmware in the image file
+ *
+ * @param
+ * [ in] app_code: block data of app_code from image file
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_app_code(struct block_data *app_code)
+{
+ if (!app_code) {
+ LOGE("Invalid app_code block data\n");
+ return _EINVAL;
+ }
+
+ if (app_code->size == 0) {
+ LOGD("No %s in image file\n", AREA_ID_STR(app_code->id));
+ return _EINVAL;
+ }
+
+ return DO_UPDATE;
+}
+
+/**
+ * syna_tcm_check_flash_openshort()
+ *
+ * Check whether the valid size of openshort area in the image file
+ *
+ * @param
+ * [ in] open_short: block data of open_short from image file
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_openshort(struct block_data *open_short)
+{
+ if (!open_short) {
+ LOGE("Invalid open_short block data\n");
+ return _EINVAL;
+ }
+
+ /* open_short area may not be included in all product */
+ if (open_short->size == 0) {
+ LOGD("No %s in image file\n", AREA_ID_STR(open_short->id));
+ return DO_NONE;
+ }
+
+ return DO_UPDATE;
+}
+
+/**
+ * syna_tcm_check_flash_app_prod_test()
+ *
+ * Check whether the valid size of app prod_test area in the image file
+ *
+ * @param
+ * [ in] prod_test: block data of app_prod_test from image file
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_app_prod_test(struct block_data *prod_test)
+{
+ if (!prod_test) {
+ LOGE("Invalid app_prod_test block data\n");
+ return _EINVAL;
+ }
+
+ /* app_prod_test area may not be included in all product */
+ if (prod_test->size == 0) {
+ LOGD("No %s in image file\n", AREA_ID_STR(prod_test->id));
+ return DO_NONE;
+ }
+
+ return DO_UPDATE;
+}
+
+/**
+ * syna_tcm_check_flash_ppdt()
+ *
+ * Check whether the valid size of ppdt area in the image file
+ *
+ * @param
+ * [ in] ppdt: block data of PPDT from image file
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_ppdt(struct block_data *ppdt)
+{
+ if (!ppdt) {
+ LOGE("Invalid ppdt block data\n");
+ return _EINVAL;
+ }
+
+ /* open_short area may not be included in all product */
+ if (ppdt->size == 0) {
+ LOGD("No %s in image file\n", AREA_ID_STR(ppdt->id));
+ return DO_NONE;
+ }
+
+ return DO_UPDATE;
+}
+
+/**
+ * syna_tcm_check_flash_block()
+ *
+ * Dispatch to the proper helper to ensure the data of associated block area
+ * is correct in between the device and the image file.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] block: target block area to check
+ *
+ * @return
+ * '0' represent no need to do reflash;
+ * '1' represent a positive result of checking;
+ * otherwise, negative value on error.
+ */
+static int syna_tcm_check_flash_block(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct block_data *block)
+{
+ int retval = 0;
+ struct tcm_application_info *app_info;
+ struct tcm_boot_info *boot_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return DO_NONE;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return DO_NONE;
+ }
+
+ if (!block) {
+ LOGE("Invalid block data\n");
+ return DO_NONE;
+ }
+
+ app_info = &tcm_dev->app_info;
+ boot_info = &tcm_dev->boot_info;
+
+ switch (block->id) {
+ case AREA_APP_CODE:
+ retval = syna_tcm_check_flash_app_code(block);
+ break;
+ case AREA_APP_CONFIG:
+ retval = syna_tcm_check_flash_app_config(block, app_info,
+ reflash_data->write_block_size);
+ break;
+ case AREA_BOOT_CONFIG:
+ retval = syna_tcm_check_flash_boot_config(block, boot_info,
+ reflash_data->write_block_size);
+ break;
+ case AREA_DISP_CONFIG:
+ retval = syna_tcm_check_flash_disp_config(block, boot_info,
+ reflash_data->write_block_size);
+ break;
+ case AREA_OPEN_SHORT_TUNING:
+ retval = syna_tcm_check_flash_openshort(block);
+ break;
+ case AREA_PROD_TEST:
+ retval = syna_tcm_check_flash_app_prod_test(block);
+ break;
+ case AREA_PPDT:
+ retval = syna_tcm_check_flash_ppdt(block);
+ break;
+ default:
+ retval = DO_NONE;
+ break;
+ }
+
+ return retval;
+}
+
+/**
+ * syna_tcm_get_flash_data_location()
+ *
+ * Return the address and length of the specified data area
+ * in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] area: specified area in flash memory
+ * [out] addr: the flash address of the specified area returned
+ * [out] len: the size of the specified area returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_get_flash_data_location(struct tcm_dev *tcm_dev,
+ enum flash_area area, unsigned int *addr, unsigned int *len)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned char payload;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ switch (area) {
+ case AREA_CUSTOM_LCM:
+ payload = FLASH_LCM_DATA;
+ break;
+ case AREA_CUSTOM_OEM:
+ payload = FLASH_OEM_DATA;
+ break;
+ case AREA_PPDT:
+ payload = FLASH_PPDT_DATA;
+ break;
+ case AREA_FORCE_TUNING:
+ payload = FLASH_FORCE_CALIB_DATA;
+ break;
+ case AREA_OPEN_SHORT_TUNING:
+ payload = FLASH_OPEN_SHORT_TUNING_DATA;
+ break;
+ default:
+ LOGE("Invalid flash area %d\n", area);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_DATA_LOCATION,
+ &payload,
+ sizeof(payload),
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_GET_DATA_LOCATION);
+ goto exit;
+ }
+
+ if (tcm_dev->resp_buf.data_length != 4) {
+ LOGE("Invalid data length %d\n",
+ tcm_dev->resp_buf.data_length);
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ *addr = syna_pal_le2_to_uint(&tcm_dev->resp_buf.buf[0]);
+ *len = syna_pal_le2_to_uint(&tcm_dev->resp_buf.buf[2]);
+
+ retval = 0;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_reflash_send_command()
+ *
+ * Helper to wrap up the write_message() function.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: given command code
+ * [ in] payload: payload data, if any
+ * [ in] payload_len: length of payload data
+ * [ in] delay_ms_resp: delay time to get the response of command
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_reflash_send_command(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *payload,
+ unsigned int payload_len, unsigned int delay_ms_resp)
+{
+ int retval = 0;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device is not in BL mode, 0x%x\n", tcm_dev->dev_mode);
+ retval = _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ command,
+ payload,
+ payload_len,
+ &resp_code,
+ delay_ms_resp);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n", command);
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_read_flash()
+ *
+ * Implement the bootloader command to read specified data from flash memory.
+ *
+ * Reads to the protected bootloader code or application code areas will read
+ * as 0. If the number of words requested is too large, it may be truncated to
+ * an defined maximum read size.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] address: the address in flash memory to read
+ * [out] rd_data: data retrieved
+ * [ in] rd_len: length of data to be read
+ * [ in] rd_delay_ms: a short delay after the command executed
+ * set 'DEFAULT_FLASH_READ_DELAY' to use default,
+ * which is 10 us;
+ * set '0' or 'RESP_IN_ATTN' to select ATTN-driven.
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_read_flash(struct tcm_dev *tcm_dev,
+ unsigned int address, unsigned char *rd_data,
+ unsigned int rd_len, unsigned int rd_delay_ms)
+{
+ int retval = 0;
+ unsigned int length_words;
+ unsigned int flash_addr_words;
+ unsigned char out[6] = { 0 };
+ unsigned int delay_ms = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid rd_data buffer\n");
+ return _EINVAL;
+ }
+
+ if (address == 0 || rd_len == 0) {
+ LOGE("Invalid flash address and length\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ length_words = rd_len / 2;
+ flash_addr_words = address / 2;
+
+ LOGD("Flash address: 0x%x (words: 0x%x), size: %d (words: %d)\n",
+ address, flash_addr_words, rd_len, length_words);
+
+ out[0] = (unsigned char)flash_addr_words;
+ out[1] = (unsigned char)(flash_addr_words >> 8);
+ out[2] = (unsigned char)(flash_addr_words >> 16);
+ out[3] = (unsigned char)(flash_addr_words >> 24);
+ out[4] = (unsigned char)length_words;
+ out[5] = (unsigned char)(length_words >> 8);
+
+ if (rd_delay_ms == DEFAULT_FLASH_READ_DELAY)
+ delay_ms = FLASH_READ_DELAY_MS;
+ else
+ delay_ms = rd_delay_ms;
+
+ if (delay_ms == RESP_IN_ATTN) {
+ LOGD("xfer: %d, delay: ATTN-driven\n", length_words);
+ } else {
+ delay_ms = (delay_ms * length_words) / 1000;
+ LOGD("xfer: %d, delay: %d\n", length_words, delay_ms);
+ }
+
+ retval = syna_tcm_reflash_send_command(tcm_dev,
+ CMD_READ_FLASH,
+ out,
+ sizeof(out),
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to read flash data from addr 0x%x, size %d\n",
+ address, rd_len);
+ goto exit;
+ }
+
+ if (tcm_dev->resp_buf.data_length != rd_len) {
+ LOGE("Fail to read requested length %d, rd_len %d\n",
+ tcm_dev->resp_buf.data_length, rd_len);
+ retval = _EIO;
+ goto exit;
+ }
+
+ retval = syna_pal_mem_cpy(rd_data,
+ rd_len,
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ rd_len);
+ if (retval < 0) {
+ LOGE("Fail to copy read data, size %d\n", rd_len);
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_read_flash_boot_config()
+ *
+ * Read the data of boot config area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [out] rd_data: buffer used for storing the retrieved data
+ * [ in] rd_delay_us: delay time to access data in flash memory
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_read_flash_boot_config(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct tcm_buffer *rd_data, unsigned int rd_delay_us)
+{
+ int retval;
+ unsigned int temp;
+ unsigned int addr = 0;
+ unsigned int length = 0;
+ struct tcm_boot_info *boot_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid read data buffer\n");
+ return _EINVAL;
+ }
+
+ boot_info = &tcm_dev->boot_info;
+
+ if (IS_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("BOOT_CONFIG not available in app fw mode %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ temp = VALUE(boot_info->boot_config_start_block);
+ addr = temp * reflash_data->write_block_size;
+ length = BOOT_CONFIG_SIZE * BOOT_CONFIG_SLOTS;
+
+ if (addr == 0 || length == 0) {
+ LOGE("BOOT_CONFIG data area unavailable\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ LOGD("BOOT_CONFIG address: 0x%x, length: %d\n", addr, length);
+
+ retval = syna_tcm_buf_alloc(rd_data, length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for rd_data buffer\n");
+ goto exit;
+ }
+
+ retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf,
+ length, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to read BOOT_CONFIG area (addr: 0x%x, length: %d)\n",
+ addr, length);
+ goto exit;
+ }
+
+ rd_data->data_length = length;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_read_flash_app_config()
+ *
+ * Read the data of app config area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [out] rd_data: buffer used for storing the retrieved data
+ * [ in] rd_delay_us: delay time to access data in flash memory
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_read_flash_app_config(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct tcm_buffer *rd_data, unsigned int rd_delay_us)
+{
+ int retval;
+ unsigned int temp;
+ unsigned int addr = 0;
+ unsigned int length = 0;
+ struct tcm_application_info *app_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid read data buffer\n");
+ return _EINVAL;
+ }
+
+ app_info = &tcm_dev->app_info;
+
+ if (IS_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("APP_CONFIG not available in app fw mode %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ temp = VALUE(app_info->app_config_start_write_block);
+ addr = temp * reflash_data->write_block_size;
+ length = VALUE(app_info->app_config_size);
+
+ if (addr == 0 || length == 0) {
+ LOGE("APP_CONFIG data area unavailable\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ LOGD("APP_CONFIG address: 0x%x, length: %d\n", addr, length);
+
+ retval = syna_tcm_buf_alloc(rd_data, length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for rd_data buffer\n");
+ goto exit;
+ }
+
+ retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf,
+ length, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to read APP_CONFIG area (addr: 0x%x, length: %d)\n",
+ addr, length);
+ goto exit;
+ }
+
+ rd_data->data_length = length;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_read_flash_disp_config()
+ *
+ * Read the data of display config area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [out] rd_data: buffer used for storing the retrieved data
+ * [ in] rd_delay_us: delay time to access data in flash memory
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_read_flash_disp_config(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct tcm_buffer *rd_data, unsigned int rd_delay_us)
+{
+ int retval;
+ unsigned int temp;
+ unsigned int addr = 0;
+ unsigned int length = 0;
+ struct tcm_boot_info *boot_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid read data buffer\n");
+ return _EINVAL;
+ }
+
+ boot_info = &tcm_dev->boot_info;
+
+ if (IS_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("DISP_CONFIG not available in app fw mode %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ temp = VALUE(boot_info->display_config_start_block);
+ addr = temp * reflash_data->write_block_size;
+ temp = VALUE(boot_info->display_config_length_blocks);
+ length = temp * reflash_data->write_block_size;
+
+ if (addr == 0 || length == 0) {
+ LOGE("DISP_CONFIG data area unavailable\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ LOGD("DISP_CONFIG address: 0x%x, length: %d\n", addr, length);
+
+ retval = syna_tcm_buf_alloc(rd_data, length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for rd_data buffer\n");
+ goto exit;
+ }
+
+ retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf,
+ length, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to read DISP_CONFIG area (addr: 0x%x, length: %d)\n",
+ addr, length);
+ goto exit;
+ }
+
+ rd_data->data_length = length;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_read_flash_custom_otp()
+ *
+ * Read the data of custom OTP area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [out] rd_data: buffer used for storing the retrieved data
+ * [ in] rd_delay_us: delay time to access data in flash memory
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_read_flash_custom_otp(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct tcm_buffer *rd_data, unsigned int rd_delay_us)
+{
+ int retval;
+ unsigned int temp;
+ unsigned int addr = 0;
+ unsigned int length = 0;
+ struct tcm_boot_info *boot_info;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid read data buffer\n");
+ return _EINVAL;
+ }
+
+ boot_info = &tcm_dev->boot_info;
+
+ if (IS_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("CUSTOM_OTP not available in app fw mode %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ temp = VALUE(boot_info->custom_otp_start_block);
+ addr = temp * reflash_data->write_block_size;
+ temp = VALUE(boot_info->custom_otp_length_blocks);
+ length = temp * reflash_data->write_block_size;
+
+ if (addr == 0 || length == 0) {
+ LOGE("CUSTOM_OTP data area unavailable\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ LOGD("CUSTOM_OTP address: 0x%x, length: %d\n", addr, length);
+
+ retval = syna_tcm_buf_alloc(rd_data, length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for rd_data buffer\n");
+ goto exit;
+ }
+
+ retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf,
+ length, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to read CUSTOM_OTP area (addr: 0x%x, length: %d)\n",
+ addr, length);
+ goto exit;
+ }
+
+ rd_data->data_length = length;
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_read_flash_custom_data()
+ *
+ * Read the data of custom data in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] address: address generated by get_flash_data_location()
+ * [ in] size: size generated by get_flash_data_location()
+ * [out] rd_data: buffer used for storing the retrieved data
+ * [ in] rd_delay_us: delay time to access data in flash memory
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_read_flash_custom_data(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ unsigned int address, unsigned int size,
+ struct tcm_buffer *rd_data, unsigned int rd_delay_us)
+{
+ int retval;
+ unsigned int addr = 0;
+ unsigned int length = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid read data buffer\n");
+ return _EINVAL;
+ }
+
+ if (IS_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Custom data not available in app fw mode %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ addr = address * reflash_data->write_block_size;
+ length = size * reflash_data->write_block_size;
+
+ if (addr == 0 || length == 0) {
+ LOGE("Custom data area unavailable\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ LOGD("Custom data address: 0x%x, length: %d\n", addr, length);
+
+ retval = syna_tcm_buf_alloc(rd_data, length);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for rd_data buffer\n");
+ goto exit;
+ }
+
+ retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf,
+ length, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to read custom data (addr: 0x%x, length: %d)\n",
+ addr, length);
+ goto exit;
+ }
+
+ rd_data->data_length = length;
+
+exit:
+ return retval;
+}
+/**
+ * syna_tcm_read_flash_area()
+ *
+ * Entry function to read in the data of specific area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] area: flash area to read
+ * [out] data: buffer storing the retrieved data
+ * [ in] rd_delay_us: delay time in micro-sec to read words data from flash.
+ * set 'DEFAULT_FLASH_READ_DELAY' to use default,
+ * which is 10 us;
+ * set '0' or 'RESP_IN_ATTN' to select ATTN-driven.
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_read_flash_area(struct tcm_dev *tcm_dev,
+ enum flash_area area, struct tcm_buffer *rd_data,
+ unsigned int rd_delay_us)
+{
+ int retval;
+ unsigned int addr = 0;
+ unsigned int length = 0;
+ struct tcm_reflash_data_blob reflash_data;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!rd_data) {
+ LOGE("Invalid data buffer\n");
+ return _EINVAL;
+ }
+
+ switch (area) {
+ case AREA_CUSTOM_LCM:
+ case AREA_CUSTOM_OEM:
+ case AREA_PPDT:
+ case AREA_FORCE_TUNING:
+ case AREA_OPEN_SHORT_TUNING:
+ retval = syna_tcm_get_flash_data_location(tcm_dev,
+ area, &addr, &length);
+ if (retval < 0) {
+ LOGE("Fail to get data location of 0x%x\n", area);
+ return retval;
+ }
+ break;
+ default:
+ break;
+ }
+
+ retval = syna_tcm_set_up_flash_access(tcm_dev,
+ &reflash_data);
+ if (retval < 0) {
+ LOGE("Fail to set up flash access\n");
+ return retval;
+ }
+
+ syna_tcm_buf_init(&reflash_data.out);
+
+ switch (area) {
+ case AREA_BOOT_CONFIG:
+ retval = syna_tcm_read_flash_boot_config(tcm_dev,
+ &reflash_data, rd_data, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to get boot config data\n");
+ goto exit;
+ }
+ break;
+ case AREA_APP_CONFIG:
+ retval = syna_tcm_read_flash_app_config(tcm_dev,
+ &reflash_data, rd_data, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to get app config data\n");
+ goto exit;
+ }
+ break;
+ case AREA_DISP_CONFIG:
+ retval = syna_tcm_read_flash_disp_config(tcm_dev,
+ &reflash_data, rd_data, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to get disp config data\n");
+ goto exit;
+ }
+ break;
+ case AREA_CUSTOM_OTP:
+ retval = syna_tcm_read_flash_custom_otp(tcm_dev,
+ &reflash_data, rd_data, rd_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to get custom otp data\n");
+ goto exit;
+ }
+ break;
+ case AREA_CUSTOM_LCM:
+ case AREA_CUSTOM_OEM:
+ case AREA_PPDT:
+ case AREA_FORCE_TUNING:
+ case AREA_OPEN_SHORT_TUNING:
+ retval = syna_tcm_read_flash_custom_data(tcm_dev,
+ &reflash_data, addr, length, rd_data,
+ rd_delay_us);
+ break;
+ default:
+ LOGE("Invalid data area\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ LOGI("%s read\n", AREA_ID_STR(area));
+
+ retval = 0;
+
+exit:
+ retval = syna_tcm_switch_fw_mode(tcm_dev,
+ MODE_APPLICATION_FIRMWARE,
+ FW_MODE_SWITCH_DELAY_MS);
+ if (retval < 0)
+ LOGE("Fail to go back to application firmware\n");
+
+ syna_tcm_buf_release(&reflash_data.out);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_write_flash()
+ *
+ * Implement the bootloader command to write specified data to flash memory.
+ *
+ * If the length of the data to write is not an integer multiple of words,
+ * the trailing byte will be discarded. If the length of the data to write
+ * is not an integer number of write blocks, it will be zero-padded to the
+ * next write block.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] address: the address in flash memory to write
+ * [ in] wr_data: data to write
+ * [ in] wr_len: length of data to write
+ * [ in] wr_delay_ms: a short delay after the command executed
+ * set 'DEFAULT_FLASH_WRITE_DELAY' to use default,
+ * which is 20 us;
+ * set '0' or 'RESP_IN_ATTN' to select ATTN-driven.
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_write_flash(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ unsigned int address, const unsigned char *wr_data,
+ unsigned int wr_len, unsigned int wr_delay_ms)
+{
+ int retval;
+ unsigned int offset;
+ unsigned int w_length;
+ unsigned int xfer_length;
+ unsigned int remaining_length;
+ unsigned int flash_address;
+ unsigned int block_address;
+ unsigned int delay_ms;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ w_length = tcm_dev->max_wr_size - 8;
+
+ w_length = w_length - (w_length % reflash_data->write_block_size);
+
+ w_length = MIN(w_length, reflash_data->max_write_payload_size);
+
+ offset = 0;
+
+ remaining_length = wr_len;
+
+ syna_tcm_buf_lock(&reflash_data->out);
+
+ while (remaining_length) {
+ if (remaining_length > w_length)
+ xfer_length = w_length;
+ else
+ xfer_length = remaining_length;
+
+ retval = syna_tcm_buf_alloc(&reflash_data->out,
+ xfer_length + 2);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for buf.out\n");
+ syna_tcm_buf_unlock(&reflash_data->out);
+ return retval;
+ }
+
+ flash_address = address + offset;
+ block_address = flash_address / reflash_data->write_block_size;
+ reflash_data->out.buf[0] = (unsigned char)block_address;
+ reflash_data->out.buf[1] = (unsigned char)(block_address >> 8);
+
+ retval = syna_pal_mem_cpy(&reflash_data->out.buf[2],
+ reflash_data->out.buf_size - 2,
+ &wr_data[offset],
+ wr_len - offset,
+ xfer_length);
+ if (retval < 0) {
+ LOGE("Fail to copy write data ,size: %d\n",
+ xfer_length);
+ syna_tcm_buf_unlock(&reflash_data->out);
+ return retval;
+ }
+
+ if (wr_delay_ms == DEFAULT_FLASH_WRITE_DELAY)
+ delay_ms = FLASH_WRITE_DELAY_MS;
+ else
+ delay_ms = wr_delay_ms;
+
+ if (delay_ms == RESP_IN_ATTN) {
+ LOGD("xfer: %d, delay: ATTN-driven\n", xfer_length);
+ } else {
+ delay_ms = (delay_ms * xfer_length) / 1000;
+ LOGD("xfer: %d, delay: %d\n", xfer_length, delay_ms);
+ }
+
+ retval = syna_tcm_reflash_send_command(tcm_dev,
+ CMD_WRITE_FLASH,
+ reflash_data->out.buf,
+ xfer_length + 2,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to write data to flash addr 0x%x, size %d\n",
+ flash_address, xfer_length + 2);
+ syna_tcm_buf_unlock(&reflash_data->out);
+ return retval;
+ }
+
+ offset += xfer_length;
+ remaining_length -= xfer_length;
+ }
+
+ syna_tcm_buf_unlock(&reflash_data->out);
+
+ return 0;
+}
+
+/**
+ * syna_tcm_write_flash_block()
+ *
+ * Write data to the target block data area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] area: target block area to write
+ * [ in] wr_delay_us: delay time in micro-sec to write block data to flash.
+ * set 'DEFAULT_FLASH_WRITE_DELAY' to use default,
+ * which is 20 ms;
+ * set '0' or 'RESP_IN_ATTN' to select ATTN-driven.
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_write_flash_block(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct block_data *block, unsigned int wr_delay_us)
+{
+ int retval;
+ unsigned int size;
+ unsigned int flash_addr;
+ const unsigned char *data;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!block) {
+ LOGE("Invalid block data\n");
+ return _EINVAL;
+ }
+
+ data = block->data;
+ size = block->size;
+ flash_addr = block->flash_addr;
+
+ LOGD("Write data to %s - address: 0x%x, size: %d\n",
+ AREA_ID_STR(block->id), flash_addr, size);
+
+ if (size == 0) {
+ LOGI("No need to update, size = %d\n", size);
+ goto exit;
+ }
+
+ retval = syna_tcm_write_flash(tcm_dev, reflash_data,
+ flash_addr, data, size, wr_delay_us);
+ if (retval < 0) {
+ LOGE("Fail to write %s to flash (addr: 0x%x, size: %d)\n",
+ AREA_ID_STR(block->id), flash_addr, size);
+ return retval;
+ }
+
+exit:
+ LOGN("%s area written\n", AREA_ID_STR(block->id));
+
+ return 0;
+}
+
+/**
+ * syna_tcm_erase_flash()
+ *
+ * Implement the bootloader command, which is used to erase the specified
+ * blocks of flash memory.
+ *
+ * Until this command completes, the device may be unresponsive.
+ * Therefore, this helper is implemented as a blocked function, and the delay
+ * time is set to 200 ms in default.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] address: the address in flash memory to read
+ * [ in] size: size of data to write
+ * [ in] erase_delay_ms: the delay time to get the resp from mass erase
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_erase_flash(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ unsigned int address, unsigned int size,
+ unsigned int erase_delay_ms)
+{
+ int retval;
+ unsigned int page_start = 0;
+ unsigned int page_count = 0;
+ unsigned char out_buf[4] = {0};
+ int size_erase_cmd;
+
+ page_start = address / reflash_data->page_size;
+
+ page_count = syna_pal_ceil_div(size, reflash_data->page_size);
+
+ LOGD("Page start = %d (0x%04x), Page count = %d (0x%04x)\n",
+ page_start, page_start, page_count, page_count);
+
+ if ((page_start > 0xff) || (page_count > 0xff)) {
+ size_erase_cmd = 4;
+
+ out_buf[0] = (unsigned char)(page_start & 0xff);
+ out_buf[1] = (unsigned char)((page_start >> 8) & 0xff);
+ out_buf[2] = (unsigned char)(page_count & 0xff);
+ out_buf[3] = (unsigned char)((page_count >> 8) & 0xff);
+ } else {
+ size_erase_cmd = 2;
+
+ out_buf[0] = (unsigned char)(page_start & 0xff);
+ out_buf[1] = (unsigned char)(page_count & 0xff);
+ }
+
+ retval = syna_tcm_reflash_send_command(tcm_dev,
+ CMD_ERASE_FLASH,
+ out_buf,
+ size_erase_cmd,
+ erase_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to erase data at flash page 0x%x, count %d\n",
+ page_start, page_count);
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+ * syna_tcm_erase_flash_block()
+ *
+ * Mass erase the target block data area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] block: target block area to erase
+ * [ in] delay_ms: a short delay after the erase command executed
+ * set 'DEFAULT_FLASH_ERASE_DELAY' to use default,
+ * which is 500 ms;
+ * set '0' or 'RESP_IN_ATTN' to select ATTN-driven.
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_erase_flash_block(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct block_data *block, unsigned int delay_ms)
+{
+ int retval;
+ unsigned int size;
+ unsigned int flash_addr;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!block) {
+ LOGE("Invalid block data\n");
+ return _EINVAL;
+ }
+
+ flash_addr = block->flash_addr;
+
+ size = block->size;
+
+ if (delay_ms == DEFAULT_FLASH_ERASE_DELAY)
+ delay_ms = FLASH_ERASE_DELAY_MS;
+
+ LOGD("Erase %s block - address: 0x%x, size: %d\n",
+ AREA_ID_STR(block->id), flash_addr, size);
+
+ if (size == 0) {
+ LOGI("No need to erase, size = %d\n", size);
+ goto exit;
+ }
+
+ retval = syna_tcm_erase_flash(tcm_dev, reflash_data,
+ flash_addr, size, delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to erase %s data (addr: 0x%x, size: %d)\n",
+ AREA_ID_STR(block->id), flash_addr, size);
+ return retval;
+ }
+
+exit:
+ LOGN("%s area erased\n", AREA_ID_STR(block->id));
+
+ return 0;
+}
+
+/**
+ * syna_tcm_update_flash_block()
+ *
+ * Perform the reflash sequence to the target area
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ * [ in] block: target block area to update
+ * [ in] delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * for polling, set a value formatted with
+ * [erase | write];
+ * for ATTN-driven, set a '0' or 'RESP_IN_ATTN'
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_update_flash_block(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ struct block_data *block, unsigned int delay_ms)
+{
+ int retval;
+ unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF;
+ unsigned int wr_blk_delay_ms = delay_ms & 0xFFFF;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash data blob\n");
+ return _EINVAL;
+ }
+
+ if (!block) {
+ LOGE("Invalid block data\n");
+ return _EINVAL;
+ }
+
+ /* reflash is not needed for the partition */
+ retval = syna_tcm_check_flash_block(tcm_dev,
+ reflash_data,
+ block);
+ if (retval < 0) {
+ LOGE("Invalid %s area\n", AREA_ID_STR(block->id));
+ return retval;
+ }
+
+ if (retval == DO_NONE)
+ return 0;
+
+ LOGN("Prepare to erase %s area\n", AREA_ID_STR(block->id));
+
+ retval = syna_tcm_erase_flash_block(tcm_dev,
+ reflash_data,
+ block,
+ erase_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id));
+ return retval;
+ }
+
+ LOGN("Prepare to update %s area\n", AREA_ID_STR(block->id));
+
+ retval = syna_tcm_write_flash_block(tcm_dev,
+ reflash_data,
+ block,
+ wr_blk_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to write %s area\n", AREA_ID_STR(block->id));
+ return retval;
+ }
+
+ return 0;
+}
+
+/**
+ * syna_tcm_do_reflash_tddi()
+ *
+ * Implement the sequence specific for MODE_TDDI_BOOTLOADER.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: misc. data used for fw update
+ * [ in] type: the area to update
+ * [ in] delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * for polling, set a value formatted with
+ * [erase | write];
+ * for ATTN-driven, set a '0' or 'RESP_IN_ATTN'
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_do_reflash_tddi(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ enum update_area type, unsigned int delay_ms)
+{
+ int retval = 0;
+ int idx;
+ unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF;
+ unsigned int wr_blk_delay_ms = delay_ms & 0xFFFF;
+ struct image_info *image_info;
+ struct block_data *block;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash_data blob\n");
+ return _EINVAL;
+ }
+
+ image_info = &reflash_data->image_info;
+
+ if (tcm_dev->dev_mode != MODE_TDDI_BOOTLOADER) {
+ LOGE("Incorrect bootloader mode, 0x%02x, expected: 0x%02x\n",
+ tcm_dev->dev_mode, MODE_TDDI_BOOTLOADER);
+ return _EINVAL;
+ }
+
+ if (type == UPDATE_NONE)
+ goto exit;
+
+ /* Always mass erase all blocks, before writing the data */
+ for (idx = 0; idx < AREA_MAX; idx++) {
+
+ block = &image_info->data[idx];
+
+ if (!block->available)
+ continue;
+
+ retval = syna_tcm_check_flash_block(tcm_dev,
+ reflash_data,
+ block);
+ if (retval == DO_NONE)
+ continue;
+
+ LOGN("Prepare to erase %s area\n", AREA_ID_STR(block->id));
+
+ retval = syna_tcm_erase_flash_block(tcm_dev,
+ reflash_data,
+ block,
+ erase_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id));
+ goto exit;
+ }
+ }
+
+ /* Write all the data to flash */
+ for (idx = 0; idx < AREA_MAX; idx++) {
+
+ block = &image_info->data[idx];
+
+ if (!block->available)
+ continue;
+
+ retval = syna_tcm_check_flash_block(tcm_dev,
+ reflash_data,
+ block);
+ if (retval == DO_NONE)
+ continue;
+
+ LOGN("Prepare to update %s area\n", AREA_ID_STR(block->id));
+
+ retval = syna_tcm_write_flash_block(tcm_dev,
+ reflash_data,
+ block,
+ wr_blk_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to update %s area\n",
+ AREA_ID_STR(block->id));
+ goto exit;
+ }
+ }
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_do_reflash_generic()
+ *
+ * Implement the generic sequence of fw update in MODE_BOOTLOADER.
+ *
+ * Typically, it is applied on most of discrete touch controllers
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: misc. data used for fw update
+ * [ in] type: the area to update
+ * [ in] wait_delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * for polling, set a value formatted with
+ * [erase | write];
+ * for ATTN-driven, set a '0' or 'RESP_IN_ATTN'
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_do_reflash_generic(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data,
+ enum update_area type, unsigned int wait_delay_ms)
+{
+ int retval = 0;
+ struct block_data *block;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!reflash_data) {
+ LOGE("Invalid reflash_data blob\n");
+ return _EINVAL;
+ }
+
+ if (tcm_dev->dev_mode != MODE_BOOTLOADER) {
+ LOGE("Incorrect bootloader mode, 0x%02x, expected: 0x%02x\n",
+ tcm_dev->dev_mode, MODE_BOOTLOADER);
+ return _EINVAL;
+ }
+
+ switch (type) {
+ case UPDATE_FIRMWARE_CONFIG:
+ block = &reflash_data->image_info.data[AREA_APP_CODE];
+
+ retval = syna_tcm_update_flash_block(tcm_dev,
+ reflash_data,
+ block,
+ wait_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to update application firmware\n");
+ goto exit;
+ }
+ case UPDATE_CONFIG_ONLY:
+ block = &reflash_data->image_info.data[AREA_APP_CONFIG];
+
+ retval = syna_tcm_update_flash_block(tcm_dev,
+ reflash_data,
+ block,
+ wait_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to update application config\n");
+ goto exit;
+ }
+ break;
+ case UPDATE_NONE:
+ default:
+ break;
+ }
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_do_fw_update()
+ *
+ * The entry function to perform fw update upon TouchBoot.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] image: binary data to write
+ * [ in] image_size: size of data array
+ * [ in] wait_delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * for polling, set a value formatted with
+ * [erase | write];
+ * for ATTN-driven, set a '0' or 'RESP_IN_ATTN'
+ * [ in] force_reflash: '1' to do reflash anyway
+ * '0' to compare ID info before doing reflash.
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_do_fw_update(struct tcm_dev *tcm_dev,
+ const unsigned char *image, unsigned int image_size,
+ unsigned int wait_delay_ms, bool force_reflash)
+{
+ int retval;
+ enum update_area type = UPDATE_NONE;
+ struct tcm_reflash_data_blob reflash_data;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!image) || (image_size == 0)) {
+ LOGE("Invalid image data\n");
+ return _EINVAL;
+ }
+
+ LOGN("Prepare to do reflash\n");
+
+ syna_tcm_buf_init(&reflash_data.out);
+
+ reflash_data.image = image;
+ reflash_data.image_size = image_size;
+ syna_pal_mem_set(&reflash_data.image_info, 0x00,
+ sizeof(struct image_info));
+
+ retval = syna_tcm_parse_fw_image(image, &reflash_data.image_info);
+ if (retval < 0) {
+ LOGE("Fail to parse firmware image\n");
+ return retval;
+ }
+
+ LOGN("Start of reflash\n");
+
+ ATOMIC_SET(tcm_dev->firmware_flashing, 1);
+
+ if (force_reflash) {
+ type = UPDATE_FIRMWARE_CONFIG;
+ goto reflash;
+ }
+
+ type = (enum update_area)syna_tcm_compare_image_id_info(tcm_dev,
+ &reflash_data);
+
+ if (type == UPDATE_NONE)
+ goto exit;
+
+reflash:
+ syna_tcm_buf_init(&reflash_data.out);
+
+ /* set up flash access, and enter the bootloader mode */
+ retval = syna_tcm_set_up_flash_access(tcm_dev, &reflash_data);
+ if (retval < 0) {
+ LOGE("Fail to set up flash access\n");
+ goto exit;
+ }
+
+ /* perform the fw update */
+ if (tcm_dev->dev_mode == MODE_BOOTLOADER) {
+ retval = syna_tcm_do_reflash_generic(tcm_dev,
+ &reflash_data,
+ type,
+ wait_delay_ms);
+ } else if (tcm_dev->dev_mode == MODE_TDDI_BOOTLOADER) {
+ retval = syna_tcm_do_reflash_tddi(tcm_dev,
+ &reflash_data,
+ type,
+ wait_delay_ms);
+ } else {
+ LOGE("Incorrect bootloader mode, 0x%02x\n",
+ tcm_dev->dev_mode);
+ goto reset;
+ }
+
+ if (retval < 0) {
+ LOGE("Fail to do firmware update\n");
+ goto reset;
+ }
+
+ LOGN("End of reflash\n");
+
+ retval = 0;
+reset:
+ retval = syna_tcm_reset(tcm_dev);
+ if (retval < 0) {
+ LOGE("Fail to do reset\n");
+ goto exit;
+ }
+
+exit:
+ ATOMIC_SET(tcm_dev->firmware_flashing, 0);
+
+ syna_tcm_buf_release(&reflash_data.out);
+
+ return retval;
+}
+
diff --git a/tcm/synaptics_touchcom_func_reflash.h b/tcm/synaptics_touchcom_func_reflash.h
new file mode 100644
index 0000000..8e90b80
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_reflash.h
@@ -0,0 +1,145 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_reflash.h
+ *
+ * This file declares relevant functions and structures for TouchBoot.
+ */
+
+#ifndef _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_
+#define _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_
+
+#include "synaptics_touchcom_core_dev.h"
+#include "synaptics_touchcom_func_base_flash.h"
+
+/**
+ * @section: Blocks to be updated
+ */
+enum update_area {
+ UPDATE_NONE = 0,
+ UPDATE_FIRMWARE_CONFIG,
+ UPDATE_CONFIG_ONLY,
+ UPDATE_ALL_BLOCKS,
+};
+
+/**
+ * @section: Data Type in flash memory
+ */
+enum flash_data {
+ FLASH_LCM_DATA = 1,
+ FLASH_OEM_DATA,
+ FLASH_PPDT_DATA,
+ FLASH_FORCE_CALIB_DATA,
+ FLASH_OPEN_SHORT_TUNING_DATA,
+};
+
+/**
+ * @section: Specific data blob for reflash
+ *
+ * The structure contains various parameters being used in reflash
+ */
+struct tcm_reflash_data_blob {
+ /* binary data of an image file */
+ const unsigned char *image;
+ unsigned int image_size;
+ /* parsed data based on given image file */
+ struct image_info image_info;
+ /* standard information for flash access */
+ unsigned int page_size;
+ unsigned int write_block_size;
+ unsigned int max_write_payload_size;
+ /* temporary buffer during the reflash */
+ struct tcm_buffer out;
+};
+
+/**
+ * syna_tcm_compare_image_id_info()
+ *
+ * Compare the ID information between device and the image file,
+ * and determine the area to be updated.
+ * The function should be called after parsing the image file.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] reflash_data: data blob for reflash
+ *
+ * @return
+ * Comparison result
+ */
+int syna_tcm_compare_image_id_info(struct tcm_dev *tcm_dev,
+ struct tcm_reflash_data_blob *reflash_data);
+
+/**
+ * syna_tcm_read_flash_area()
+ *
+ * Entry function to read in the data of specific area in the flash memory.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] area: flash area to read
+ * [out] data: buffer storing the retrieved data
+ * [ in] rd_delay_us: delay time in micro-sec to read words data from flash.
+ * set '0' to use default time, which is 10 us;
+ * set 'FORCE_ATTN_DRIVEN' to adopt ATTN-driven.
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_read_flash_area(struct tcm_dev *tcm_dev,
+ enum flash_area area, struct tcm_buffer *rd_data,
+ unsigned int rd_delay_us);
+
+/**
+ * syna_tcm_do_fw_update()
+ *
+ * The entry function to perform fw update upon TouchBoot.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] image: image file given
+ * [ in] image_size: size of data array
+ * [ in] wait_delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * for polling, set a value formatted with
+ * [erase | write];
+ * for ATTN-driven, set a '0' or 'RESP_IN_ATTN'
+ * [ in] force_reflash: '1' to do reflash anyway
+ * '0' to compare ID info before doing reflash.
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_do_fw_update(struct tcm_dev *tcm_dev,
+ const unsigned char *image, unsigned int image_size,
+ unsigned int wait_delay_ms, bool force_reflash);
+
+#endif /* end of _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ */
diff --git a/tcm/synaptics_touchcom_func_romboot.c b/tcm/synaptics_touchcom_func_romboot.c
new file mode 100644
index 0000000..e144fa3
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_romboot.c
@@ -0,0 +1,1586 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TCM touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_romboot.c
+ *
+ * This file implements the ROM boot-loader related functions.
+ * The declarations are available in synaptics_touchcom_func_romboot.h.
+ */
+
+#include "synaptics_touchcom_func_base.h"
+#include "synaptics_touchcom_func_romboot.h"
+
+#define JEDEC_STATUS_CHECK_US_MIN 5000
+#define JEDEC_STATUS_CHECK_US_MAX 10000
+
+#define BINARY_FILE_MAGIC_VALUE 0xaa55
+
+#define ROMBOOT_FLASH_PAGE_SIZE 256
+
+/**
+ * @section: JEDEC flash command set
+ */
+enum flash_command {
+ JEDEC_PAGE_PROGRAM = 0x02,
+ JEDEC_READ_STATUS = 0x05,
+ JEDEC_WRITE_ENABLE = 0x06,
+ JEDEC_CHIP_ERASE = 0xc7,
+};
+
+
+/**
+ * syna_tcm_romboot_send_command()
+ *
+ * Helper to send a packet to ROM bootloader.
+ *
+ * Please be noted that the given packet must be formatted into the
+ * specific structure in order to communicate with flash.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] out: data sent out, if any
+ * [ in] out_size: size of data sent out
+ * [ in] in: buffer to store the data read in
+ * [ in] in_size: size of data read in
+ * [ in] delay_ms_resp: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_send_command(struct tcm_dev *tcm_dev,
+ unsigned char *out, unsigned int out_size, unsigned char *in,
+ unsigned int in_size, unsigned int delay_ms_resp)
+{
+ int retval;
+ unsigned char resp_code;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (out_size < sizeof(struct flash_param)) {
+ LOGE("Invalid size of out data, %d, min. size:%d\n",
+ out_size, (int)sizeof(struct flash_param));
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED,
+ out,
+ out_size,
+ &resp_code,
+ delay_ms_resp);
+ if (retval < 0) {
+ LOGE("Fail to send romboot flash command 0x%02x\n",
+ CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED);
+ goto exit;
+ }
+
+ LOGD("resp_code: 0x%x, resp length: %d\n",
+ resp_code, tcm_dev->resp_buf.data_length);
+
+ if ((in == NULL) || (in_size < tcm_dev->resp_buf.data_length))
+ goto exit;
+
+ /* copy resp data to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)in,
+ in_size,
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ tcm_dev->resp_buf.data_length);
+ if (retval < 0) {
+ LOGE("Fail to copy resp data to caller\n");
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+
+/**
+ * syna_tcm_romboot_multichip_send_command()
+ *
+ * Send a command code to the ROM bootloader inside the multi-chip device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] command: command to send
+ * [ in] out: additional data sent out, if any
+ * [ in] out_size: size of data sent out
+ * [ in] in: buffer to store the data read in
+ * [ in] in_size: size of data read in
+ * [ in] delay_ms: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_multichip_send_command(struct tcm_dev *tcm_dev,
+ unsigned char command, unsigned char *out,
+ unsigned int out_size, unsigned char *in,
+ unsigned int in_size, unsigned int delay_ms)
+{
+ int retval;
+ unsigned char *payld_buf = NULL;
+ unsigned int payld_size;
+ struct flash_param flash_param;
+ unsigned int offset = (int)sizeof(struct flash_param);
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ syna_pal_mem_set((void *)&flash_param, 0x00, sizeof(flash_param));
+
+ flash_param.read_size[0] = (unsigned char)(in_size & 0xff);
+ flash_param.read_size[1] = (unsigned char)(in_size >> 8) & 0xff;
+
+ flash_param.command = command;
+
+ payld_size = offset + out_size;
+ if (flash_param.command != 0x00)
+ payld_size += 2;
+
+ LOGD("Command: 0x%02x, packet size: %d, wr:%d, rd:%d\n",
+ command, payld_size, out_size, in_size);
+
+ payld_buf = syna_pal_mem_alloc(payld_size, sizeof(unsigned char));
+ if (!payld_buf) {
+ LOGE("Fail to allocate buffer to store flash command\n");
+ return _ENOMEM;
+ }
+
+ if (flash_param.command != 0x00) {
+ payld_buf[offset] = (unsigned char)out_size;
+ payld_buf[offset + 1] = (unsigned char)(out_size >> 8);
+
+ if (out_size > 0) {
+ retval = syna_pal_mem_cpy(&payld_buf[offset + 2],
+ payld_size - offset - 2,
+ out,
+ out_size,
+ out_size);
+ if (retval < 0) {
+ LOGE("Fail to copy payload to payld_buf\n");
+ goto exit;
+ }
+ }
+
+ LOGD("Packet: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ flash_param.byte0, flash_param.byte1, flash_param.byte2,
+ flash_param.read_size[0], flash_param.read_size[1],
+ flash_param.command, payld_buf[offset],
+ payld_buf[offset + 1]);
+ }
+
+ retval = syna_pal_mem_cpy(payld_buf, payld_size,
+ &flash_param, sizeof(flash_param), sizeof(flash_param));
+ if (retval < 0) {
+ LOGE("Fail to copy flash_param header to payld_buf\n");
+ goto exit;
+ }
+
+ retval = syna_tcm_romboot_send_command(tcm_dev,
+ payld_buf,
+ payld_size,
+ in,
+ in_size,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to write command 0x%x\n", flash_param.command);
+ goto exit;
+ }
+
+exit:
+ syna_pal_mem_free((void *)payld_buf);
+
+ return retval;
+}
+/**
+ * syna_tcm_romboot_multichip_get_resp()
+ *
+ * To get the response data from ROM bootloader inside the multi-chip device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] length: size to read
+ * [ in] resp: buffer to store the resp data
+ * [ in] resp_size: size of resp data
+ * [ in] delay_ms: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_multichip_get_resp(struct tcm_dev *tcm_dev,
+ unsigned int length, unsigned char *resp,
+ unsigned int resp_size, unsigned int delay_ms)
+{
+ int retval;
+ unsigned char *tmp_buf = NULL;
+ unsigned int xfer_len;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (resp && (resp_size < length)) {
+ LOGE("Invalid buffer size, len:%d, size:%d\n",
+ length, resp_size);
+ return _EINVAL;
+ }
+
+ xfer_len = length + 2;
+
+ tmp_buf = syna_pal_mem_alloc(xfer_len, sizeof(unsigned char));
+ if (!tmp_buf) {
+ LOGE("Fail to allocate tmp_buf\n");
+ return _ENOMEM;
+ }
+
+ retval = syna_tcm_romboot_multichip_send_command(tcm_dev,
+ CMD_NONE, NULL, 0,
+ tmp_buf, xfer_len, delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to get resp, size: %d\n", xfer_len);
+ goto exit;
+ }
+
+ if (resp) {
+ retval = syna_pal_mem_cpy(resp, resp_size,
+ &tmp_buf[1], xfer_len - 1, length);
+ if (retval < 0) {
+ LOGE("Fail to copy resp data\n");
+ goto exit;
+ }
+ }
+
+exit:
+ syna_pal_mem_free((void *)tmp_buf);
+
+ return retval;
+}
+/**
+ * syna_tcm_romboot_multichip_get_status()
+ *
+ * To poll the status until the completion
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] resp_status: response status returned
+ * [ in] resp_length: response length returned
+ * [ in] delay_ms: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_multichip_get_status(struct tcm_dev *tcm_dev,
+ unsigned char *resp_status, unsigned int *resp_length,
+ unsigned int delay_ms)
+{
+ int retval;
+ unsigned char resp[4] = { 0 };
+ int timeout = 0;
+ int MAX_TIMEOUT = 1000;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ syna_pal_sleep_ms(delay_ms);
+
+ do {
+ retval = syna_tcm_romboot_multichip_send_command(tcm_dev,
+ CMD_NONE, NULL, 0,
+ resp, 3, delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to poll the resp\n");
+ goto exit;
+ }
+
+ LOGD("status: %02x %02x %02x\n", resp[0], resp[1], resp[2]);
+
+ if (resp[0] == 0xff) {
+ syna_pal_sleep_ms(100);
+ timeout += 100;
+ continue;
+ } else if (resp[0] == 0x01) {
+ *resp_status = resp[0];
+ *resp_length = syna_pal_le2_to_uint(&resp[1]);
+ goto exit;
+ } else {
+ LOGE("Invalid resp, %02x %02x %02x\n",
+ resp[0], resp[1], resp[2]);
+ retval = _EIO;
+ goto exit;
+ }
+
+ } while (timeout < MAX_TIMEOUT);
+
+ if (timeout >= 500) {
+ LOGE("Timeout to get the status\n");
+ retval = _EIO;
+ }
+exit:
+ return retval;
+}
+/**
+ * syna_tcm_romboot_multichip_write_flash()
+ *
+ * Write the given binary data to the flash through the ROM bootloader
+ * inside the multi-chip device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] romboot_data: data blob for romboot
+ * [ in] address: the address in flash memory to write
+ * [ in] wr_data: binary data to write
+ * [ in] wr_len: length of data to write
+ * [ in] wr_delay_ms: a short delay after the command executed
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_multichip_write_flash(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_data_blob *romboot_data,
+ unsigned int address, const unsigned char *wr_data,
+ unsigned int wr_len, unsigned int wr_delay_ms)
+{
+ int retval;
+ unsigned int offset;
+ unsigned int w_length;
+ unsigned int xfer_length;
+ unsigned int remaining_length;
+ unsigned int flash_address;
+ unsigned int block_address;
+ unsigned char resp_code = 0;
+ unsigned int resp_length = 0;
+ unsigned int delay_ms;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ w_length = tcm_dev->max_wr_size - 16;
+
+ w_length = w_length - (w_length % romboot_data->write_block_size);
+
+ w_length = MIN(w_length, romboot_data->max_write_payload_size);
+
+ offset = 0;
+
+ remaining_length = wr_len;
+
+ syna_tcm_buf_lock(&romboot_data->out);
+
+ while (remaining_length) {
+ if (remaining_length > w_length)
+ xfer_length = w_length;
+ else
+ xfer_length = remaining_length;
+
+ retval = syna_tcm_buf_alloc(&romboot_data->out,
+ xfer_length + 2);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for buf.out\n");
+ goto exit;
+ }
+
+ flash_address = address + offset;
+ block_address = flash_address / romboot_data->write_block_size;
+
+ romboot_data->out.buf[0] = (unsigned char)block_address & 0xff;
+ romboot_data->out.buf[1] = (unsigned char)(block_address >> 8);
+
+ retval = syna_pal_mem_cpy(&romboot_data->out.buf[2],
+ romboot_data->out.buf_size - 2,
+ &wr_data[offset],
+ wr_len - offset,
+ xfer_length);
+ if (retval < 0) {
+ LOGE("Fail to copy write data ,size: %d\n",
+ xfer_length);
+ goto exit;
+ }
+
+ if (wr_delay_ms == 0)
+ delay_ms = ROMBOOT_DELAY_MS;
+ else
+ delay_ms = wr_delay_ms;
+
+ LOGD("write xfer: %d (remaining: %d)\n",
+ xfer_length, remaining_length);
+
+ retval = syna_tcm_romboot_multichip_send_command(tcm_dev,
+ CMD_WRITE_FLASH,
+ romboot_data->out.buf,
+ xfer_length + 2,
+ NULL,
+ 0,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to write data to flash addr 0x%x, size %d\n",
+ flash_address, xfer_length + 2);
+ goto exit;
+ }
+
+ retval = syna_tcm_romboot_multichip_get_status(tcm_dev,
+ &resp_code, &resp_length, ROMBOOT_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to get the response of command 0x%x\n",
+ CMD_WRITE_FLASH);
+ goto exit;
+ }
+
+ LOGD("status:%02x, data_length:%d\n", resp_code, resp_length);
+
+ if (resp_code != STATUS_OK) {
+ LOGE("Invalid response of command %x\n",
+ CMD_WRITE_FLASH);
+ retval = _EIO;
+ goto exit;
+ }
+
+ retval = syna_tcm_romboot_multichip_get_resp(tcm_dev,
+ resp_length, NULL, 0, ROMBOOT_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to get the boot info packet\n");
+ goto exit;
+ }
+
+ offset += xfer_length;
+ remaining_length -= xfer_length;
+ }
+
+ retval = 0;
+
+exit:
+ syna_tcm_buf_unlock(&romboot_data->out);
+
+ return retval;
+}
+/**
+ * syna_tcm_romboot_multichip_erase_flash()
+ *
+ * Ask the ROM bootloader to erase the flash inside the multi-chip device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] romboot_data: data blob for remboot
+ * [ in] address: the address in flash memory to read
+ * [ in] size: size of data to write
+ * [ in] erase_delay_ms: the delay time to get the resp from mass erase
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_multichip_erase_flash(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_data_blob *romboot_data,
+ unsigned int address, unsigned int size,
+ unsigned int erase_delay_ms)
+{
+ int retval;
+ unsigned int page_start = 0;
+ unsigned int page_count = 0;
+ unsigned char out_buf[4] = { 0 };
+ unsigned char resp_code = 0;
+ unsigned int resp_length = 0;
+ int size_erase_cmd;
+
+ page_start = address / romboot_data->page_size;
+
+ page_count = syna_pal_ceil_div(size, romboot_data->page_size);
+
+ LOGD("Page start = %d (0x%04x), Page count = %d (0x%04x)\n",
+ page_start, page_start, page_count, page_count);
+
+ if ((page_start > 0xff) || (page_count > 0xff)) {
+ size_erase_cmd = 4;
+
+ out_buf[0] = (unsigned char)(page_start & 0xff);
+ out_buf[1] = (unsigned char)((page_start >> 8) & 0xff);
+ out_buf[2] = (unsigned char)(page_count & 0xff);
+ out_buf[3] = (unsigned char)((page_count >> 8) & 0xff);
+ } else {
+ size_erase_cmd = 2;
+
+ out_buf[0] = (unsigned char)(page_start & 0xff);
+ out_buf[1] = (unsigned char)(page_count & 0xff);
+ }
+
+ retval = syna_tcm_romboot_multichip_send_command(tcm_dev,
+ CMD_ERASE_FLASH,
+ out_buf,
+ size_erase_cmd,
+ NULL,
+ 0,
+ erase_delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to erase data at 0x%x (page:0x%x, count:%d)\n",
+ address, page_start, page_count);
+ return retval;
+ }
+
+ retval = syna_tcm_romboot_multichip_get_status(tcm_dev,
+ &resp_code, &resp_length, ROMBOOT_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to get the response of command 0x%x\n",
+ CMD_ERASE_FLASH);
+ return retval;
+ }
+
+ LOGD("status:%02x, data_length:%d\n", resp_code, resp_length);
+
+ if (resp_code != STATUS_OK) {
+ LOGE("Invalid response of command %x\n", CMD_WRITE_FLASH);
+ retval = _EIO;
+ return retval;
+ }
+
+ return retval;
+}
+
+/**
+ * syna_tcm_romboot_multichip_get_boot_info()
+ *
+ * To request a boot information packet from ROM bootloader inside
+ * the multi-chip device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] boot_info: the boot info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_multichip_get_boot_info(struct tcm_dev *tcm_dev,
+ struct tcm_boot_info *boot_info)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int resp_data_len = 0;
+ unsigned int copy_size;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = syna_tcm_romboot_multichip_send_command(tcm_dev,
+ CMD_GET_BOOT_INFO, NULL, 0, NULL, 0, ROMBOOT_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to run command 0x%x\n", CMD_GET_BOOT_INFO);
+ goto exit;
+ }
+
+ retval = syna_tcm_romboot_multichip_get_status(tcm_dev,
+ &resp_code, &resp_data_len, ROMBOOT_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to get the response of command 0x%x\n",
+ CMD_GET_BOOT_INFO);
+ return retval;
+ }
+
+ LOGD("status:%02x, data_length:%d\n", resp_code, resp_data_len);
+
+ if (resp_code != STATUS_OK) {
+ LOGE("Invalid response of command %x\n", CMD_GET_BOOT_INFO);
+ retval = _EIO;
+ return retval;
+ }
+
+ if (boot_info == NULL)
+ goto exit;
+
+ copy_size = MIN(sizeof(struct tcm_boot_info), resp_data_len);
+
+ retval = syna_tcm_romboot_multichip_get_resp(tcm_dev,
+ copy_size, (unsigned char *)boot_info,
+ sizeof(struct tcm_boot_info), ROMBOOT_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to get the boot info packet\n");
+ return retval;
+ }
+
+exit:
+ return retval;
+}
+/**
+ * syna_tcm_romboot_preparation()
+ *
+ * Perform the preparation before doing firmware update of multi-chip device
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] romboot_data: data blob for romboot access
+ * [ in] is_multichip: flag to indicate a multichip DUT
+ *
+ * @return
+ * Result of image file comparison
+ */
+static int syna_tcm_romboot_preparation(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_data_blob *romboot_data, bool is_multichip)
+{
+ int retval;
+ unsigned int temp;
+ struct tcm_boot_info *boot_info;
+ unsigned int wr_chunk;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!romboot_data) {
+ LOGE("Invalid romboot data blob\n");
+ return _EINVAL;
+ }
+
+ LOGI("Set up preparation, multi-chip: %s\n",
+ (is_multichip)?"yes":"no");
+
+ retval = syna_tcm_identify(tcm_dev, NULL);
+ if (retval < 0) {
+ LOGE("Fail to do identification\n");
+ return retval;
+ }
+
+ /* switch to bootloader mode */
+ if (IS_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGI("Prepare to enter bootloader mode\n");
+ if (is_multichip)
+ retval = syna_tcm_switch_fw_mode(tcm_dev,
+ MODE_MULTICHIP_TDDI_BOOTLOADER,
+ FW_MODE_SWITCH_DELAY_MS);
+ else
+ retval = syna_tcm_switch_fw_mode(tcm_dev,
+ MODE_TDDI_BOOTLOADER,
+ FW_MODE_SWITCH_DELAY_MS);
+
+ if (retval < 0) {
+ LOGE("Fail to enter bootloader mode\n");
+ return retval;
+ }
+ }
+ /* switch to rom boot mode */
+ if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGI("Prepare to enter rom boot mode\n");
+
+ retval = syna_tcm_switch_fw_mode(tcm_dev,
+ MODE_ROMBOOTLOADER,
+ FW_MODE_SWITCH_DELAY_MS);
+ if (retval < 0) {
+ LOGE("Fail to enter rom boot mode\n");
+ return retval;
+ }
+ }
+
+ if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGE("Device not in romboot mode\n");
+ return _EINVAL;
+ }
+
+ if (!is_multichip)
+ return 0;
+
+ boot_info = &tcm_dev->boot_info;
+
+ retval = syna_tcm_romboot_multichip_get_boot_info(tcm_dev,
+ boot_info);
+ if (retval < 0) {
+ LOGE("Fail to get boot info\n");
+ return retval;
+ }
+
+ wr_chunk = tcm_dev->max_wr_size;
+
+ temp = boot_info->write_block_size_words;
+ romboot_data->write_block_size = temp * 2;
+
+ LOGI("Write block size: %d (words size: %d)\n",
+ romboot_data->write_block_size, temp);
+
+ temp = syna_pal_le2_to_uint(boot_info->erase_page_size_words);
+ romboot_data->page_size = temp * 2;
+
+ LOGI("Erase page size: %d (words size: %d)\n",
+ romboot_data->page_size, temp);
+
+ temp = syna_pal_le2_to_uint(boot_info->max_write_payload_size);
+ romboot_data->max_write_payload_size = temp;
+
+ LOGI("Max write flash data size: %d\n",
+ romboot_data->max_write_payload_size);
+
+ if (romboot_data->write_block_size > (wr_chunk - 9)) {
+ LOGE("Write block size, %d, greater than chunk space, %d\n",
+ romboot_data->write_block_size, (wr_chunk - 9));
+ return _EINVAL;
+ }
+
+ if (romboot_data->write_block_size == 0) {
+ LOGE("Invalid write block size %d\n",
+ romboot_data->write_block_size);
+ return _EINVAL;
+ }
+
+ if (romboot_data->page_size == 0) {
+ LOGE("Invalid erase page size %d\n",
+ romboot_data->page_size);
+ return _EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * syna_tcm_romboot_jedec_send_command()
+ *
+ * Send a jedec flash commend to the ROM bootloader
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] flash_command: flash command to send
+ * [ in] out: additional data sent out, if any
+ * [ in] out_size: size of data sent out
+ * [ in] in: buffer to store the data read in
+ * [ in] in_size: size of data read in
+ * [ in] delay_ms: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_jedec_send_command(struct tcm_dev *tcm_dev,
+ unsigned char flash_command, unsigned char *out,
+ unsigned int out_size, unsigned char *in, unsigned int in_size,
+ unsigned int delay_ms)
+{
+ int retval;
+ unsigned char *payld_buf = NULL;
+ unsigned int payld_size;
+ struct flash_param flash_param;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ syna_pal_mem_set((void *)&flash_param, 0x00, sizeof(flash_param));
+
+ flash_param.spi_param = 1;
+ flash_param.clk_div = 0x19;
+
+ flash_param.read_size[0] = (unsigned char)(in_size & 0xff);
+ flash_param.read_size[1] = (unsigned char)(in_size >> 8) & 0xff;
+
+ flash_param.command = flash_command;
+
+ payld_size = sizeof(struct flash_param) + out_size;
+
+ LOGD("Flash command: 0x%02x, total size: %d, wr: %d, rd: %d\n",
+ flash_command, payld_size, out_size, in_size);
+ LOGD("Packet: %02x %02x %02x %02x %02x %02x\n",
+ flash_param.byte0, flash_param.byte1, flash_param.byte2,
+ flash_param.read_size[0], flash_param.read_size[1],
+ flash_param.command);
+
+ payld_buf = syna_pal_mem_alloc(payld_size, sizeof(unsigned char));
+ if (!payld_buf) {
+ LOGE("Fail to allocate buffer to store flash command\n");
+ return _ENOMEM;
+ }
+
+ retval = syna_pal_mem_cpy(payld_buf, payld_size,
+ &flash_param, sizeof(flash_param), sizeof(flash_param));
+ if (retval < 0) {
+ LOGE("Fail to copy flash_param header to payld_buf\n");
+ goto exit;
+ }
+
+ if (out && (out_size > 0)) {
+ retval = syna_pal_mem_cpy(payld_buf + sizeof(flash_param),
+ payld_size - sizeof(flash_param),
+ out, out_size, out_size);
+ if (retval < 0) {
+ LOGE("Fail to copy data to payld_buf\n");
+ goto exit;
+ }
+ }
+
+ retval = syna_tcm_romboot_send_command(tcm_dev,
+ payld_buf,
+ payld_size,
+ in,
+ in_size,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to write flash command 0x%x\n", flash_command);
+ goto exit;
+ }
+
+exit:
+ syna_pal_mem_free((void *)payld_buf);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_romboot_jedec_get_status()
+ *
+ * Use jedec command to poll the flash status until the completion
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] delay_ms: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_jedec_get_status(struct tcm_dev *tcm_dev,
+ unsigned int delay_ms)
+{
+ int retval;
+ int idx;
+ unsigned char status;
+ int STATUS_CHECK_RETRY = 50;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ for (idx = 0; idx < STATUS_CHECK_RETRY; idx++) {
+ retval = syna_tcm_romboot_jedec_send_command(tcm_dev,
+ JEDEC_READ_STATUS,
+ NULL,
+ 0,
+ &status,
+ sizeof(status),
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Failed to write JEDEC_READ_STATUS\n");
+ return retval;
+ }
+
+ syna_pal_sleep_us(JEDEC_STATUS_CHECK_US_MIN,
+ JEDEC_STATUS_CHECK_US_MAX);
+ /* once completed, status = 0 */
+ if (!status)
+ break;
+ }
+
+ if (status)
+ retval = _EIO;
+ else
+ retval = status;
+
+ return retval;
+}
+
+/**
+ * syna_tcm_romboot_jedec_erase_flash()
+ *
+ * Ask the ROM bootloader to erase the flash by using the jedec command
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] delay_ms: delay time to get the response
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_jedec_erase_flash(struct tcm_dev *tcm_dev,
+ unsigned int delay_ms)
+{
+ int retval;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = syna_tcm_romboot_jedec_send_command(tcm_dev,
+ JEDEC_WRITE_ENABLE,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Failed to write JEDEC_WRITE_ENABLE\n");
+ return retval;
+ }
+
+ retval = syna_tcm_romboot_jedec_send_command(tcm_dev,
+ JEDEC_CHIP_ERASE,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Failed to write JEDEC_WRITE_ENABLE\n");
+ return retval;
+ }
+
+ retval = syna_tcm_romboot_jedec_get_status(tcm_dev, delay_ms);
+ if (retval < 0)
+ LOGE("Fail to get correct status, retval = %d\n", retval);
+
+ return retval;
+}
+
+ /**
+ * syna_tcm_romboot_jedec_write_flash()
+ *
+ * Write the given binary data to the flash through the ROM bootloader
+ * by using the jedec command
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] address: the address in flash memory to write
+ * [ in] data: binary data to write
+ * [ in] data_size: size of binary data
+ * [ in] delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_jedec_write_flash(struct tcm_dev *tcm_dev,
+ unsigned int address, const unsigned char *data,
+ unsigned int data_size, unsigned int delay_ms)
+{
+ int retval = 0;
+ unsigned int offset;
+ unsigned char buf[ROMBOOT_FLASH_PAGE_SIZE + 3];
+ unsigned int remaining_length;
+ unsigned int xfer_length;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!data) || (data_size == 0)) {
+ LOGE("Invalid image data, no data available\n");
+ return _EINVAL;
+ }
+
+ remaining_length = data_size;
+
+ offset = 0;
+
+ while (remaining_length) {
+ if (remaining_length > ROMBOOT_FLASH_PAGE_SIZE)
+ xfer_length = ROMBOOT_FLASH_PAGE_SIZE;
+ else
+ xfer_length = remaining_length;
+
+ syna_pal_mem_set(buf, 0x00, sizeof(buf));
+
+ retval = syna_tcm_romboot_jedec_send_command(tcm_dev,
+ JEDEC_WRITE_ENABLE,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Failed to write JEDEC_WRITE_ENABLE\n");
+ goto exit;
+ }
+
+ buf[0] = (unsigned char)((address + offset) >> 16);
+ buf[1] = (unsigned char)((address + offset) >> 8);
+ buf[2] = (unsigned char)(address + offset);
+
+ retval = syna_pal_mem_cpy(&buf[3],
+ sizeof(buf) - 3,
+ &data[offset],
+ data_size - offset,
+ xfer_length);
+ if (retval < 0) {
+ LOGE("Fail to copy data to write, size: %d\n",
+ xfer_length);
+ goto exit;
+ }
+
+ retval = syna_tcm_romboot_jedec_send_command(tcm_dev,
+ JEDEC_PAGE_PROGRAM,
+ buf,
+ sizeof(buf),
+ NULL,
+ 0,
+ delay_ms);
+ if (retval < 0) {
+ LOGE("Failed to write data to addr 0x%x (offset: %x)\n",
+ address + offset, offset);
+ LOGE("Remaining data %d\n",
+ remaining_length);
+ goto exit;
+ }
+
+ retval = syna_tcm_romboot_jedec_get_status(tcm_dev, delay_ms);
+ if (retval < 0) {
+ LOGE("Fail to get correct status, retval = %d\n",
+ retval);
+ goto exit;
+ }
+ offset += xfer_length;
+ remaining_length -= xfer_length;
+ }
+
+exit:
+ return retval;
+}
+
+
+/**
+ * syna_tcm_romboot_erase_flash()
+ *
+ * The entry function to perform mass erase
+ *
+ * @param
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] romboot_data: data blob for remboot
+ * [ in] blk: the block in flash memory to erase
+ * [ in] delay_ms: delay time to get the response
+ * [ in] is_multichip: use multi-chip command packet instead
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_erase_flash(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_data_blob *romboot_data,
+ struct block_data *blk, unsigned int delay_ms,
+ bool is_multichip)
+{
+ int retval;
+
+ if (!tcm_dev || !romboot_data || !blk)
+ return _EINVAL;
+
+ if (is_multichip)
+ retval = syna_tcm_romboot_multichip_erase_flash(tcm_dev,
+ romboot_data, blk->flash_addr, blk->size,
+ delay_ms);
+ else
+ retval = syna_tcm_romboot_jedec_erase_flash(tcm_dev,
+ delay_ms);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_romboot_write_flash()
+ *
+ * The entry function to write hex data to flash
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] romboot_data: data blob for remboot
+ * [ in] blk: the block in flash memory to update
+ * [ in] delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * [ in] is_multichip: use multi-chip command packet instead
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_romboot_write_flash(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_data_blob *romboot_data,
+ struct block_data *blk, unsigned int delay_ms,
+ bool is_multichip)
+{
+ int retval;
+
+ if (!tcm_dev || !romboot_data || !blk)
+ return _EINVAL;
+
+ if (is_multichip)
+ retval = syna_tcm_romboot_multichip_write_flash(tcm_dev,
+ romboot_data, blk->flash_addr, blk->data,
+ blk->size, delay_ms);
+ else
+ retval = syna_tcm_romboot_jedec_write_flash(tcm_dev,
+ blk->flash_addr, blk->data, blk->size,
+ delay_ms);
+
+ return retval;
+}
+
+
+/**
+ * syna_tcm_romboot_do_ihex_update()
+ *
+ * The entry function to perform ihex update upon ROM Boot.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] ihex: ihex data to write
+ * [ in] ihex_size: size of ihex data
+ * [ in] flash_size: size for temporary buffer allocation, which used
+ * to re-order the flash data.
+ * in general, (ihex_size + 4K) is preferred size
+ * [ in] len_per_line: length per line in the ihex file
+ * [ in] delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * [ in] is_multichip: flag to indicate a multi-chip product used
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_romboot_do_ihex_update(struct tcm_dev *tcm_dev,
+ const unsigned char *ihex, unsigned int ihex_size,
+ unsigned int flash_size, unsigned int len_per_line,
+ unsigned int delay_ms, bool is_multichip)
+{
+ int retval;
+ struct tcm_romboot_data_blob romboot_data;
+ struct ihex_info *ihex_info = NULL;
+ struct block_data *block;
+ unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF;
+ unsigned int wr_delay_ms = delay_ms & 0xFFFF;
+ unsigned short *header;
+ int idx;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!ihex) || (ihex_size == 0)) {
+ LOGE("Invalid ihex data\n");
+ return _EINVAL;
+ }
+
+ if (flash_size == 0)
+ flash_size = ihex_size + 4096;
+
+ romboot_data.bdata = ihex;
+ romboot_data.bdata_size = ihex_size;
+ syna_pal_mem_set(&romboot_data.ihex_info, 0x00,
+ sizeof(struct ihex_info));
+
+ ihex_info = &romboot_data.ihex_info;
+
+ ihex_info->bin = syna_pal_mem_alloc(flash_size,
+ sizeof(unsigned char));
+ if (!ihex_info->bin) {
+ LOGE("Fail to allocate buffer for ihex data\n");
+
+ return _ENOMEM;
+ }
+
+ ihex_info->bin_size = flash_size;
+
+ syna_tcm_buf_init(&romboot_data.out);
+
+ LOGN("Parse ihex file\n");
+
+ /* parse ihex file */
+ retval = syna_tcm_parse_fw_ihex((const char *)ihex,
+ ihex_size, ihex_info, len_per_line);
+ if (retval < 0) {
+ LOGE("Fail to parse firmware ihex file\n");
+ goto exit;
+ }
+
+ if (!is_multichip) {
+ header = (unsigned short *)ihex_info->block[0].data;
+ if (*header != BINARY_FILE_MAGIC_VALUE) {
+ LOGE("Incorrect image header 0x%04X\n", *header);
+ goto exit;
+ }
+ }
+
+ /* set up flash access, and enter the bootloader mode */
+ retval = syna_tcm_romboot_preparation(tcm_dev,
+ &romboot_data,
+ is_multichip);
+ if (retval < 0) {
+ LOGE("Fail to do preparation\n");
+ goto reset;
+ }
+
+ LOGN("Start of ihex update\n");
+
+ ATOMIC_SET(tcm_dev->firmware_flashing, 1);
+
+ for (idx = 0; idx < IHEX_MAX_BLOCKS; idx++) {
+
+ block = &ihex_info->block[idx];
+
+ if (!block->available)
+ continue;
+
+ if (block->size == 0)
+ continue;
+
+ /* for single-chip, mass erase will affect the
+ * entire flash memory, so it just takes once
+ */
+ if ((idx != 0) && (!is_multichip))
+ break;
+
+ retval = syna_tcm_romboot_erase_flash(tcm_dev,
+ &romboot_data,
+ block,
+ erase_delay_ms,
+ is_multichip);
+ if (retval < 0) {
+ LOGE("Fail to erase flash\n");
+ goto reset;
+ }
+ }
+
+ LOGN("Flash erased\n");
+ LOGN("Start to write all data to flash\n");
+
+ for (idx = 0; idx < IHEX_MAX_BLOCKS; idx++) {
+
+ block = &ihex_info->block[idx];
+
+ if (!block->available)
+ continue;
+
+ LOGD("block:%d, addr:0x%x, size:%d\n",
+ idx, block->flash_addr, block->size);
+
+ if (block->size == 0)
+ continue;
+
+ retval = syna_tcm_romboot_write_flash(tcm_dev,
+ &romboot_data,
+ block,
+ wr_delay_ms,
+ is_multichip);
+ if (retval < 0) {
+ LOGE("Fail to write data to addr 0x%x, size:%d\n",
+ block->flash_addr, block->size);
+ goto reset;
+ }
+
+ LOGI("Data written, size:%d\n", block->size);
+ }
+
+ LOGN("End of ihex update\n");
+
+ retval = 0;
+
+reset:
+ retval = syna_tcm_reset(tcm_dev);
+ if (retval < 0) {
+ LOGE("Fail to do reset\n");
+ goto exit;
+ }
+
+exit:
+ syna_pal_mem_free((void *)ihex_info->bin);
+
+ ATOMIC_SET(tcm_dev->firmware_flashing, 0);
+
+ syna_tcm_buf_release(&romboot_data.out);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_romboot_do_multichip_reflash()
+ *
+ * The entry function to perform fw update in multi-chip product.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] image: image file given
+ * [ in] image_size: size of data array
+ * [ in] wait_delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * for polling, set a value formatted with
+ * [erase | write];
+ * for ATTN-driven, set a '0' or 'RESP_IN_ATTN'
+ * [ in] force_reflash: '1' to do reflash anyway
+ * '0' to compare ID info before doing reflash.
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_romboot_do_multichip_reflash(struct tcm_dev *tcm_dev,
+ const unsigned char *image, unsigned int image_size,
+ unsigned int wait_delay_ms, bool force_reflash)
+{
+ int retval;
+ int idx;
+ struct tcm_romboot_data_blob romboot_data;
+ struct block_data *block;
+ struct app_config_header *header;
+ unsigned int image_fw_id;
+ unsigned int erase_delay_ms = (wait_delay_ms >> 16) & 0xFFFF;
+ unsigned int wr_delay_ms = wait_delay_ms & 0xFFFF;
+ bool has_tool_boot_cfg = false;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!image) || (image_size == 0)) {
+ LOGE("Invalid image data\n");
+ return _EINVAL;
+ }
+
+ LOGN("Prepare to do reflash\n");
+
+ syna_pal_mem_set(&romboot_data, 0x00,
+ sizeof(struct tcm_romboot_data_blob));
+
+ syna_tcm_buf_init(&romboot_data.out);
+
+ romboot_data.bdata = image;
+ romboot_data.bdata_size = image_size;
+
+ syna_tcm_buf_init(&romboot_data.out);
+
+ retval = syna_tcm_parse_fw_image(image, &romboot_data.image_info);
+ if (retval < 0) {
+ LOGE("Fail to parse firmware image\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+
+ block = &romboot_data.image_info.data[AREA_APP_CONFIG];
+ if (block->size < sizeof(struct app_config_header)) {
+ LOGE("Invalid application config in image file\n");
+ retval = _EINVAL;
+ goto exit;
+ }
+ header = (struct app_config_header *)block->data;
+
+ image_fw_id = syna_pal_le4_to_uint(header->build_id);
+
+ LOGN("Device firmware ID: %d, image build id: %d\n",
+ tcm_dev->packrat_number, image_fw_id);
+
+ if ((image_fw_id <= tcm_dev->packrat_number) && !force_reflash) {
+ LOGN("No need to do reflash\n");
+ retval = 0;
+ goto exit;
+ }
+
+ block = &romboot_data.image_info.data[AREA_TOOL_BOOT_CONFIG];
+ has_tool_boot_cfg = block->available;
+
+ /* set up flash access, and enter the bootloader mode */
+ retval = syna_tcm_romboot_preparation(tcm_dev, &romboot_data, true);
+ if (retval < 0) {
+ LOGE("Fail to do preparation\n");
+ goto reset;
+ }
+
+ if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) {
+ LOGE("Incorrect device mode 0x%02x, expected:0x%02x\n",
+ tcm_dev->dev_mode, MODE_ROMBOOTLOADER);
+ retval = _EINVAL;
+ goto reset;
+ }
+
+ LOGN("Start of reflash\n");
+
+ ATOMIC_SET(tcm_dev->firmware_flashing, 1);
+
+ /* Traverse through all blocks in the image file,
+ * then erase the corresponding block area
+ */
+ for (idx = 0; idx < AREA_MAX; idx++) {
+
+ block = &romboot_data.image_info.data[idx];
+
+ if (!block->available)
+ continue;
+
+ if (idx == AREA_ROMBOOT_APP_CODE)
+ continue;
+
+ if ((idx == AREA_BOOT_CONFIG) && has_tool_boot_cfg)
+ continue;
+
+ LOGD("Erase %s block - address: 0x%x (%d), size: %d\n",
+ AREA_ID_STR(block->id), block->flash_addr,
+ block->flash_addr, block->size);
+
+ if (block->size == 0)
+ continue;
+
+ if (erase_delay_ms == DEFAULT_FLASH_ERASE_DELAY)
+ erase_delay_ms = ROMBOOT_DELAY_MS;
+
+ retval = syna_tcm_romboot_erase_flash(tcm_dev,
+ &romboot_data,
+ block,
+ erase_delay_ms,
+ true);
+ if (retval < 0) {
+ LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id));
+ goto reset;
+ }
+
+ LOGN("%s partition erased\n", AREA_ID_STR(block->id));
+ }
+
+ /* Traverse through all blocks in the image file,
+ * and then update the corresponding block area
+ */
+ for (idx = 0; idx < AREA_MAX; idx++) {
+
+ LOGD("Prepare to update %s partition\n", AREA_ID_STR(idx));
+
+ block = &romboot_data.image_info.data[idx];
+
+ if (!block->available)
+ continue;
+
+ if (idx == AREA_ROMBOOT_APP_CODE)
+ continue;
+
+ if ((idx == AREA_BOOT_CONFIG) && has_tool_boot_cfg)
+ continue;
+
+ LOGD("Write data to %s - address: 0x%x (%d), size: %d\n",
+ AREA_ID_STR(block->id), block->flash_addr,
+ block->flash_addr, block->size);
+
+ if (block->size == 0)
+ continue;
+
+ if (wr_delay_ms == DEFAULT_FLASH_WRITE_DELAY)
+ wr_delay_ms = ROMBOOT_DELAY_MS;
+
+ retval = syna_tcm_romboot_write_flash(tcm_dev,
+ &romboot_data,
+ block,
+ wr_delay_ms,
+ true);
+
+ if (retval < 0) {
+ LOGE("Fail to update %s partition, size: %d\n",
+ AREA_ID_STR(block->id), block->size);
+ goto reset;
+ }
+
+ LOGN("%s written\n", AREA_ID_STR(block->id));
+ }
+
+ LOGN("End of reflash\n");
+
+ retval = 0;
+reset:
+ retval = syna_tcm_reset(tcm_dev);
+ if (retval < 0) {
+ LOGE("Fail to do reset\n");
+ goto exit;
+ }
+
+exit:
+ ATOMIC_SET(tcm_dev->firmware_flashing, 0);
+
+ syna_tcm_buf_release(&romboot_data.out);
+
+ return retval;
+}
+
+
+/**
+ * syna_tcm_get_romboot_info()
+ *
+ * Implement the bootloader command code, which is used to request a
+ * RomBoot information packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] rom_boot_info: the romboot info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_romboot_info(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_info *rom_boot_info)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int copy_size = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_ROMBOOT_INFO,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to send command 0x%02x\n",
+ CMD_GET_ROMBOOT_INFO);
+ goto exit;
+ }
+
+ if (rom_boot_info == NULL)
+ goto exit;
+
+ copy_size = MIN(sizeof(struct tcm_romboot_info),
+ tcm_dev->resp_buf.data_length);
+
+ /* copy romboot_info to caller */
+ retval = syna_pal_mem_cpy((unsigned char *)rom_boot_info,
+ sizeof(struct tcm_romboot_info),
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ copy_size);
+ if (retval < 0) {
+ LOGE("Fail to copy romboot info to caller\n");
+ goto exit;
+ }
+
+exit:
+ return retval;
+}
+
+
+
diff --git a/tcm/synaptics_touchcom_func_romboot.h b/tcm/synaptics_touchcom_func_romboot.h
new file mode 100644
index 0000000..812caf9
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_romboot.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_touchcom_func_romboot.h
+ *
+ * This file declares relevant functions and structures for ROM boot-loader.
+ */
+
+#ifndef _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_
+#define _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_
+
+#include "synaptics_touchcom_core_dev.h"
+#include "synaptics_touchcom_func_base_flash.h"
+
+
+#define ROMBOOT_DELAY_MS (20)
+
+/**
+ * @section: Structure to assemble flash command
+ */
+struct flash_param {
+ union {
+ struct {
+ unsigned char byte0;
+ unsigned char byte1;
+ unsigned char byte2;
+ };
+ struct {
+ unsigned char spi_param;
+ unsigned char clk_div;
+ unsigned char mode;
+ };
+ };
+ unsigned char read_size[2];
+ unsigned char command;
+};
+
+/**
+ * @section: Specific data blob for romboot
+ *
+ * The structure contains various parameters being used in
+ * ROM boot control
+ */
+struct tcm_romboot_data_blob {
+ /* binary data to write */
+ const unsigned char *bdata;
+ unsigned int bdata_size;
+ /* parsed data based on given binary data */
+ struct ihex_info ihex_info;
+ struct image_info image_info;
+ /* standard information for flash access */
+ unsigned int page_size;
+ unsigned int write_block_size;
+ unsigned int max_write_payload_size;
+ /* temporary buffer during the reflash */
+ struct tcm_buffer out;
+};
+
+
+/**
+ * syna_tcm_romboot_do_ihex_update()
+ *
+ * The entry function to perform ihex update upon ROM Boot.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] ihex: ihex data to write
+ * [ in] ihex_size: size of ihex data
+ * [ in] flash_size: size for temporary buffer allocation, which used
+ * to re-order the flash data.
+ * in general, (ihex_size + 4K) is preferred size
+ * [ in] len_per_line: length per line in the ihex file
+ * [ in] delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * [ in] is_multichip: flag to indicate a multi-chip product used
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_romboot_do_ihex_update(struct tcm_dev *tcm_dev,
+ const unsigned char *ihex, unsigned int ihex_size,
+ unsigned int flash_size, unsigned int len_per_line,
+ unsigned int delay_ms, bool is_multichip);
+
+/**
+ * syna_tcm_romboot_do_multichip_reflash()
+ *
+ * The entry function to perform fw update with multi-chip product.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] image: image file given
+ * [ in] image_size: size of data array
+ * [ in] wait_delay_ms: a short delay time in millisecond to wait for
+ * the completion of flash access
+ * set [erase_delay_ms | write_delay_ms] for setup;
+ * set '0' to use default time;
+ * set 'FORCE_ATTN_DRIVEN' to adopt ATTN-driven.
+ * [ in] force_reflash: '1' to do reflash anyway
+ * '0' to compare ID info before doing reflash.
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_romboot_do_multichip_reflash(struct tcm_dev *tcm_dev,
+ const unsigned char *image, unsigned int image_size,
+ unsigned int wait_delay_ms, bool force_reflash);
+
+/**
+ * syna_tcm_get_romboot_info()
+ *
+ * Implement the bootloader command code, which is used to request a
+ * RomBoot information packet.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [out] rom_boot_info: the romboot info packet returned
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_romboot_info(struct tcm_dev *tcm_dev,
+ struct tcm_romboot_info *rom_boot_info);
+
+
+#endif /* end of _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ */
diff --git a/tcm/synaptics_touchcom_func_touch.c b/tcm/synaptics_touchcom_func_touch.c
new file mode 100644
index 0000000..f390d0c
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_touch.c
@@ -0,0 +1,877 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Synaptics TCM touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_tcm2_func_touch.c
+ *
+ * This file implements the touch report handling functions.
+ * The declarations are available in synaptics_touchcom_func_touch.h.
+ */
+
+#include "synaptics_touchcom_func_touch.h"
+
+/**
+ * syna_tcm_get_touch_data()
+ *
+ * Get data entity from the received report according to bit offset and bit
+ * length defined in the touch report configuration.
+ *
+ * @param
+ * [ in] report: touch report generated by TouchComm device
+ * [ in] report_size: size of given report
+ * [ in] offset: bit offset in the report
+ * [ in] bits: number of bits representing the data
+ * [out] data: data parsed
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_touch_data(const unsigned char *report,
+ unsigned int report_size, unsigned int offset,
+ unsigned int bits, unsigned int *data)
+{
+ unsigned char mask;
+ unsigned char byte_data;
+ unsigned int output_data;
+ unsigned int bit_offset;
+ unsigned int byte_offset;
+ unsigned int data_bits;
+ unsigned int available_bits;
+ unsigned int remaining_bits;
+
+ if (bits == 0 || bits > 32) {
+ LOGE("Invalid number of bits %d\n", bits);
+ return _EINVAL;
+ }
+
+ if (!report) {
+ LOGE("Invalid report data\n");
+ return _EINVAL;
+ }
+
+ if (offset + bits > report_size * 8) {
+ *data = 0;
+ return 0;
+ }
+
+ output_data = 0;
+ remaining_bits = bits;
+
+ bit_offset = offset % 8;
+ byte_offset = offset / 8;
+
+ while (remaining_bits) {
+ byte_data = report[byte_offset];
+ byte_data >>= bit_offset;
+
+ available_bits = 8 - bit_offset;
+ data_bits = MIN(available_bits, remaining_bits);
+ mask = 0xff >> (8 - data_bits);
+
+ byte_data &= mask;
+
+ output_data |= byte_data << (bits - remaining_bits);
+
+ bit_offset = 0;
+ byte_offset += 1;
+ remaining_bits -= data_bits;
+ }
+
+ *data = output_data;
+
+ return 0;
+}
+
+/**
+ * syna_tcm_get_gesture_data()
+ *
+ * The contents of the gesture data entity depend on which gesture
+ * is detected. The default size of data is defined in 16-64 bits natively.
+ *
+ * @param
+ * [ in] report: touch report generated by TouchComm device
+ * [ in] report_size: size of given report
+ * [ in] offset: bit offset in the report
+ * [ in] bits: total bits representing the gesture data
+ * [out] gesture_data: gesture data parsed
+ * [ in] gesture_id: gesture id retrieved
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+static int syna_tcm_get_gesture_data(const unsigned char *report,
+ unsigned int report_size, unsigned int offset,
+ unsigned int bits, struct tcm_gesture_data_blob *gesture_data,
+ unsigned int gesture_id)
+{
+ int retval;
+ unsigned int idx;
+ unsigned int data;
+ unsigned int size;
+ unsigned int data_end;
+
+ if (!report) {
+ LOGE("Invalid report data\n");
+ return _EINVAL;
+ }
+
+ if (offset + bits > report_size * 8)
+ return 0;
+
+ data_end = offset + bits;
+
+ size = (sizeof(gesture_data->data) / sizeof(unsigned char));
+
+ idx = 0;
+ while ((offset < data_end) && (idx < size)) {
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, 16, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object index\n");
+ return retval;
+ }
+ gesture_data->data[idx++] = (unsigned char)(data & 0xff);
+ gesture_data->data[idx++] = (unsigned char)((data >> 8) & 0xff);
+ offset += 16;
+ }
+
+ switch (gesture_id) {
+ case GESTURE_ID_DOUBLE_TAP:
+ case GESTURE_ID_ACTIVE_TAP_AND_HOLD:
+ LOGD("Tap info: (%d, %d)\n",
+ syna_pal_le2_to_uint(gesture_data->tap_x),
+ syna_pal_le2_to_uint(gesture_data->tap_y));
+ break;
+ case GESTURE_ID_SWIPE:
+ LOGD("Swipe info: direction:%x (%d, %d)\n",
+ syna_pal_le2_to_uint(gesture_data->swipe_direction),
+ syna_pal_le2_to_uint(gesture_data->tap_x),
+ syna_pal_le2_to_uint(gesture_data->tap_y));
+ break;
+ default:
+ LOGW("Unknown gesture_id:%d\n", gesture_id);
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * syna_tcm_parse_touch_report()
+ *
+ * Traverse through touch report configuration and parse the contents of
+ * report packet to get the exactly touched data entity from touch reports.
+ *
+ * At the end of function, the touched data will be parsed and stored at the
+ * associated position in structure touch_data_blob.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] report: touch report generated by TouchComm device
+ * [ in] report_size: size of given report
+ * [out] touch_data: touch data generated
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_parse_touch_report(struct tcm_dev *tcm_dev,
+ unsigned char *report, unsigned int report_size,
+ struct tcm_touch_data_blob *touch_data)
+{
+ int retval;
+ bool active_only;
+ bool num_of_active_objects;
+ unsigned char code;
+ unsigned int size;
+ unsigned int idx;
+ unsigned int obj;
+ unsigned int next;
+ unsigned int data;
+ unsigned int bits;
+ unsigned int offset;
+ unsigned int objects;
+ unsigned int active_objects;
+ unsigned int config_size;
+ unsigned char *config_data;
+ struct tcm_objects_data_blob *object_data;
+ static unsigned int end_of_foreach;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (!report) {
+ LOGE("Invalid report data\n");
+ return _EINVAL;
+ }
+
+ if (report_size <= 0) {
+ LOGE("Invalid report data length\n");
+ return _EINVAL;
+ }
+
+ if (!touch_data) {
+ LOGE("Invalid touch data structure\n");
+ return _EINVAL;
+ }
+
+ if (tcm_dev->max_objects == 0) {
+ LOGE("Invalid max_objects supported\n");
+ return _EINVAL;
+ }
+
+ object_data = touch_data->object_data;
+
+ if (!object_data) {
+ LOGE("Invalid object_data\n");
+ return _EINVAL;
+ }
+
+ config_data = tcm_dev->touch_config.buf;
+ config_size = tcm_dev->touch_config.data_length;
+
+ if ((!config_data) || (config_size == 0)) {
+ LOGE("Invalid config_data\n");
+ return _EINVAL;
+ }
+
+ size = sizeof(touch_data->object_data);
+ syna_pal_mem_set(touch_data->object_data, 0x00, size);
+
+ num_of_active_objects = false;
+
+ idx = 0;
+ offset = 0;
+ objects = 0;
+ active_objects = 0;
+ active_only = false;
+ obj = 0;
+ next = 0;
+
+ while (idx < config_size) {
+ code = config_data[idx++];
+ switch (code) {
+ case TOUCH_REPORT_END:
+ goto exit;
+ case TOUCH_REPORT_FOREACH_ACTIVE_OBJECT:
+ obj = 0;
+ next = idx;
+ active_only = true;
+ break;
+ case TOUCH_REPORT_FOREACH_OBJECT:
+ obj = 0;
+ next = idx;
+ active_only = false;
+ break;
+ case TOUCH_REPORT_FOREACH_END:
+ end_of_foreach = idx;
+ if (active_only) {
+ if (num_of_active_objects) {
+ objects++;
+ obj++;
+ if (objects < active_objects)
+ idx = next;
+ } else if (offset < report_size * 8) {
+ obj++;
+ idx = next;
+ }
+ } else {
+ obj++;
+ if (obj < tcm_dev->max_objects)
+ idx = next;
+ }
+ break;
+ case TOUCH_REPORT_PAD_TO_NEXT_BYTE:
+ offset = syna_pal_ceil_div(offset, 8) * 8;
+ break;
+ case TOUCH_REPORT_TIMESTAMP:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get time-stamp\n");
+ return retval;
+ }
+ touch_data->timestamp = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_INDEX:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object index\n");
+ return retval;
+ }
+ obj = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_CLASSIFICATION:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object classification\n");
+ return retval;
+ }
+ object_data[obj].status = (unsigned char)data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_X_POSITION:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object x position\n");
+ return retval;
+ }
+ object_data[obj].x_pos = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_Y_POSITION:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object y position\n");
+ return retval;
+ }
+ object_data[obj].y_pos = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_Z:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object z\n");
+ return retval;
+ }
+ object_data[obj].z = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_X_WIDTH:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object x width\n");
+ return retval;
+ }
+ object_data[obj].x_width = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_Y_WIDTH:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object y width\n");
+ return retval;
+ }
+ object_data[obj].y_width = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_TX_POSITION_TIXELS:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object tx position\n");
+ return retval;
+ }
+ object_data[obj].tx_pos = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_OBJECT_N_RX_POSITION_TIXELS:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get object rx position\n");
+ return retval;
+ }
+ object_data[obj].rx_pos = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get number of active objects\n");
+ return retval;
+ }
+ active_objects = data;
+ num_of_active_objects = true;
+ touch_data->num_of_active_objects = data;
+ offset += bits;
+ if (touch_data->num_of_active_objects == 0) {
+ if (end_of_foreach == 0) {
+ LOGE("Invalid end_foreach\n");
+ return 0;
+ }
+ idx = end_of_foreach;
+ }
+ break;
+ case TOUCH_REPORT_0D_BUTTONS_STATE:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get 0D buttons state\n");
+ return retval;
+ }
+ touch_data->buttons_state = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_GESTURE_ID:
+ if (tcm_dev->custom_gesture_parse_func) {
+ retval = tcm_dev->custom_gesture_parse_func(
+ TOUCH_REPORT_GESTURE_ID, config_data,
+ &idx, report, &offset, report_size,
+ tcm_dev->cbdata_gesture_parse);
+ } else {
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report,
+ report_size, offset, bits, &data);
+ touch_data->gesture_id = data;
+ offset += bits;
+ }
+ if (retval < 0) {
+ LOGE("Fail to get gesture id\n");
+ return retval;
+ }
+ break;
+ case TOUCH_REPORT_GESTURE_DATA:
+ if (tcm_dev->custom_gesture_parse_func) {
+ retval = tcm_dev->custom_gesture_parse_func(
+ TOUCH_REPORT_GESTURE_DATA, config_data,
+ &idx, report, &offset, report_size,
+ tcm_dev->cbdata_gesture_parse);
+ } else {
+ bits = config_data[idx++];
+ retval = syna_tcm_get_gesture_data(report,
+ report_size,
+ offset, bits,
+ &touch_data->gesture_data,
+ touch_data->gesture_id);
+ offset += bits;
+ }
+ if (retval < 0) {
+ LOGE("Fail to get gesture data\n");
+ return retval;
+ }
+ break;
+ case TOUCH_REPORT_FRAME_RATE:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get frame rate\n");
+ return retval;
+ }
+ touch_data->frame_rate = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_FORCE_MEASUREMENT:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get force measurement data\n");
+ return retval;
+ }
+ touch_data->force_data = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_FINGERPRINT_AREA_MEET:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get data for fingerprint area\n");
+ return retval;
+ }
+ touch_data->fingerprint_area_meet = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_POWER_IM:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get power IM\n");
+ return retval;
+ }
+ touch_data->power_im = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_CID_IM:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get CID IM\n");
+ return retval;
+ }
+ touch_data->cid_im = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_RAIL_IM:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get rail IM\n");
+ return retval;
+ }
+ touch_data->rail_im = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_CID_VARIANCE_IM:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get CID variance IM\n");
+ return retval;
+ }
+ touch_data->cid_variance_im = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_NSM_FREQUENCY_INDEX:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get NSM frequency\n");
+ return retval;
+ }
+ touch_data->nsm_frequency = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_NSM_STATE:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get NSM state\n");
+ return retval;
+ }
+ touch_data->nsm_state = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_CPU_CYCLES_USED_SINCE_LAST_FRAME:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get cpu cycles info\n");
+ return retval;
+ }
+ touch_data->num_of_cpu_cycles = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_FACE_DETECT:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to detect face\n");
+ return retval;
+ }
+ touch_data->fd_data = data;
+ offset += bits;
+ break;
+ case TOUCH_REPORT_SENSING_MODE:
+ bits = config_data[idx++];
+ retval = syna_tcm_get_touch_data(report, report_size,
+ offset, bits, &data);
+ if (retval < 0) {
+ LOGE("Fail to get sensing mode\n");
+ return retval;
+ }
+ touch_data->sensing_mode = data;
+ offset += bits;
+ break;
+ default:
+ /* use custom parsing method, if registered */
+ if (tcm_dev->custom_touch_data_parse_func) {
+ retval = tcm_dev->custom_touch_data_parse_func(
+ code, config_data, &idx, report,
+ &offset, report_size,
+ tcm_dev->cbdata_touch_data_parse);
+ if (retval >= 0)
+ continue;
+ }
+
+ LOGW("Unknown touch config code:0x%02x (length:%d)\n",
+ code, config_data[idx]);
+ bits = config_data[idx++];
+ offset += bits;
+ break;
+ }
+ }
+
+exit:
+ return 0;
+}
+
+/**
+ * syna_tcm_set_touch_report_config()
+ *
+ * Setup the format and content of touch report if needed.
+ *
+ * TouchComm allows to set how touch reports are formatted and what items get
+ * reported each time a touch report is generated.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] config: the customized report configuration
+ * [ in] config_size: size of given config
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_touch_report_config(struct tcm_dev *tcm_dev,
+ unsigned char *config, unsigned int config_size)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int size;
+ struct tcm_application_info *app_info;
+ unsigned char *data;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if ((!config) || (config_size == 0)) {
+ LOGE("Invalid given config data\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Not in application fw mode, mode: %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ app_info = &tcm_dev->app_info;
+ size = syna_pal_le2_to_uint(app_info->max_touch_report_config_size);
+
+ if (config_size > size) {
+ LOGE("Invalid config size: %d (max: %d)\n", config_size, size);
+ return _EINVAL;
+ }
+
+ data = syna_pal_mem_alloc(size, sizeof(unsigned char));
+ if (!data) {
+ LOGE("Fail to allocate memory for touch config setting\n");
+ return _ENOMEM;
+ }
+
+ retval = syna_pal_mem_cpy(data,
+ size,
+ config,
+ config_size,
+ config_size);
+ if (retval < 0) {
+ LOGE("Fail to copy custom touch config\n");
+ goto exit;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_SET_TOUCH_REPORT_CONFIG,
+ data,
+ size,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to write command CMD_SET_TOUCH_REPORT_CONFIG\n");
+ goto exit;
+ }
+
+ LOGI("Set touch config done\n");
+
+exit:
+ if (data)
+ syna_pal_mem_free((void *)data);
+
+ return retval;
+}
+
+/**
+ * syna_tcm_preserve_touch_report_config()
+ *
+ * Retrieve and preserve the current touch report configuration.
+ *
+ * The retrieved configuration is stored in touch_config buffer defined
+ * in structure syna_tcm_dev for later using of touch position parsing.
+ *
+ * The touch_config buffer will be allocated internally and its size will
+ * be updated accordingly.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_preserve_touch_report_config(struct tcm_dev *tcm_dev)
+{
+ int retval = 0;
+ unsigned char resp_code;
+ unsigned int size = 0;
+
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) {
+ LOGE("Not in application fw mode, mode: %d\n",
+ tcm_dev->dev_mode);
+ return _EINVAL;
+ }
+
+ retval = tcm_dev->write_message(tcm_dev,
+ CMD_GET_TOUCH_REPORT_CONFIG,
+ NULL,
+ 0,
+ &resp_code,
+ tcm_dev->msg_data.default_resp_reading);
+ if (retval < 0) {
+ LOGE("Fail to write command CMD_GET_TOUCH_REPORT_CONFIG\n");
+ goto exit;
+ }
+
+ syna_tcm_buf_lock(&tcm_dev->resp_buf);
+
+ size = tcm_dev->resp_buf.data_length;
+ retval = syna_tcm_buf_alloc(&tcm_dev->touch_config,
+ size);
+ if (retval < 0) {
+ LOGE("Fail to allocate memory for internal touch_config\n");
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ goto exit;
+ }
+
+ syna_tcm_buf_lock(&tcm_dev->touch_config);
+
+ retval = syna_pal_mem_cpy(tcm_dev->touch_config.buf,
+ tcm_dev->touch_config.buf_size,
+ tcm_dev->resp_buf.buf,
+ tcm_dev->resp_buf.buf_size,
+ size);
+ if (retval < 0) {
+ LOGE("Fail to clone touch config\n");
+ syna_tcm_buf_unlock(&tcm_dev->touch_config);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+ goto exit;
+ }
+
+ tcm_dev->touch_config.data_length = size;
+
+ syna_tcm_buf_unlock(&tcm_dev->touch_config);
+ syna_tcm_buf_unlock(&tcm_dev->resp_buf);
+
+exit:
+ return retval;
+}
+
+/**
+ * syna_tcm_set_custom_touch_data_parsing_callback()
+ *
+ * Set up callback function to handle custom touch data.
+ *
+ * Once finding the "new" custom entity in touch report, the core library will
+ * invoke the custom parsing method to handle this "new" code entity.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] p_cb: the callback function pointer
+ * [ in] p_cbdata: pointer to caller data passed to callback function
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_custom_touch_data_parsing_callback(struct tcm_dev *tcm_dev,
+ tcm_touch_data_parse_callback_t p_cb, void *p_cbdata)
+{
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_dev->custom_touch_data_parse_func = p_cb;
+ tcm_dev->cbdata_touch_data_parse = p_cbdata;
+
+ LOGI("enabled\n");
+
+ return 0;
+}
+
+/**
+ * syna_tcm_set_custom_gesture_parsing_callback()
+ *
+ * Set up callback function to handle the gesture data defined as the following
+ * code entities
+ * - TOUCH_REPORT_GESTURE_ID
+ * - TOUCH_REPORT_GESTURE_DATA
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] p_cb: the callback function pointer
+ * [ in] p_cbdata: pointer to caller data passed to callback function
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_custom_gesture_parsing_callback(struct tcm_dev *tcm_dev,
+ tcm_gesture_parse_callback_t p_cb, void *p_cbdata)
+{
+ if (!tcm_dev) {
+ LOGE("Invalid tcm device handle\n");
+ return _EINVAL;
+ }
+
+ tcm_dev->custom_gesture_parse_func = p_cb;
+ tcm_dev->cbdata_gesture_parse = p_cbdata;
+
+ LOGI("enabled\n");
+
+ return 0;
+}
+
diff --git a/tcm/synaptics_touchcom_func_touch.h b/tcm/synaptics_touchcom_func_touch.h
new file mode 100644
index 0000000..eb63c05
--- /dev/null
+++ b/tcm/synaptics_touchcom_func_touch.h
@@ -0,0 +1,235 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Synaptics TouchCom touchscreen driver
+ *
+ * Copyright (C) 2017-2020 Synaptics Incorporated. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+/**
+ * @file synaptics_tcm2_func_touch.h
+ *
+ * This file declares related APIs and definitions for touch report handling.
+ */
+
+#ifndef _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_
+#define _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_
+
+#include "synaptics_touchcom_core_dev.h"
+
+/**
+ * @section: Types of Object Reported
+ *
+ * List the object classifications
+ */
+enum object_classification {
+ LIFT = 0,
+ FINGER = 1,
+ GLOVED_OBJECT = 2,
+ STYLUS = 3,
+ ERASER = 4,
+ SMALL_OBJECT = 5,
+ PALM = 6,
+ EDGE_TOUCHED = 8,
+ HOVER_OBJECT = 9,
+ NOP = -1,
+};
+
+/**
+ * @section: Types of Gesture ID
+ *
+ * List the gesture ID assigned
+ */
+enum gesture_classification {
+ GESTURE_ID_NONE = 0,
+ GESTURE_ID_DOUBLE_TAP = 1,
+ GESTURE_ID_SWIPE = 2,
+ GESTURE_ID_ACTIVE_TAP_AND_HOLD = 7,
+};
+
+/**
+ * @section: Codes for Touch Report Configuration
+ *
+ * Define the 8-bit codes for the touch report configuration
+ */
+enum touch_report_code {
+ /* control flow codes */
+ TOUCH_REPORT_END = 0x00,
+ TOUCH_REPORT_FOREACH_ACTIVE_OBJECT = 0x01,
+ TOUCH_REPORT_FOREACH_OBJECT = 0x02,
+ TOUCH_REPORT_FOREACH_END = 0x03,
+ TOUCH_REPORT_PAD_TO_NEXT_BYTE = 0x04,
+ /* entity codes */
+ TOUCH_REPORT_TIMESTAMP = 0x05,
+ TOUCH_REPORT_OBJECT_N_INDEX = 0x06,
+ TOUCH_REPORT_OBJECT_N_CLASSIFICATION = 0x07,
+ TOUCH_REPORT_OBJECT_N_X_POSITION = 0x08,
+ TOUCH_REPORT_OBJECT_N_Y_POSITION = 0x09,
+ TOUCH_REPORT_OBJECT_N_Z = 0x0a,
+ TOUCH_REPORT_OBJECT_N_X_WIDTH = 0x0b,
+ TOUCH_REPORT_OBJECT_N_Y_WIDTH = 0x0c,
+ TOUCH_REPORT_OBJECT_N_TX_POSITION_TIXELS = 0x0d,
+ TOUCH_REPORT_OBJECT_N_RX_POSITION_TIXELS = 0x0e,
+ TOUCH_REPORT_0D_BUTTONS_STATE = 0x0f,
+ TOUCH_REPORT_GESTURE_ID = 0x10,
+ TOUCH_REPORT_FRAME_RATE = 0x11,
+ TOUCH_REPORT_POWER_IM = 0x12,
+ TOUCH_REPORT_CID_IM = 0x13,
+ TOUCH_REPORT_RAIL_IM = 0x14,
+ TOUCH_REPORT_CID_VARIANCE_IM = 0x15,
+ TOUCH_REPORT_NSM_FREQUENCY_INDEX = 0x16,
+ TOUCH_REPORT_NSM_STATE = 0x17,
+ TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS = 0x18,
+ TOUCH_REPORT_CPU_CYCLES_USED_SINCE_LAST_FRAME = 0x19,
+ TOUCH_REPORT_FACE_DETECT = 0x1a,
+ TOUCH_REPORT_GESTURE_DATA = 0x1b,
+ TOUCH_REPORT_FORCE_MEASUREMENT = 0x1c,
+ TOUCH_REPORT_FINGERPRINT_AREA_MEET = 0x1d,
+ TOUCH_REPORT_SENSING_MODE = 0x1e,
+ TOUCH_REPORT_KNOB_DATA = 0x24,
+};
+
+/**
+ * syna_tcm_parse_touch_report()
+ *
+ * Traverse through touch report configuration and parse the contents of
+ * report packet to get the exactly touched data entity from touch reports.
+ *
+ * At the end of function, the touched data will be parsed and stored at the
+ * associated position in struct touch_data_blob.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] report: touch report generated by TouchComm device
+ * [ in] report_size: size of given report
+ * [out] touch_data: touch data generated
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_parse_touch_report(struct tcm_dev *tcm_dev,
+ unsigned char *report, unsigned int report_size,
+ struct tcm_touch_data_blob *touch_data);
+
+/**
+ * syna_tcm_set_touch_report_config()
+ *
+ * Setup the format and content of touch report if needed
+ *
+ * TouchComm allows to set how touch reports are formatted and what items get
+ * reported each time a touch report is generated.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] config: the customized report configuration
+ * [ in] config_size: size of given config
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_touch_report_config(struct tcm_dev *tcm_dev,
+ unsigned char *config, unsigned int config_size);
+
+/**
+ * syna_tcm_preserve_touch_report_config()
+ *
+ * Retrieve and preserve the current touch report configuration.
+ *
+ * The retrieved configuration is stored in touch_config buffer defined
+ * in struct syna_tcm_dev for later using of touch position parsing.
+ *
+ * The touch_config buffer will be allocated internally and its size will
+ * be updated accordingly.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_preserve_touch_report_config(struct tcm_dev *tcm_dev);
+
+
+/**
+ * syna_tcm_get_touch_data()
+ *
+ * Get data entity from the received report according to bit offset and bit
+ * length defined in the touch report configuration.
+ *
+ * @param
+ * [ in] report: touch report generated by TouchComm device
+ * [ in] report_size: size of given report
+ * [ in] offset: bit offset in the report
+ * [ in] bits: number of bits representing the data
+ * [out] data: data parsed
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_get_touch_data(const unsigned char *report,
+ unsigned int report_size, unsigned int offset,
+ unsigned int bits, unsigned int *data);
+
+/**
+ * syna_tcm_set_custom_touch_data_parsing_callback()
+ *
+ * Set up callback function to handle custom touch data.
+ *
+ * Once finding the "new" custom entity in touch report, the core library will
+ * invoke the custom parsing method to handle this "new" code entity.
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] p_cb: the callback function pointer
+ * [ in] p_cbdata: pointer to caller data passed to callback function
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_custom_touch_data_parsing_callback(struct tcm_dev *tcm_dev,
+ tcm_touch_data_parse_callback_t p_cb, void *p_cbdata);
+
+/**
+ * syna_tcm_set_custom_gesture_parsing_callback()
+ *
+ * Set up callback function to handle the gesture data defined as the following
+ * code entities
+ * - TOUCH_REPORT_GESTURE_ID
+ * - TOUCH_REPORT_GESTURE_DATA
+ *
+ * @param
+ * [ in] tcm_dev: the device handle
+ * [ in] p_cb: the callback function pointer
+ * [ in] p_cbdata: pointer to caller data passed to callback function
+ *
+ * @return
+ * on success, 0 or positive value; otherwise, negative value on error.
+ */
+int syna_tcm_set_custom_gesture_parsing_callback(struct tcm_dev *tcm_dev,
+ tcm_gesture_parse_callback_t p_cb, void *p_cbdata);
+
+
+#endif /* end of _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ */