/* * cyttsp5_core.c * Parade TrueTouch(TM) Standard Product V5 Core Module. * For use with Parade touchscreen controllers. * Supported parts include: * CYTMA5XX * CYTMA448 * CYTMA445A * CYTT21XXX * CYTT31XXX * * Copyright (C) 2015 Parade Technologies * Copyright (C) 2012-2015 Cypress Semiconductor * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * version 2, and only version 2, as published by the * Free Software Foundation. * * 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. * * Contact Parade Technologies at www.paradetech.com * */ #include "cyttsp5_regs.h" #include #include #include #include static struct cyttsp5_core_data *priv_data; #define CY_CORE_STARTUP_RETRY_COUNT 3 MODULE_FIRMWARE(CY_FW_FILE_NAME); static const char *cy_driver_core_name = CYTTSP5_CORE_NAME; static const char *cy_driver_core_version = CY_DRIVER_VERSION; static const char *cy_driver_core_date = CY_DRIVER_DATE; static bool cyttsp5_first_probe = true; static bool is_cyttsp5_probe_success; static const struct cyttsp5_bus_ops *cyttsp5_bus_ops_save; struct cyttsp5_hid_field { int report_count; int report_size; int size; /* report_count * report_size */ int offset; int data_type; int logical_min; int logical_max; /* Usage Page (Hi 16 bit) + Usage (Lo 16 bit) */ u32 usage_page; u32 collection_usage_pages[CY_HID_MAX_COLLECTIONS]; struct cyttsp5_hid_report *report; bool record_field; }; struct cyttsp5_hid_report { u8 id; u8 type; int size; struct cyttsp5_hid_field *fields[CY_HID_MAX_FIELDS]; int num_fields; int record_field_index; int header_size; int record_size; u32 usage_page; }; struct atten_node { struct list_head node; char *id; struct device *dev; int (*func)(struct device *); int mode; }; struct param_node { struct list_head node; u8 id; u32 value; u8 size; }; struct module_node { struct list_head node; struct cyttsp5_module *module; void *data; }; struct cyttsp5_hid_cmd { u8 opcode; u8 report_type; union { u8 report_id; u8 power_state; }; u8 has_data_register; size_t write_length; u8 *write_buf; u8 *read_buf; u8 wait_interrupt; u8 reset_cmd; u16 timeout_ms; }; struct cyttsp5_hid_output { u8 cmd_type; u16 length; u8 command_code; size_t write_length; u8 *write_buf; u8 novalidate; u8 reset_expected; u16 timeout_ms; }; #define SET_CMD_OPCODE(byte, opcode) SET_CMD_LOW(byte, opcode) #define SET_CMD_REPORT_TYPE(byte, type) SET_CMD_HIGH(byte, ((type) << 4)) #define SET_CMD_REPORT_ID(byte, id) SET_CMD_LOW(byte, id) #define HID_OUTPUT_APP_COMMAND(command) \ .cmd_type = HID_OUTPUT_CMD_APP, \ .command_code = command #define HID_OUTPUT_BL_COMMAND(command) \ .cmd_type = HID_OUTPUT_CMD_BL, \ .command_code = command #ifdef VERBOSE_DEBUG void cyttsp5_pr_buf(struct device *dev, u8 *dptr, int size, const char *data_name) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); u8 *pr_buf = cd->pr_buf; int i, k; const char fmt[] = "%02X "; int max; if (!size) return; max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); pr_buf[0] = 0; for (i = k = 0; i < size && k < max; i++, k += 3) scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); if (size) { parade_debug(dev, DEBUG_LEVEL_2, "%s: %s[0..%d]=%s%s\n", __func__, data_name, size - 1, pr_buf, size <= max ? "" : CY_PR_TRUNCATED); } else { parade_debug(dev, DEBUG_LEVEL_2, "%s: %s[]\n", __func__, data_name); } } EXPORT_SYMBOL_GPL(cyttsp5_pr_buf); #endif #ifdef TTHE_TUNER_SUPPORT static int tthe_print(struct cyttsp5_core_data *cd, u8 *buf, int buf_len, const u8 *data_name) { int len = strlen(data_name); int i, n; u8 *p; int remain; u8 data_name_with_time_stamp[100]; if (cd->show_timestamp) { sprintf(data_name_with_time_stamp, "[%u] %s", jiffies_to_msecs(jiffies), data_name); data_name = data_name_with_time_stamp; len = strlen(data_name); } mutex_lock(&cd->tthe_lock); if (!cd->tthe_buf) goto exit; if (cd->tthe_buf_len + (len + buf_len) > CY_MAX_PRBUF_SIZE) goto exit; if (len + buf_len == 0) goto exit; remain = CY_MAX_PRBUF_SIZE - cd->tthe_buf_len; if (remain < len) len = remain; p = cd->tthe_buf + cd->tthe_buf_len; memcpy(p, data_name, len); cd->tthe_buf_len += len; p += len; remain -= len; *p = 0; for (i = 0; i < buf_len; i++) { n = scnprintf(p, remain, "%02X ", buf[i]); if (!n) break; p += n; remain -= n; cd->tthe_buf_len += n; } n = scnprintf(p, remain, "\n"); if (!n) cd->tthe_buf[cd->tthe_buf_len] = 0; cd->tthe_buf_len += n; wake_up(&cd->wait_q); exit: mutex_unlock(&cd->tthe_lock); return 0; } static int _cyttsp5_request_tthe_print(struct device *dev, u8 *buf, int buf_len, const u8 *data_name) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); return tthe_print(cd, buf, buf_len, data_name); } #endif #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE static void watchdog_delay_enable(struct work_struct *work) { struct cyttsp5_core_data *cd; cd = container_of(work, struct cyttsp5_core_data, watchdog_enable_work.work); cd->watchdog_interval = CY_WATCHDOG_TIMEOUT; printk("++++++watchdog_delay_enable\n"); } #endif /* * cyttsp5_platform_detect_read() * * This function is passed to platform detect * function to perform a read operation */ static int cyttsp5_platform_detect_read(struct device *dev, void *buf, int size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); return cyttsp5_adap_read_default(cd, buf, size); } /* Must be called with cd->hid_report_lock acquired */ static struct cyttsp5_hid_report *cyttsp5_get_hid_report_( struct cyttsp5_core_data *cd, u8 report_type, u8 report_id, bool create) { struct cyttsp5_hid_report *report = NULL; int i; /* Look for created reports */ for (i = 0; i < cd->num_hid_reports; i++) { if (cd->hid_reports[i]->type == report_type && cd->hid_reports[i]->id == report_id) { return cd->hid_reports[i]; } } /* Create a new report */ if (create && cd->num_hid_reports < CY_HID_MAX_REPORTS) { report = kzalloc(sizeof(struct cyttsp5_hid_report), GFP_KERNEL); if (!report) return NULL; report->type = report_type; report->id = report_id; cd->hid_reports[cd->num_hid_reports++] = report; } return report; } /* Must be called with cd->hid_report_lock acquired */ static void cyttsp5_free_hid_reports_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_report *report; int i, j; for (i = 0; i < cd->num_hid_reports; i++) { report = cd->hid_reports[i]; for (j = 0; j < report->num_fields; j++) kfree(report->fields[j]); kfree(report); cd->hid_reports[i] = NULL; } cd->num_hid_reports = 0; } static void cyttsp5_free_hid_reports(struct cyttsp5_core_data *cd) { mutex_lock(&cd->hid_report_lock); cyttsp5_free_hid_reports_(cd); mutex_unlock(&cd->hid_report_lock); } /* Must be called with cd->hid_report_lock acquired */ static struct cyttsp5_hid_field *cyttsp5_create_hid_field_( struct cyttsp5_hid_report *report) { struct cyttsp5_hid_field *field; if (!report) return NULL; if (report->num_fields == CY_HID_MAX_FIELDS) return NULL; field = kzalloc(sizeof(struct cyttsp5_hid_field), GFP_KERNEL); if (!field) return NULL; field->report = report; report->fields[report->num_fields++] = field; return field; } static int cyttsp5_add_parameter(struct cyttsp5_core_data *cd, u8 param_id, u32 param_value, u8 param_size) { struct param_node *param, *param_new; /* Check if parameter exists */ spin_lock(&cd->spinlock); list_for_each_entry(param, &cd->param_list, node) { if (param->id == param_id) { /* Update parameter */ param->value = param_value; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Update parameter id:%d value:%d size:%d\n", __func__, param_id, param_value, param_size); goto exit_unlock; } } spin_unlock(&cd->spinlock); param_new = kzalloc(sizeof(*param_new), GFP_KERNEL); if (!param_new) return -ENOMEM; param_new->id = param_id; param_new->value = param_value; param_new->size = param_size; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Add parameter id:%d value:%d size:%d\n", __func__, param_id, param_value, param_size); spin_lock(&cd->spinlock); list_add(¶m_new->node, &cd->param_list); exit_unlock: spin_unlock(&cd->spinlock); return 0; } int request_exclusive(struct cyttsp5_core_data *cd, void *ownptr, int timeout_ms) { int t = msecs_to_jiffies(timeout_ms); bool with_timeout = (timeout_ms != 0); mutex_lock(&cd->system_lock); if (!cd->exclusive_dev && cd->exclusive_waits == 0) { cd->exclusive_dev = ownptr; goto exit; } cd->exclusive_waits++; wait: mutex_unlock(&cd->system_lock); if (with_timeout) { t = wait_event_timeout(cd->wait_q, !cd->exclusive_dev, t); if (IS_TMO(t)) { dev_err(cd->dev, "%s: tmo waiting exclusive access\n", __func__); return -ETIME; } } else { wait_event(cd->wait_q, !cd->exclusive_dev); } mutex_lock(&cd->system_lock); if (cd->exclusive_dev) goto wait; cd->exclusive_dev = ownptr; cd->exclusive_waits--; exit: mutex_unlock(&cd->system_lock); parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: request_exclusive ok=%p\n", __func__, ownptr); return 0; } static int release_exclusive_(struct cyttsp5_core_data *cd, void *ownptr) { if (cd->exclusive_dev != ownptr) return -EINVAL; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: exclusive_dev %p freed\n", __func__, cd->exclusive_dev); cd->exclusive_dev = NULL; wake_up(&cd->wait_q); return 0; } /* * returns error if was not owned */ int release_exclusive(struct cyttsp5_core_data *cd, void *ownptr) { int rc; mutex_lock(&cd->system_lock); rc = release_exclusive_(cd, ownptr); mutex_unlock(&cd->system_lock); return rc; } static int cyttsp5_hid_exec_cmd_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_cmd *hid_cmd) { int rc; u8 *cmd; u8 cmd_length; u8 cmd_offset = 0; cmd_length = 2 /* command register */ + 2 /* command */ + (hid_cmd->report_id >= 0XF ? 1 : 0) /* Report ID */ + (hid_cmd->has_data_register ? 2 : 0) /* Data register */ + hid_cmd->write_length; /* Data length */ cmd = kzalloc(cmd_length, GFP_KERNEL); if (!cmd) return -ENOMEM; /* Set Command register */ memcpy(&cmd[cmd_offset], &cd->hid_desc.command_register, sizeof(cd->hid_desc.command_register)); cmd_offset += sizeof(cd->hid_desc.command_register); /* Set Command */ SET_CMD_REPORT_TYPE(cmd[cmd_offset], hid_cmd->report_type); if (hid_cmd->report_id >= 0XF) SET_CMD_REPORT_ID(cmd[cmd_offset], 0xF); else SET_CMD_REPORT_ID(cmd[cmd_offset], hid_cmd->report_id); cmd_offset++; SET_CMD_OPCODE(cmd[cmd_offset], hid_cmd->opcode); cmd_offset++; if (hid_cmd->report_id >= 0XF) { cmd[cmd_offset] = hid_cmd->report_id; cmd_offset++; } /* Set Data register */ if (hid_cmd->has_data_register) { memcpy(&cmd[cmd_offset], &cd->hid_desc.data_register, sizeof(cd->hid_desc.data_register)); cmd_offset += sizeof(cd->hid_desc.data_register); } /* Set Data */ if (hid_cmd->write_length && hid_cmd->write_buf) { memcpy(&cmd[cmd_offset], hid_cmd->write_buf, hid_cmd->write_length); cmd_offset += hid_cmd->write_length; } rc = cyttsp5_adap_write_read_specific(cd, cmd_length, cmd, hid_cmd->read_buf); if (rc) dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__); kfree(cmd); return rc; } static int cyttsp5_hid_exec_cmd_and_wait_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_cmd *hid_cmd) { int rc; int t; u16 timeout_ms; int *cmd_state; if (hid_cmd->reset_cmd) cmd_state = &cd->hid_reset_cmd_state; else cmd_state = &cd->hid_cmd_state; if (hid_cmd->wait_interrupt) { mutex_lock(&cd->system_lock); *cmd_state = 1; mutex_unlock(&cd->system_lock); } rc = cyttsp5_hid_exec_cmd_(cd, hid_cmd); if (rc) { if (hid_cmd->wait_interrupt) goto error; goto exit; } if (!hid_cmd->wait_interrupt) goto exit; if (hid_cmd->timeout_ms) timeout_ms = hid_cmd->timeout_ms; else timeout_ms = CY_HID_RESET_TIMEOUT; //printk("****%s timeout_ms=%d, rc=%d\n", __func__, timeout_ms, rc); t = wait_event_timeout(cd->wait_q, (*cmd_state == 0), msecs_to_jiffies(timeout_ms)); if (IS_TMO(t)) { dev_err(cd->dev, "%s: HID output cmd execution timed out %d\n", __func__, timeout_ms); rc = -ETIME; goto error; } goto exit; error: mutex_lock(&cd->system_lock); *cmd_state = 0; mutex_unlock(&cd->system_lock); exit: return rc; } static int cyttsp5_hid_cmd_reset_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_cmd hid_cmd = { .opcode = HID_CMD_RESET, .wait_interrupt = 1, .reset_cmd = 1, .timeout_ms = CY_HID_RESET_TIMEOUT, }; return cyttsp5_hid_exec_cmd_and_wait_(cd, &hid_cmd); } static int cyttsp5_hid_cmd_reset(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_cmd_reset_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int cyttsp5_hid_cmd_set_power_(struct cyttsp5_core_data *cd, u8 power_state) { int rc; struct cyttsp5_hid_cmd hid_cmd = { .opcode = HID_CMD_SET_POWER, .wait_interrupt = 1, .timeout_ms = CY_HID_SET_POWER_TIMEOUT, }; hid_cmd.power_state = power_state; rc = cyttsp5_hid_exec_cmd_and_wait_(cd, &hid_cmd); if (rc) { dev_err(cd->dev, "%s: Failed to set power to state:%d\n", __func__, power_state); return rc; } /* validate */ if ((cd->response_buf[2] != HID_RESPONSE_REPORT_ID) || ((cd->response_buf[3] & 0x3) != power_state) || ((cd->response_buf[4] & 0xF) != HID_CMD_SET_POWER)) rc = -EINVAL; return rc; } static int cyttsp5_hid_cmd_set_power(struct cyttsp5_core_data *cd, u8 power_state) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_cmd_set_power_(cd, power_state); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static const u16 crc_table[16] = { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, }; static u16 _cyttsp5_compute_crc(u8 *buf, u32 size) { u16 remainder = 0xFFFF; u16 xor_mask = 0x0000; u32 index; u32 byte_value; u32 table_index; u32 crc_bit_width = sizeof(u16) * 8; /* Divide the message by polynomial, via the table. */ for (index = 0; index < size; index++) { byte_value = buf[index]; table_index = ((byte_value >> 4) & 0x0F) ^ (remainder >> (crc_bit_width - 4)); remainder = crc_table[table_index] ^ (remainder << 4); table_index = (byte_value & 0x0F) ^ (remainder >> (crc_bit_width - 4)); remainder = crc_table[table_index] ^ (remainder << 4); } /* Perform the final remainder CRC. */ return remainder ^ xor_mask; } static int cyttsp5_hid_output_validate_bl_response( struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { u16 size; u16 crc; u8 status; size = get_unaligned_le16(&cd->response_buf[0]); if (hid_output->reset_expected && !size) return 0; if (cd->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET] != HID_BL_RESPONSE_REPORT_ID) { dev_err(cd->dev, "%s: HID output response, wrong report_id\n", __func__); return -EPROTO; } if (cd->response_buf[4] != HID_OUTPUT_BL_SOP) { dev_err(cd->dev, "%s: HID output response, wrong SOP\n", __func__); return -EPROTO; } if (cd->response_buf[size - 1] != HID_OUTPUT_BL_EOP) { dev_err(cd->dev, "%s: HID output response, wrong EOP\n", __func__); return -EPROTO; } crc = _cyttsp5_compute_crc(&cd->response_buf[4], size - 7); if (cd->response_buf[size - 3] != LOW_BYTE(crc) || cd->response_buf[size - 2] != HI_BYTE(crc)) { dev_err(cd->dev, "%s: HID output response, wrong CRC 0x%X\n", __func__, crc); return -EPROTO; } status = cd->response_buf[5]; if (status) { dev_err(cd->dev, "%s: HID output response, ERROR:%d\n", __func__, status); return -EPROTO; } return 0; } static int cyttsp5_hid_output_validate_app_response( struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { int command_code; u16 size; size = get_unaligned_le16(&cd->response_buf[0]); if (hid_output->reset_expected && !size) return 0; if (cd->response_buf[HID_OUTPUT_RESPONSE_REPORT_OFFSET] != HID_APP_RESPONSE_REPORT_ID) { dev_err(cd->dev, "%s: HID output response, wrong report_id\n", __func__); return -EPROTO; } command_code = cd->response_buf[HID_OUTPUT_RESPONSE_CMD_OFFSET] & HID_OUTPUT_RESPONSE_CMD_MASK; if (command_code != hid_output->command_code) { dev_err(cd->dev, "%s: HID output response, wrong command_code:%X\n", __func__, command_code); return -EPROTO; } return 0; } static void cyttsp5_check_set_parameter(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output, bool raw) { u8 *param_buf; u32 param_value = 0; u8 param_size; u8 param_id; int i = 0; if (!(cd->cpdata->flags & CY_CORE_FLAG_RESTORE_PARAMETERS)) return; /* Check command input for Set Parameter command */ if (raw && hid_output->length >= 10 && hid_output->length <= 13 && !memcmp(&hid_output->write_buf[0], &cd->hid_desc.output_register, sizeof(cd->hid_desc.output_register)) && hid_output->write_buf[4] == HID_APP_OUTPUT_REPORT_ID && hid_output->write_buf[6] == HID_OUTPUT_SET_PARAM) param_buf = &hid_output->write_buf[7]; else if (!raw && hid_output->cmd_type == HID_OUTPUT_CMD_APP && hid_output->command_code == HID_OUTPUT_SET_PARAM && hid_output->write_length >= 3 && hid_output->write_length <= 6) param_buf = &hid_output->write_buf[0]; else return; /* Get parameter ID, size and value */ param_id = param_buf[0]; param_size = param_buf[1]; if (param_size > 4) { dev_err(cd->dev, "%s: Invalid parameter size\n", __func__); return; } param_buf = ¶m_buf[2]; while (i < param_size) param_value += *(param_buf++) << (8 * i++); /* Check command response for Set Parameter command */ if (cd->response_buf[2] != HID_APP_RESPONSE_REPORT_ID || (cd->response_buf[4] & HID_OUTPUT_CMD_MASK) != HID_OUTPUT_SET_PARAM || cd->response_buf[5] != param_id || cd->response_buf[6] != param_size) { dev_err(cd->dev, "%s: Set Parameter command not successful\n", __func__); return; } cyttsp5_add_parameter(cd, param_id, param_value, param_size); } static void cyttsp5_check_command(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output, bool raw) { cyttsp5_check_set_parameter(cd, hid_output, raw); } static int cyttsp5_hid_output_validate_response(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) return cyttsp5_hid_output_validate_bl_response(cd, hid_output); return cyttsp5_hid_output_validate_app_response(cd, hid_output); } static int cyttsp5_hid_send_output_user_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { int rc; if (!hid_output->length || !hid_output->write_buf) return -EINVAL; rc = cyttsp5_adap_write_read_specific(cd, hid_output->length, hid_output->write_buf, NULL); if (rc) dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__); return rc; } static int cyttsp5_hid_send_output_user_and_wait_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { int rc; int t; mutex_lock(&cd->system_lock); cd->hid_cmd_state = HID_OUTPUT_USER_CMD + 1; mutex_unlock(&cd->system_lock); rc = cyttsp5_hid_send_output_user_(cd, hid_output); if (rc) goto error; t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), msecs_to_jiffies(CY_HID_OUTPUT_USER_TIMEOUT)); if (IS_TMO(t)) { dev_err(cd->dev, "%s: HID output cmd execution timed out\n", __func__); rc = -ETIME; goto error; } cyttsp5_check_command(cd, hid_output, true); goto exit; error: mutex_lock(&cd->system_lock); cd->hid_cmd_state = 0; mutex_unlock(&cd->system_lock); exit: return rc; } static int cyttsp5_hid_send_output_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { int rc; u8 *cmd; u16 length; u8 report_id; u8 cmd_offset = 0; u16 crc; u8 cmd_allocated = 0; switch (hid_output->cmd_type) { case HID_OUTPUT_CMD_APP: report_id = HID_APP_OUTPUT_REPORT_ID; length = 5; break; case HID_OUTPUT_CMD_BL: report_id = HID_BL_OUTPUT_REPORT_ID; length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */; break; default: return -EINVAL; } length += hid_output->write_length; if (length + 2 > CYTTSP5_PREALLOCATED_CMD_BUFFER) { cmd = kzalloc(length + 2, GFP_KERNEL); if (!cmd) return -ENOMEM; cmd_allocated = 1; } else { cmd = cd->cmd_buf; } /* Set Output register */ memcpy(&cmd[cmd_offset], &cd->hid_desc.output_register, sizeof(cd->hid_desc.output_register)); cmd_offset += sizeof(cd->hid_desc.output_register); cmd[cmd_offset++] = LOW_BYTE(length); cmd[cmd_offset++] = HI_BYTE(length); cmd[cmd_offset++] = report_id; cmd[cmd_offset++] = 0x0; /* reserved */ if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) cmd[cmd_offset++] = HID_OUTPUT_BL_SOP; cmd[cmd_offset++] = hid_output->command_code; /* Set Data Length for bootloader */ if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) { cmd[cmd_offset++] = LOW_BYTE(hid_output->write_length); cmd[cmd_offset++] = HI_BYTE(hid_output->write_length); } /* Set Data */ if (hid_output->write_length && hid_output->write_buf) { memcpy(&cmd[cmd_offset], hid_output->write_buf, hid_output->write_length); cmd_offset += hid_output->write_length; } if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) { crc = _cyttsp5_compute_crc(&cmd[6], hid_output->write_length + 4); cmd[cmd_offset++] = LOW_BYTE(crc); cmd[cmd_offset++] = HI_BYTE(crc); cmd[cmd_offset++] = HID_OUTPUT_BL_EOP; } cyttsp5_pr_buf(cd->dev, cmd, length + 2, "command"); rc = cyttsp5_adap_write_read_specific(cd, length + 2, cmd, NULL); if (rc) dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__); if (cmd_allocated) kfree(cmd); return rc; } static int cyttsp5_hid_send_output_and_wait_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_output *hid_output) { int rc; int t; #ifdef VERBOSE_DEBUG u16 size; #endif u16 timeout_ms; mutex_lock(&cd->system_lock); cd->hid_cmd_state = hid_output->command_code + 1; mutex_unlock(&cd->system_lock); if (hid_output->timeout_ms) timeout_ms = hid_output->timeout_ms; else timeout_ms = CY_HID_OUTPUT_TIMEOUT; rc = cyttsp5_hid_send_output_(cd, hid_output); if (rc) goto error; //printk("%s time=%d\n", __func__, timeout_ms); t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), msecs_to_jiffies(timeout_ms)); if (IS_TMO(t)) { dev_err(cd->dev, "%s: HID output cmd execution timed out\n", __func__); rc = -ETIME; goto error; } if (!hid_output->novalidate) rc = cyttsp5_hid_output_validate_response(cd, hid_output); cyttsp5_check_command(cd, hid_output, false); #ifdef VERBOSE_DEBUG size = get_unaligned_le16(&cd->response_buf[0]); cyttsp5_pr_buf(cd->dev, cd->response_buf, size, "return_buf"); #endif goto exit; error: mutex_lock(&cd->system_lock); cd->hid_cmd_state = 0; mutex_unlock(&cd->system_lock); exit: return rc; } static int cyttsp5_hid_output_null_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_NULL), }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_null(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_null_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int cyttsp5_hid_output_start_bootloader_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_START_BOOTLOADER), .timeout_ms = CY_HID_OUTPUT_START_BOOTLOADER_TIMEOUT, .reset_expected = 1, }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_start_bootloader(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_start_bootloader_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_start_bl(struct device *dev, int protect) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_start_bootloader(cd); return cyttsp5_hid_output_start_bootloader_(cd); } static void cyttsp5_si_get_cydata(struct cyttsp5_core_data *cd) { struct cyttsp5_cydata *cydata = &cd->sysinfo.cydata; struct cyttsp5_cydata_dev *cydata_dev = (struct cyttsp5_cydata_dev *) &cd->response_buf[HID_SYSINFO_CYDATA_OFFSET]; cydata->pip_ver_major = cydata_dev->pip_ver_major; cydata->pip_ver_minor = cydata_dev->pip_ver_minor; cydata->bl_ver_major = cydata_dev->bl_ver_major; cydata->bl_ver_minor = cydata_dev->bl_ver_minor; cydata->fw_ver_major = cydata_dev->fw_ver_major; cydata->fw_ver_minor = cydata_dev->fw_ver_minor; cydata->fw_pid = get_unaligned_le16(&cydata_dev->fw_pid); cydata->fw_ver_conf = get_unaligned_le16(&cydata_dev->fw_ver_conf); cydata->post_code = get_unaligned_le16(&cydata_dev->post_code); cydata->revctrl = get_unaligned_le32(&cydata_dev->revctrl); cydata->jtag_id_l = get_unaligned_le16(&cydata_dev->jtag_si_id_l); cydata->jtag_id_h = get_unaligned_le16(&cydata_dev->jtag_si_id_h); memcpy(cydata->mfg_id, cydata_dev->mfg_id, CY_NUM_MFGID); cyttsp5_pr_buf(cd->dev, (u8 *)cydata_dev, sizeof(struct cyttsp5_cydata_dev), "sysinfo_cydata"); } static void cyttsp5_si_get_sensing_conf_data(struct cyttsp5_core_data *cd) { struct cyttsp5_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data; struct cyttsp5_sensing_conf_data_dev *scd_dev = (struct cyttsp5_sensing_conf_data_dev *) &cd->response_buf[HID_SYSINFO_SENSING_OFFSET]; scd->electrodes_x = scd_dev->electrodes_x; scd->electrodes_y = scd_dev->electrodes_y; scd->origin_x = scd_dev->origin_x; scd->origin_y = scd_dev->origin_y; /* PIP 1.4 (001-82649 *Q) add X_IS_TX bit in X_ORG */ if (scd->origin_x & 0x02) { scd->tx_num = scd->electrodes_x; scd->rx_num = scd->electrodes_y; } else { scd->tx_num = scd->electrodes_y; scd->rx_num = scd->electrodes_x; } scd->panel_id = scd_dev->panel_id; scd->btn = scd_dev->btn; scd->scan_mode = scd_dev->scan_mode; scd->max_tch = scd_dev->max_num_of_tch_per_refresh_cycle; scd->res_x = get_unaligned_le16(&scd_dev->res_x); scd->res_y = get_unaligned_le16(&scd_dev->res_y); scd->max_z = get_unaligned_le16(&scd_dev->max_z); scd->len_x = get_unaligned_le16(&scd_dev->len_x); scd->len_y = get_unaligned_le16(&scd_dev->len_y); cyttsp5_pr_buf(cd->dev, (u8 *)scd_dev, sizeof(struct cyttsp5_sensing_conf_data_dev), "sensing_conf_data"); } static int cyttsp5_si_setup(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; int max_tch = si->sensing_conf_data.max_tch; if (!si->xy_data) si->xy_data = kzalloc(max_tch * si->desc.tch_record_size, GFP_KERNEL); if (!si->xy_data) return -ENOMEM; if (!si->xy_mode) si->xy_mode = kzalloc(si->desc.tch_header_size, GFP_KERNEL); if (!si->xy_mode) { kfree(si->xy_data); return -ENOMEM; } return 0; } static int cyttsp5_si_get_btn_data(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; int num_btns = 0; int num_defined_keys; u16 *key_table; int btn; int i; int rc = 0; unsigned int btns = cd->response_buf[HID_SYSINFO_BTN_OFFSET] & HID_SYSINFO_BTN_MASK; size_t btn_keys_size; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: get btn data\n", __func__); for (i = 0; i < HID_SYSINFO_MAX_BTN; i++) { if (btns & (1 << i)) num_btns++; } si->num_btns = num_btns; if (num_btns) { btn_keys_size = num_btns * sizeof(struct cyttsp5_btn); if (!si->btn) si->btn = kzalloc(btn_keys_size, GFP_KERNEL); if (!si->btn) return -ENOMEM; if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) num_defined_keys = 0; else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) num_defined_keys = 0; else num_defined_keys = cd->cpdata->sett [CY_IC_GRPNUM_BTN_KEYS]->size; for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) { key_table = (u16 *)cd->cpdata->sett [CY_IC_GRPNUM_BTN_KEYS]->data; si->btn[btn].key_code = key_table[btn]; si->btn[btn].enabled = true; } for (; btn < num_btns; btn++) { si->btn[btn].key_code = KEY_RESERVED; si->btn[btn].enabled = true; } return rc; } kfree(si->btn); si->btn = NULL; return rc; } static void cyttsp5_si_put_log_data(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; struct cyttsp5_cydata *cydata = &si->cydata; struct cyttsp5_sensing_conf_data *scd = &si->sensing_conf_data; int i; parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: pip_ver_major =0x%02X (%d)\n", __func__, cydata->pip_ver_major, cydata->pip_ver_major); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: pip_ver_minor =0x%02X (%d)\n", __func__, cydata->pip_ver_minor, cydata->pip_ver_minor); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_pid =0x%04X (%d)\n", __func__, cydata->fw_pid, cydata->fw_pid); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_major =0x%02X (%d)\n", __func__, cydata->fw_ver_major, cydata->fw_ver_major); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_minor =0x%02X (%d)\n", __func__, cydata->fw_ver_minor, cydata->fw_ver_minor); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: revctrl =0x%08X (%d)\n", __func__, cydata->revctrl, cydata->revctrl); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_conf =0x%04X (%d)\n", __func__, cydata->fw_ver_conf, cydata->fw_ver_conf); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: bl_ver_major =0x%02X (%d)\n", __func__, cydata->bl_ver_major, cydata->bl_ver_major); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: bl_ver_minor =0x%02X (%d)\n", __func__, cydata->bl_ver_minor, cydata->bl_ver_minor); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: jtag_id_h =0x%04X (%d)\n", __func__, cydata->jtag_id_h, cydata->jtag_id_h); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: jtag_id_l =0x%04X (%d)\n", __func__, cydata->jtag_id_l, cydata->jtag_id_l); for (i = 0; i < CY_NUM_MFGID; i++) parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: mfg_id[%d] =0x%02X (%d)\n", __func__, i, cydata->mfg_id[i], cydata->mfg_id[i]); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: post_code =0x%04X (%d)\n", __func__, cydata->post_code, cydata->post_code); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: electrodes_x =0x%02X (%d)\n", __func__, scd->electrodes_x, scd->electrodes_x); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: electrodes_y =0x%02X (%d)\n", __func__, scd->electrodes_y, scd->electrodes_y); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: len_x =0x%04X (%d)\n", __func__, scd->len_x, scd->len_x); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: len_y =0x%04X (%d)\n", __func__, scd->len_y, scd->len_y); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: res_x =0x%04X (%d)\n", __func__, scd->res_x, scd->res_x); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: res_y =0x%04X (%d)\n", __func__, scd->res_y, scd->res_y); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: max_z =0x%04X (%d)\n", __func__, scd->max_z, scd->max_z); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: origin_x =0x%02X (%d)\n", __func__, scd->origin_x, scd->origin_x); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: origin_y =0x%02X (%d)\n", __func__, scd->origin_y, scd->origin_y); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: panel_id =0x%02X (%d)\n", __func__, scd->panel_id, scd->panel_id); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: btn =0x%02X (%d)\n", __func__, scd->btn, scd->btn); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: scan_mode =0x%02X (%d)\n", __func__, scd->scan_mode, scd->scan_mode); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: max_num_of_tch_per_refresh_cycle =0x%02X (%d)\n", __func__, scd->max_tch, scd->max_tch); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: xy_mode =%p\n", __func__, si->xy_mode); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: xy_data =%p\n", __func__, si->xy_data); } static int cyttsp5_get_sysinfo_regs(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; int rc; rc = cyttsp5_si_get_btn_data(cd); if (rc < 0) return rc; cyttsp5_si_get_cydata(cd); cyttsp5_si_get_sensing_conf_data(cd); cyttsp5_si_setup(cd); cyttsp5_si_put_log_data(cd); si->ready = true; return rc; } static void cyttsp5_free_si_ptrs(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; kfree(si->btn); kfree(si->xy_mode); kfree(si->xy_data); } static int cyttsp5_hid_output_get_sysinfo_(struct cyttsp5_core_data *cd) { int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_SYSINFO), .timeout_ms = CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT, }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; rc = cyttsp5_get_sysinfo_regs(cd); if (rc) cyttsp5_free_si_ptrs(cd); return rc; } static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_get_sysinfo_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int cyttsp5_hid_output_suspend_scanning_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_SUSPEND_SCANNING), .timeout_ms = 1000, }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_suspend_scanning(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_suspend_scanning_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_suspend_scanning(struct device *dev, int protect) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_suspend_scanning(cd); return cyttsp5_hid_output_suspend_scanning_(cd); } static int cyttsp5_hid_output_resume_scanning_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RESUME_SCANNING), }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_resume_scanning(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_resume_scanning_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_resume_scanning(struct device *dev, int protect) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_resume_scanning(cd); return cyttsp5_hid_output_resume_scanning_(cd); } static int cyttsp5_hid_output_get_param_(struct cyttsp5_core_data *cd, u8 param_id, u32 *value) { int write_length = 1; u8 param[1] = { param_id }; u8 read_param_id; int param_size; u8 *ptr; int rc; int i; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_PARAM), .write_length = write_length, .write_buf = param, }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; read_param_id = cd->response_buf[5]; if (read_param_id != param_id) return -EPROTO; param_size = cd->response_buf[6]; ptr = &cd->response_buf[7]; *value = 0; for (i = 0; i < param_size; i++) *value += ptr[i] << (i * 8); return 0; } static int cyttsp5_hid_output_get_param(struct cyttsp5_core_data *cd, u8 param_id, u32 *value) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_get_param_(cd, param_id, value); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } int _cyttsp5_request_hid_output_get_param(struct device *dev, int protect, u8 param_id, u32 *value) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_get_param(cd, param_id, value); return cyttsp5_hid_output_get_param_(cd, param_id, value); } static int cyttsp5_hid_output_set_param_(struct cyttsp5_core_data *cd, u8 param_id, u32 value, u8 size) { u8 write_buf[6]; u8 *ptr = &write_buf[2]; int rc; int i; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_SET_PARAM), .write_buf = write_buf, }; write_buf[0] = param_id; write_buf[1] = size; for (i = 0; i < size; i++) { ptr[i] = value & 0xFF; value = value >> 8; } hid_output.write_length = 2 + size; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; if (param_id != cd->response_buf[5] || size != cd->response_buf[6]) return -EPROTO; return 0; } static int cyttsp5_hid_output_set_param(struct cyttsp5_core_data *cd, u8 param_id, u32 value, u8 size) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_set_param_(cd, param_id, value, size); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } int _cyttsp5_request_hid_output_set_param(struct device *dev, int protect, u8 param_id, u32 value, u8 size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_set_param(cd, param_id, value, size); return cyttsp5_hid_output_set_param_(cd, param_id, value, size); } static int cyttsp5_hid_output_enter_easywake_state_( struct cyttsp5_core_data *cd, u8 data, u8 *return_data) { int write_length = 1; u8 param[1] = { data }; int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_ENTER_EASYWAKE_STATE), .write_length = write_length, .write_buf = param, }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; *return_data = cd->response_buf[5]; return rc; } static int cyttsp5_hid_output_verify_config_block_crc_( struct cyttsp5_core_data *cd, u8 ebid, u8 *status, u16 *calculated_crc, u16 *stored_crc) { int write_length = 1; u8 param[1] = { ebid }; u8 *ptr; int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_VERIFY_CONFIG_BLOCK_CRC), .write_length = write_length, .write_buf = param, .timeout_ms = 1000, }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; ptr = &cd->response_buf[5]; *status = ptr[0]; *calculated_crc = get_unaligned_le16(&ptr[1]); *stored_crc = get_unaligned_le16(&ptr[3]); return 0; } static int cyttsp5_hid_output_verify_config_block_crc( struct cyttsp5_core_data *cd, u8 ebid, u8 *status, u16 *calculated_crc, u16 *stored_crc) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, status, calculated_crc, stored_crc); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_verify_config_block_crc( struct device *dev, int protect, u8 ebid, u8 *status, u16 *calculated_crc, u16 *stored_crc) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_verify_config_block_crc(cd, ebid, status, calculated_crc, stored_crc); return cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, status, calculated_crc, stored_crc); } static int cyttsp5_hid_output_get_config_row_size_(struct cyttsp5_core_data *cd, u16 *row_size) { int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_CONFIG_ROW_SIZE), }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; *row_size = get_unaligned_le16(&cd->response_buf[5]); return 0; } static int cyttsp5_hid_output_get_config_row_size(struct cyttsp5_core_data *cd, u16 *row_size) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_get_config_row_size_(cd, row_size); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_get_config_row_size(struct device *dev, int protect, u16 *row_size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_get_config_row_size(cd, row_size); return cyttsp5_hid_output_get_config_row_size_(cd, row_size); } static int cyttsp5_hid_output_read_conf_block_(struct cyttsp5_core_data *cd, u16 row_number, u16 length, u8 ebid, u8 *read_buf, u16 *crc) { int read_ebid; int read_length; int status; int rc; int write_length = 5; u8 write_buf[5]; u8 cmd_offset = 0; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_READ_CONF_BLOCK), .write_length = write_length, .write_buf = write_buf, }; write_buf[cmd_offset++] = LOW_BYTE(row_number); write_buf[cmd_offset++] = HI_BYTE(row_number); write_buf[cmd_offset++] = LOW_BYTE(length); write_buf[cmd_offset++] = HI_BYTE(length); write_buf[cmd_offset++] = ebid; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; status = cd->response_buf[5]; if (status) return -EINVAL; read_ebid = cd->response_buf[6]; if ((read_ebid != ebid) || (cd->response_buf[9] != 0)) return -EPROTO; read_length = get_unaligned_le16(&cd->response_buf[7]); if (length < read_length) length = read_length; if (length + 10 > CY_MAX_INPUT) { dev_err(cd->dev, "%s:%d: size=%d is larger than the max %d!!!\n", __func__, __LINE__, length + 10, CY_MAX_INPUT); return -EINVAL; } memcpy(read_buf, &cd->response_buf[10], length); *crc = get_unaligned_le16(&cd->response_buf[read_length + 10]); return 0; } static int cyttsp5_hid_output_read_conf_ver_(struct cyttsp5_core_data *cd, u16 *config_ver) { int rc; u8 read_buf[CY_TTCONFIG_VERSION_OFFSET + CY_TTCONFIG_VERSION_SIZE]; u16 crc; rc = cyttsp5_hid_output_read_conf_block_(cd, CY_TTCONFIG_VERSION_ROW, CY_TTCONFIG_VERSION_OFFSET + CY_TTCONFIG_VERSION_SIZE, CY_TCH_PARM_EBID, read_buf, &crc); if (rc) return rc; *config_ver = get_unaligned_le16( &read_buf[CY_TTCONFIG_VERSION_OFFSET]); return 0; } static int cyttsp5_hid_output_write_conf_block_(struct cyttsp5_core_data *cd, u16 row_number, u16 write_length, u8 ebid, u8 *write_buf, u8 *security_key, u16 *actual_write_len) { /* row_number + write_len + ebid + security_key + crc */ int full_write_length = 2 + 2 + 1 + write_length + 8 + 2; u8 *full_write_buf; u8 cmd_offset = 0; u16 crc; int status; int rc; int read_ebid; u8 *data; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_WRITE_CONF_BLOCK), .write_length = full_write_length, .timeout_ms = CY_HID_OUTPUT_WRITE_CONF_BLOCK_TIMEOUT, }; full_write_buf = kzalloc(full_write_length, GFP_KERNEL); if (!full_write_buf) return -ENOMEM; hid_output.write_buf = full_write_buf; full_write_buf[cmd_offset++] = LOW_BYTE(row_number); full_write_buf[cmd_offset++] = HI_BYTE(row_number); full_write_buf[cmd_offset++] = LOW_BYTE(write_length); full_write_buf[cmd_offset++] = HI_BYTE(write_length); full_write_buf[cmd_offset++] = ebid; data = &full_write_buf[cmd_offset]; memcpy(data, write_buf, write_length); cmd_offset += write_length; memcpy(&full_write_buf[cmd_offset], security_key, 8); cmd_offset += 8; crc = _cyttsp5_compute_crc(data, write_length); full_write_buf[cmd_offset++] = LOW_BYTE(crc); full_write_buf[cmd_offset++] = HI_BYTE(crc); rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) goto exit; status = cd->response_buf[5]; if (status) { rc = -EINVAL; goto exit; } read_ebid = cd->response_buf[6]; if (read_ebid != ebid) { rc = -EPROTO; goto exit; } *actual_write_len = get_unaligned_le16(&cd->response_buf[7]); exit: kfree(full_write_buf); return rc; } static int cyttsp5_hid_output_write_conf_block(struct cyttsp5_core_data *cd, u16 row_number, u16 write_length, u8 ebid, u8 *write_buf, u8 *security_key, u16 *actual_write_len) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_write_conf_block_(cd, row_number, write_length, ebid, write_buf, security_key, actual_write_len); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_write_conf_block(struct device *dev, int protect, u16 row_number, u16 write_length, u8 ebid, u8 *write_buf, u8 *security_key, u16 *actual_write_len) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_write_conf_block(cd, row_number, write_length, ebid, write_buf, security_key, actual_write_len); return cyttsp5_hid_output_write_conf_block_(cd, row_number, write_length, ebid, write_buf, security_key, actual_write_len); } static int cyttsp5_hid_output_get_data_structure_( struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length, u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data) { int rc; u16 total_read_len = 0; u16 read_len; u16 off_buf = 0; u8 write_buf[5]; u8 read_data_id; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_DATA_STRUCTURE), .write_length = 5, .write_buf = write_buf, }; again: write_buf[0] = LOW_BYTE(read_offset); write_buf[1] = HI_BYTE(read_offset); write_buf[2] = LOW_BYTE(read_length); write_buf[3] = HI_BYTE(read_length); write_buf[4] = data_id; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; if (cd->response_buf[5] != CY_CMD_STATUS_SUCCESS) goto set_status; read_data_id = cd->response_buf[6]; if (read_data_id != data_id) return -EPROTO; read_len = get_unaligned_le16(&cd->response_buf[7]); if (read_len && data) { memcpy(&data[off_buf], &cd->response_buf[10], read_len); total_read_len += read_len; if (read_len < read_length) { read_offset += read_len; off_buf += read_len; read_length -= read_len; goto again; } } if (data_format) *data_format = cd->response_buf[9]; if (actual_read_len) *actual_read_len = total_read_len; set_status: if (status) *status = cd->response_buf[5]; return rc; } static int cyttsp5_hid_output_get_data_structure( struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length, u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_get_data_structure_(cd, read_offset, read_length, data_id, status, data_format, actual_read_len, data); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_get_data_structure(struct device *dev, int protect, u16 read_offset, u16 read_length, u8 data_id, u8 *status, u8 *data_format, u16 *actual_read_len, u8 *data) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_get_data_structure(cd, read_offset, read_length, data_id, status, data_format, actual_read_len, data); return cyttsp5_hid_output_get_data_structure_(cd, read_offset, read_length, data_id, status, data_format, actual_read_len, data); } static int cyttsp5_hid_output_run_selftest_( struct cyttsp5_core_data *cd, u8 test_id, u8 write_idacs_to_flash, u8 *status, u8 *summary_result, u8 *results_available) { int rc; u8 write_buf[2]; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RUN_SELF_TEST), .write_length = 2, .write_buf = write_buf, .timeout_ms = CY_HID_OUTPUT_RUN_SELF_TEST_TIMEOUT, }; write_buf[0] = test_id; write_buf[1] = write_idacs_to_flash; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; if (status) *status = cd->response_buf[5]; if (summary_result) *summary_result = cd->response_buf[6]; if (results_available) *results_available = cd->response_buf[7]; return rc; } static int cyttsp5_hid_output_run_selftest( struct cyttsp5_core_data *cd, u8 test_id, u8 write_idacs_to_flash, u8 *status, u8 *summary_result, u8 *results_available) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_run_selftest_(cd, test_id, write_idacs_to_flash, status, summary_result, results_available); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_run_selftest(struct device *dev, int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status, u8 *summary_result, u8 *results_available) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_run_selftest(cd, test_id, write_idacs_to_flash, status, summary_result, results_available); return cyttsp5_hid_output_run_selftest_(cd, test_id, write_idacs_to_flash, status, summary_result, results_available); } static int cyttsp5_hid_output_get_selftest_result_( struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length, u8 test_id, u8 *status, u16 *actual_read_len, u8 *data) { int rc; u16 total_read_len = 0; u16 read_len; u16 off_buf = 0; u8 write_buf[5]; u8 read_test_id; bool repeat; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_SELF_TEST_RESULT), .write_length = 5, .write_buf = write_buf, }; /* * Do not repeat reading for Auto Shorts test * when PIP version < 1.3 */ repeat = IS_PIP_VER_GE(&cd->sysinfo, 1, 3) || test_id != CY_ST_ID_AUTOSHORTS; again: write_buf[0] = LOW_BYTE(read_offset); write_buf[1] = HI_BYTE(read_offset); write_buf[2] = LOW_BYTE(read_length); write_buf[3] = HI_BYTE(read_length); write_buf[4] = test_id; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; if (cd->response_buf[5] != CY_CMD_STATUS_SUCCESS) goto set_status; read_test_id = cd->response_buf[6]; if (read_test_id != test_id) return -EPROTO; read_len = get_unaligned_le16(&cd->response_buf[7]); if (read_len && data) { memcpy(&data[off_buf], &cd->response_buf[10], read_len); total_read_len += read_len; if (repeat && read_len < read_length) { read_offset += read_len; off_buf += read_len; read_length -= read_len; goto again; } } if (actual_read_len) *actual_read_len = total_read_len; set_status: if (status) *status = cd->response_buf[5]; return rc; } static int cyttsp5_hid_output_get_selftest_result( struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length, u8 test_id, u8 *status, u16 *actual_read_len, u8 *data) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_get_selftest_result_(cd, read_offset, read_length, test_id, status, actual_read_len, data); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_get_selftest_result(struct device *dev, int protect, u16 read_offset, u16 read_length, u8 test_id, u8 *status, u16 *actual_read_len, u8 *data) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_get_selftest_result(cd, read_offset, read_length, test_id, status, actual_read_len, data); return cyttsp5_hid_output_get_selftest_result_(cd, read_offset, read_length, test_id, status, actual_read_len, data); } static int cyttsp5_hid_output_calibrate_idacs_(struct cyttsp5_core_data *cd, u8 mode, u8 *status) { int rc; int write_length = 1; u8 write_buf[1]; u8 cmd_offset = 0; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_CALIBRATE_IDACS), .write_length = write_length, .write_buf = write_buf, .timeout_ms = CY_HID_OUTPUT_CALIBRATE_IDAC_TIMEOUT, }; write_buf[cmd_offset++] = mode; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; *status = cd->response_buf[5]; if (*status) return -EINVAL; return 0; } static int cyttsp5_hid_output_calibrate_idacs(struct cyttsp5_core_data *cd, u8 mode, u8 *status) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_calibrate_idacs_(cd, mode, status); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_calibrate_idacs(struct device *dev, int protect, u8 mode, u8 *status) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_calibrate_idacs(cd, mode, status); return cyttsp5_hid_output_calibrate_idacs_(cd, mode, status); } static int cyttsp5_hid_output_initialize_baselines_( struct cyttsp5_core_data *cd, u8 test_id, u8 *status) { int rc; int write_length = 1; u8 write_buf[1]; u8 cmd_offset = 0; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_INITIALIZE_BASELINES), .write_length = write_length, .write_buf = write_buf, }; write_buf[cmd_offset++] = test_id; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; *status = cd->response_buf[5]; if (*status) return -EINVAL; return rc; } static int cyttsp5_hid_output_initialize_baselines(struct cyttsp5_core_data *cd, u8 test_id, u8 *status) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_initialize_baselines_(cd, test_id, status); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_initialize_baselines(struct device *dev, int protect, u8 test_id, u8 *status) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_initialize_baselines(cd, test_id, status); return cyttsp5_hid_output_initialize_baselines_(cd, test_id, status); } static int cyttsp5_hid_output_exec_panel_scan_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_EXEC_PANEL_SCAN), }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_exec_panel_scan(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_exec_panel_scan_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_exec_panel_scan(struct device *dev, int protect) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_exec_panel_scan(cd); return cyttsp5_hid_output_exec_panel_scan_(cd); } /* @response: set none NULL only if all response required including header */ static int cyttsp5_hid_output_retrieve_panel_scan_( struct cyttsp5_core_data *cd, u16 read_offset, u16 read_count, u8 data_id, u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf) { int status; u8 read_data_id; int rc; int write_length = 5; u8 write_buf[5]; u8 cmd_offset = 0; u8 data_elem_size; int size; int data_size; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RETRIEVE_PANEL_SCAN), .write_length = write_length, .write_buf = write_buf, }; write_buf[cmd_offset++] = LOW_BYTE(read_offset); write_buf[cmd_offset++] = HI_BYTE(read_offset); write_buf[cmd_offset++] = LOW_BYTE(read_count); write_buf[cmd_offset++] = HI_BYTE(read_count); write_buf[cmd_offset++] = data_id; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; status = cd->response_buf[5]; if (status) return -EINVAL; read_data_id = cd->response_buf[6]; if (read_data_id != data_id) return -EPROTO; size = get_unaligned_le16(&cd->response_buf[0]); *actual_read_len = get_unaligned_le16(&cd->response_buf[7]); *config = cd->response_buf[9]; data_elem_size = *config & 0x07; data_size = *actual_read_len * data_elem_size; if (read_buf) memcpy(read_buf, &cd->response_buf[10], data_size); if (response) memcpy(response, cd->response_buf, size); return rc; } static int cyttsp5_hid_output_retrieve_panel_scan( struct cyttsp5_core_data *cd, u16 read_offset, u16 read_count, u8 data_id, u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_retrieve_panel_scan_(cd, read_offset, read_count, data_id, response, config, actual_read_len, read_buf); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_retrieve_panel_scan(struct device *dev, int protect, u16 read_offset, u16 read_count, u8 data_id, u8 *response, u8 *config, u16 *actual_read_len, u8 *read_buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_retrieve_panel_scan(cd, read_offset, read_count, data_id, response, config, actual_read_len, read_buf); return cyttsp5_hid_output_retrieve_panel_scan_(cd, read_offset, read_count, data_id, response, config, actual_read_len, read_buf); } static int cyttsp5_hid_output_user_cmd_(struct cyttsp5_core_data *cd, u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, u16 *actual_read_len) { int rc; u16 size; #ifdef TTHE_TUNER_SUPPORT int command_code = 0; int len; #endif struct cyttsp5_hid_output hid_output = { .length = write_len, .write_buf = write_buf, }; rc = cyttsp5_hid_send_output_user_and_wait_(cd, &hid_output); if (rc) return rc; size = get_unaligned_le16(&cd->response_buf[0]); if (size == 0) size = 2; if (size > read_len) { *actual_read_len = 0; return -EINVAL; } memcpy(read_buf, cd->response_buf, size); *actual_read_len = size; #ifdef TTHE_TUNER_SUPPORT /* print up to cmd code */ len = HID_OUTPUT_CMD_OFFSET + 1; if (write_len < len) len = write_len; else command_code = write_buf[HID_OUTPUT_CMD_OFFSET] & HID_OUTPUT_CMD_MASK; /* Do not print for EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */ if (command_code != HID_OUTPUT_EXEC_PANEL_SCAN && command_code != HID_OUTPUT_RETRIEVE_PANEL_SCAN) tthe_print(cd, write_buf, len, "CMD="); #endif return 0; } static int cyttsp5_get_config_ver_(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; int rc; u16 config_ver = 0; rc = cyttsp5_hid_output_suspend_scanning_(cd); if (rc) goto error; rc = cyttsp5_hid_output_read_conf_ver_(cd, &config_ver); if (rc) goto exit; si->cydata.fw_ver_conf = config_ver; exit: cyttsp5_hid_output_resume_scanning_(cd); error: parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: CONFIG_VER:%04X\n", __func__, config_ver); return rc; } static int cyttsp5_hid_output_user_cmd(struct cyttsp5_core_data *cd, u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, u16 *actual_read_len) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_user_cmd_(cd, read_len, read_buf, write_len, write_buf, actual_read_len); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_user_cmd(struct device *dev, int protect, u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, u16 *actual_read_len) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_user_cmd(cd, read_len, read_buf, write_len, write_buf, actual_read_len); return cyttsp5_hid_output_user_cmd_(cd, read_len, read_buf, write_len, write_buf, actual_read_len); } static int cyttsp5_hid_output_bl_get_information_(struct cyttsp5_core_data *cd, u8 *return_data) { int rc; int data_len; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_GET_INFO), }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) return rc; data_len = get_unaligned_le16(&cd->input_buf[6]); if (!data_len) return -EPROTO; if (data_len + 8 > CY_MAX_INPUT) { dev_err(cd->dev, "%s:%d: size=%d is larger than the max %d!!!\n", __func__, __LINE__, data_len, CY_MAX_INPUT); return -EINVAL; } memcpy(return_data, &cd->response_buf[8], data_len); return 0; } static int cyttsp5_hid_output_bl_get_information(struct cyttsp5_core_data *cd, u8 *return_data) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_bl_get_information_(cd, return_data); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_bl_get_information(struct device *dev, int protect, u8 *return_data) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_bl_get_information(cd, return_data); return cyttsp5_hid_output_bl_get_information_(cd, return_data); } static int cyttsp5_hid_output_bl_initiate_bl_(struct cyttsp5_core_data *cd, u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) { u16 write_length = key_size + row_size; u8 *write_buf; int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_INITIATE_BL), .write_length = write_length, .timeout_ms = CY_HID_OUTPUT_BL_INITIATE_BL_TIMEOUT, }; write_buf = kzalloc(write_length, GFP_KERNEL); if (!write_buf) return -ENOMEM; hid_output.write_buf = write_buf; if (key_size) memcpy(write_buf, key_buf, key_size); if (row_size) memcpy(&write_buf[key_size], metadata_row_buf, row_size); rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); kfree(write_buf); return rc; } static int cyttsp5_hid_output_bl_initiate_bl(struct cyttsp5_core_data *cd, u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_bl_initiate_bl_(cd, key_size, key_buf, row_size, metadata_row_buf); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_bl_initiate_bl(struct device *dev, int protect, u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_bl_initiate_bl(cd, key_size, key_buf, row_size, metadata_row_buf); return cyttsp5_hid_output_bl_initiate_bl_(cd, key_size, key_buf, row_size, metadata_row_buf); } static int cyttsp5_hid_output_bl_program_and_verify_( struct cyttsp5_core_data *cd, u16 data_len, u8 *data_buf) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_PROGRAM_AND_VERIFY), .write_length = data_len, .write_buf = data_buf, .timeout_ms = CY_HID_OUTPUT_BL_PROGRAM_AND_VERIFY_TIMEOUT, }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_bl_program_and_verify( struct cyttsp5_core_data *cd, u16 data_len, u8 *data_buf) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_bl_program_and_verify_(cd, data_len, data_buf); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_bl_program_and_verify( struct device *dev, int protect, u16 data_len, u8 *data_buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_bl_program_and_verify(cd, data_len, data_buf); return cyttsp5_hid_output_bl_program_and_verify_(cd, data_len, data_buf); } static int cyttsp5_hid_output_bl_verify_app_integrity_( struct cyttsp5_core_data *cd, u8 *result) { int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_VERIFY_APP_INTEGRITY), }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc) { *result = 0; return rc; } *result = cd->response_buf[8]; return 0; } static int cyttsp5_hid_output_bl_verify_app_integrity( struct cyttsp5_core_data *cd, u8 *result) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_bl_verify_app_integrity_(cd, result); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_bl_verify_app_integrity( struct device *dev, int protect, u8 *result) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_bl_verify_app_integrity(cd, result); return cyttsp5_hid_output_bl_verify_app_integrity_(cd, result); } static int cyttsp5_hid_output_bl_launch_app_(struct cyttsp5_core_data *cd) { struct cyttsp5_hid_output hid_output = { HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_LAUNCH_APP), .reset_expected = 1, }; return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); } static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_bl_launch_app_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_launch_app(struct device *dev, int protect) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_bl_launch_app(cd); return cyttsp5_hid_output_bl_launch_app_(cd); } static int cyttsp5_hid_output_bl_get_panel_id_( struct cyttsp5_core_data *cd, u8 *panel_id) { int rc; struct cyttsp5_hid_output hid_output = { HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_GET_PANEL_ID), }; rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) { parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Get Panel ID command not supported\n", __func__); *panel_id = PANEL_ID_NOT_ENABLED; return 0; } else if (rc < 0) { dev_err(cd->dev, "%s: Error on Get Panel ID command\n", __func__); return rc; } *panel_id = cd->response_buf[8]; return 0; } static int cyttsp5_hid_output_bl_get_panel_id( struct cyttsp5_core_data *cd, u8 *panel_id) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_hid_output_bl_get_panel_id_(cd, panel_id); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_hid_output_bl_get_panel_id( struct device *dev, int protect, u8 *panel_id) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_hid_output_bl_get_panel_id(cd, panel_id); return cyttsp5_hid_output_bl_get_panel_id_(cd, panel_id); } static int cyttsp5_get_hid_descriptor_(struct cyttsp5_core_data *cd, struct cyttsp5_hid_desc *desc) { struct device *dev = cd->dev; int rc; int t; u8 cmd[2]; /* Read HID descriptor length and version */ mutex_lock(&cd->system_lock); cd->hid_cmd_state = 1; mutex_unlock(&cd->system_lock); /* Set HID descriptor register */ memcpy(cmd, &cd->hid_core.hid_desc_register, sizeof(cd->hid_core.hid_desc_register)); rc = cyttsp5_adap_write_read_specific(cd, 2, cmd, NULL); if (rc) { dev_err(dev, "%s: failed to get HID descriptor length and version, rc=%d\n", __func__, rc); goto error; } t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), msecs_to_jiffies(CY_HID_GET_HID_DESCRIPTOR_TIMEOUT)); if (IS_TMO(t)) { dev_err(cd->dev, "%s: HID get descriptor timed out\n", __func__); rc = -ETIME; goto error; } memcpy((u8 *)desc, cd->response_buf, sizeof(struct cyttsp5_hid_desc)); /* Check HID descriptor length and version */ parade_debug(dev, DEBUG_LEVEL_2, "%s: HID len:%X HID ver:%X\n", __func__, le16_to_cpu(desc->hid_desc_len), le16_to_cpu(desc->bcd_version)); if (le16_to_cpu(desc->hid_desc_len) != sizeof(*desc) || le16_to_cpu(desc->bcd_version) != CY_HID_VERSION) { dev_err(dev, "%s: Unsupported HID version\n", __func__); return -ENODEV; } goto exit; error: mutex_lock(&cd->system_lock); cd->hid_cmd_state = 0; mutex_unlock(&cd->system_lock); exit: return rc; } static int cyttsp5_get_hid_descriptor(struct cyttsp5_core_data *cd, struct cyttsp5_hid_desc *desc) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_get_hid_descriptor_(cd, desc); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); return rc; } static int _cyttsp5_request_get_hid_desc(struct device *dev, int protect) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (protect) return cyttsp5_get_hid_descriptor(cd, &cd->hid_desc); return cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); } static int cyttsp5_hw_soft_reset(struct cyttsp5_core_data *cd) { int rc; if (cd->hid_desc.hid_desc_len == 0) { rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); if (rc < 0) return rc; } rc = cyttsp5_hid_cmd_reset_(cd); if (rc < 0) { dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", __func__); return rc; } parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: execute SOFT reset\n", __func__); return 0; } static int cyttsp5_hw_hard_reset(struct cyttsp5_core_data *cd) { if (cd->cpdata->xres) { cd->cpdata->xres(cd->cpdata, cd->dev); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: execute HARD reset\n", __func__); return 0; } dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); return -ENODEV; } static int cyttsp5_hw_reset(struct cyttsp5_core_data *cd) { int rc; mutex_lock(&cd->system_lock); rc = cyttsp5_hw_hard_reset(cd); //printk("======cyttsp5_hw_reset rc=%d \n", rc); mutex_unlock(&cd->system_lock); if (rc == -ENODEV) rc = cyttsp5_hw_soft_reset(cd); return rc; } static inline int get_hid_item_data(u8 *data, int item_size) { if (item_size == 1) return (int)*data; else if (item_size == 2) return (int)get_unaligned_le16(data); else if (item_size == 4) return (int)get_unaligned_le32(data); return 0; } static int parse_report_descriptor(struct cyttsp5_core_data *cd, u8 *report_desc, size_t len) { struct cyttsp5_hid_report *report; struct cyttsp5_hid_field *field; u8 *buf = report_desc; u8 *end = buf + len; int rc = 0; int offset = 0; int i; u8 report_type; u32 up_usage; /* Global items */ u8 report_id = 0; u16 usage_page = 0; int report_count = 0; int report_size = 0; int logical_min = 0; int logical_max = 0; /* Local items */ u16 usage = 0; /* Main items - Collection stack */ u32 collection_usages[CY_HID_MAX_NESTED_COLLECTIONS] = {0}; u8 collection_types[CY_HID_MAX_NESTED_COLLECTIONS] = {0}; /* First collection for header, second for report */ int logical_collection_count = 0; int collection_nest = 0; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Report descriptor length: %u\n", __func__, (u32)len); mutex_lock(&cd->hid_report_lock); cyttsp5_free_hid_reports_(cd); while (buf < end) { int item_type; int item_size; int item_tag; u8 *data; /* Get Item */ item_size = HID_GET_ITEM_SIZE(buf[0]); if (item_size == 3) item_size = 4; item_type = HID_GET_ITEM_TYPE(buf[0]); item_tag = HID_GET_ITEM_TAG(buf[0]); data = ++buf; buf += item_size; /* Process current item */ switch (item_type) { case HID_ITEM_TYPE_GLOBAL: switch (item_tag) { case HID_GLOBAL_ITEM_TAG_REPORT_ID: if (item_size != 1) { rc = -EINVAL; goto exit; } report_id = get_hid_item_data(data, item_size); offset = 0; logical_collection_count = 0; break; case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: if (item_size == 0 || item_size == 4) { rc = -EINVAL; goto exit; } usage_page = (u16)get_hid_item_data(data, item_size); break; case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: if (item_size == 0) { rc = -EINVAL; goto exit; } logical_min = get_hid_item_data(data, item_size); break; case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: if (item_size == 0) { rc = -EINVAL; goto exit; } logical_max = get_hid_item_data(data, item_size); break; case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: if (item_size == 0) { rc = -EINVAL; goto exit; } report_count = get_hid_item_data(data, item_size); break; case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: if (item_size == 0) { rc = -EINVAL; goto exit; } report_size = get_hid_item_data(data, item_size); break; default: dev_info(cd->dev, "%s: Unrecognized Global tag %d\n", __func__, item_tag); } break; case HID_ITEM_TYPE_LOCAL: switch (item_tag) { case HID_LOCAL_ITEM_TAG_USAGE: if (item_size == 0 || item_size == 4) { rc = -EINVAL; goto exit; } usage = (u16)get_hid_item_data(data, item_size); break; case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: if (item_size == 0) { rc = -EINVAL; goto exit; } /* usage_min = */ get_hid_item_data(data, item_size); break; case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: if (item_size == 0) { rc = -EINVAL; goto exit; } /* usage_max = */ get_hid_item_data(data, item_size); break; default: dev_info(cd->dev, "%s: Unrecognized Local tag %d\n", __func__, item_tag); } break; case HID_ITEM_TYPE_MAIN: switch (item_tag) { case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: if (item_size != 1) { rc = -EINVAL; goto exit; } if (CY_HID_MAX_NESTED_COLLECTIONS == collection_nest) { rc = -EINVAL; goto exit; } up_usage = usage_page << 16 | usage; /* Update collection stack */ collection_usages[collection_nest] = up_usage; collection_types[collection_nest] = get_hid_item_data(data, item_size); if (collection_types[collection_nest] == HID_COLLECTION_LOGICAL) logical_collection_count++; collection_nest++; break; case HID_MAIN_ITEM_TAG_END_COLLECTION: if (item_size != 0) { rc = -EINVAL; goto exit; } if (--collection_nest < 0) { rc = -EINVAL; goto exit; } break; case HID_MAIN_ITEM_TAG_INPUT: report_type = HID_INPUT_REPORT; goto continue_main_item; case HID_MAIN_ITEM_TAG_OUTPUT: report_type = HID_OUTPUT_REPORT; goto continue_main_item; case HID_MAIN_ITEM_TAG_FEATURE: report_type = HID_FEATURE_REPORT; continue_main_item: if (item_size != 1) { rc = -EINVAL; goto exit; } up_usage = usage_page << 16 | usage; /* Get or create report */ report = cyttsp5_get_hid_report_(cd, report_type, report_id, true); if (!report) { rc = -ENOMEM; goto exit; } if (!report->usage_page && collection_nest > 0) report->usage_page = collection_usages [collection_nest - 1]; /* Create field */ field = cyttsp5_create_hid_field_(report); if (!field) { rc = -ENOMEM; goto exit; } field->report_count = report_count; field->report_size = report_size; field->size = report_count * report_size; field->offset = offset; field->data_type = get_hid_item_data(data, item_size); field->logical_min = logical_min; field->logical_max = logical_max; field->usage_page = up_usage; for (i = 0; i < collection_nest; i++) { field->collection_usage_pages [collection_types[i]] = collection_usages[i]; } /* Update report's header or record size */ if (logical_collection_count == 1) { report->header_size += field->size; } else if (logical_collection_count == 2) { field->record_field = true; field->offset -= report->header_size; /* Set record field index */ if (report->record_field_index == 0) report->record_field_index = report->num_fields - 1; report->record_size += field->size; } report->size += field->size; offset += field->size; break; default: dev_info(cd->dev, "%s: Unrecognized Main tag %d\n", __func__, item_tag); } /* Reset all local items */ usage = 0; break; } } if (buf != end) { dev_err(cd->dev, "%s: Report descriptor length invalid\n", __func__); rc = -EINVAL; goto exit; } if (collection_nest) { dev_err(cd->dev, "%s: Unbalanced collection items (%d)\n", __func__, collection_nest); rc = -EINVAL; goto exit; } exit: if (rc) cyttsp5_free_hid_reports_(cd); mutex_unlock(&cd->hid_report_lock); return rc; } static struct cyttsp5_hid_field *find_report_desc_field( struct cyttsp5_core_data *cd, u32 usage_page, u32 collection_usage_page) { struct cyttsp5_hid_report *report = NULL; struct cyttsp5_hid_field *field = NULL; int i; int j; u32 field_cup; u32 field_up; for (i = 0; i < cd->num_hid_reports; i++) { report = cd->hid_reports[i]; for (j = 0; j < report->num_fields; j++) { field = report->fields[j]; field_cup = field->collection_usage_pages [HID_COLLECTION_LOGICAL]; field_up = field->usage_page; if (field_cup == collection_usage_page && field_up == usage_page) { return field; } } } return NULL; } static int fill_tch_abs(struct cyttsp5_tch_abs_params *tch_abs, struct cyttsp5_hid_field *field) { tch_abs->ofs = field->offset / 8; tch_abs->size = field->report_size / 8; if (field->report_size % 8) tch_abs->size += 1; tch_abs->min = 0; tch_abs->max = 1 << field->report_size; tch_abs->bofs = field->offset - (tch_abs->ofs << 3); return 0; } static struct cyttsp5_hid_report *find_report_desc(struct cyttsp5_core_data *cd, u32 usage_page) { struct cyttsp5_hid_report *report = NULL; int i; for (i = 0; i < cd->num_hid_reports; i++) { if (cd->hid_reports[i]->usage_page == usage_page) { report = cd->hid_reports[i]; break; } } return report; } static int setup_report_descriptor(struct cyttsp5_core_data *cd) { struct cyttsp5_sysinfo *si = &cd->sysinfo; struct cyttsp5_hid_report *report; struct cyttsp5_hid_field *field; int i; u32 tch_collection_usage_page = HID_CY_TCH_COL_USAGE_PG; u32 btn_collection_usage_page = HID_CY_BTN_COL_USAGE_PG; for (i = CY_TCH_X; i < CY_TCH_NUM_ABS; i++) { field = find_report_desc_field(cd, cyttsp5_tch_abs_field_map[i], tch_collection_usage_page); if (field) { parade_debug(cd->dev, DEBUG_LEVEL_2, " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", field, field->report_count, field->report_size, field->offset, field->data_type, field->logical_min, field->logical_max, field->usage_page); fill_tch_abs(&si->tch_abs[i], field); si->tch_abs[i].report = 1; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", cyttsp5_tch_abs_string[i], (u32)si->tch_abs[i].ofs, (u32)si->tch_abs[i].size, (u32)si->tch_abs[i].min, (u32)si->tch_abs[i].max, (u32)si->tch_abs[i].bofs, si->tch_abs[i].report); } else { si->tch_abs[i].report = 0; } } for (i = CY_TCH_TIME; i < CY_TCH_NUM_HDR; i++) { field = find_report_desc_field(cd, cyttsp5_tch_hdr_field_map[i], tch_collection_usage_page); if (field) { parade_debug(cd->dev, DEBUG_LEVEL_2, " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", field, field->report_count, field->report_size, field->offset, field->data_type, field->logical_min, field->logical_max, field->usage_page); fill_tch_abs(&si->tch_hdr[i], field); si->tch_hdr[i].report = 1; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", cyttsp5_tch_hdr_string[i], (u32)si->tch_hdr[i].ofs, (u32)si->tch_hdr[i].size, (u32)si->tch_hdr[i].min, (u32)si->tch_hdr[i].max, (u32)si->tch_hdr[i].bofs, si->tch_hdr[i].report); } else { si->tch_hdr[i].report = 0; } } report = find_report_desc(cd, tch_collection_usage_page); if (report) { si->desc.tch_report_id = report->id; si->desc.tch_record_size = report->record_size / 8; si->desc.tch_header_size = (report->header_size / 8) + 3; } else { si->desc.tch_report_id = HID_TOUCH_REPORT_ID; si->desc.tch_record_size = TOUCH_REPORT_SIZE; si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; } report = find_report_desc(cd, btn_collection_usage_page); if (report) si->desc.btn_report_id = report->id; else si->desc.btn_report_id = HID_BTN_REPORT_ID; for (i = 0; i < cd->num_hid_reports; i++) { struct cyttsp5_hid_report *report = cd->hid_reports[i]; switch (report->id) { case HID_WAKEUP_REPORT_ID: cd->features.easywake = 1; break; case HID_NOISE_METRIC_REPORT_ID: cd->features.noise_metric = 1; break; case HID_TRACKING_HEATMAP_REPOR_ID: cd->features.tracking_heatmap = 1; break; case HID_SENSOR_DATA_REPORT_ID: cd->features.sensor_data = 1; break; default: break; } } parade_debug(cd->dev, DEBUG_LEVEL_1, "Features: easywake:%d noise_metric:%d tracking_heatmap:%d sensor_data:%d\n", cd->features.easywake, cd->features.noise_metric, cd->features.tracking_heatmap, cd->features.sensor_data); return 0; } static int cyttsp5_get_report_descriptor_(struct cyttsp5_core_data *cd) { struct device *dev = cd->dev; u8 cmd[2]; int rc; int t; /* Read report descriptor length and version */ mutex_lock(&cd->system_lock); cd->hid_cmd_state = 1; mutex_unlock(&cd->system_lock); /* Set report descriptor register */ memcpy(cmd, &cd->hid_desc.report_desc_register, sizeof(cd->hid_desc.report_desc_register)); rc = cyttsp5_adap_write_read_specific(cd, 2, cmd, NULL); if (rc) { dev_err(dev, "%s: failed to get HID descriptor length and version, rc=%d\n", __func__, rc); goto error; } t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), msecs_to_jiffies(CY_HID_GET_REPORT_DESCRIPTOR_TIMEOUT)); if (IS_TMO(t)) { dev_err(cd->dev, "%s: HID get descriptor timed out\n", __func__); rc = -ETIME; goto error; } cyttsp5_pr_buf(cd->dev, cd->response_buf, cd->hid_core.hid_report_desc_len, "Report Desc"); rc = parse_report_descriptor(cd, cd->response_buf + 3, get_unaligned_le16(&cd->response_buf[0]) - 3); if (rc) { dev_err(cd->dev, "%s: Error parsing report descriptor r=%d\n", __func__, rc); } parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: %d reports found in descriptor\n", __func__, cd->num_hid_reports); for (t = 0; t < cd->num_hid_reports; t++) { struct cyttsp5_hid_report *report = cd->hid_reports[t]; int j; parade_debug(cd->dev, DEBUG_LEVEL_2, "Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n", t, report->type, report->id, report->size, report->num_fields, report->record_field_index, report->header_size, report->record_size, report->usage_page); for (j = 0; j < report->num_fields; j++) { struct cyttsp5_hid_field *field = report->fields[j]; parade_debug(cd->dev, DEBUG_LEVEL_2, " Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", j, field->report_count, field->report_size, field->offset, field->data_type, field->logical_min, field->logical_max, field->usage_page); parade_debug(cd->dev, DEBUG_LEVEL_2, " Collections Phys:%08X App:%08X Log:%08X\n", field->collection_usage_pages [HID_COLLECTION_PHYSICAL], field->collection_usage_pages [HID_COLLECTION_APPLICATION], field->collection_usage_pages [HID_COLLECTION_LOGICAL]); } } rc = setup_report_descriptor(cd); /* Free it for now */ cyttsp5_free_hid_reports_(cd); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: %d reports found in descriptor\n", __func__, cd->num_hid_reports); goto exit; error: mutex_lock(&cd->system_lock); cd->hid_cmd_state = 0; mutex_unlock(&cd->system_lock); exit: return rc; } static int cyttsp5_get_mode(struct cyttsp5_core_data *cd, struct cyttsp5_hid_desc *desc) { if (desc->packet_id == CY_HID_APP_REPORT_ID) return CY_MODE_OPERATIONAL; else if (desc->packet_id == CY_HID_BL_REPORT_ID) return CY_MODE_BOOTLOADER; return CY_MODE_UNKNOWN; } static int _cyttsp5_request_get_mode(struct device *dev, int protect, u8 *mode) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); int rc; if (protect) rc = cyttsp5_get_hid_descriptor(cd, &cd->hid_desc); else rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); if (rc) *mode = CY_MODE_UNKNOWN; else *mode = cyttsp5_get_mode(cd, &cd->hid_desc); return rc; } static void cyttsp5_queue_startup_(struct cyttsp5_core_data *cd) { if (cd->startup_state == STARTUP_NONE) { cd->startup_state = STARTUP_QUEUED; schedule_work(&cd->startup_work); dev_info(cd->dev, "%s: cyttsp5_startup queued\n", __func__); } else { parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: startup_state = %d\n", __func__, cd->startup_state); } } static void cyttsp5_queue_startup(struct cyttsp5_core_data *cd) { mutex_lock(&cd->system_lock); cyttsp5_queue_startup_(cd); mutex_unlock(&cd->system_lock); } static void call_atten_cb(struct cyttsp5_core_data *cd, enum cyttsp5_atten_type type, int mode) { struct atten_node *atten, *atten_n; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: check list type=%d mode=%d\n", __func__, type, mode); spin_lock(&cd->spinlock); list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) { if (!mode || atten->mode & mode) { spin_unlock(&cd->spinlock); parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: attention for '%s'", __func__, dev_name(atten->dev)); atten->func(atten->dev); spin_lock(&cd->spinlock); } } spin_unlock(&cd->spinlock); } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE static void cyttsp5_start_wd_timer(struct cyttsp5_core_data *cd) { if (!cd->watchdog_interval){ //printk("++++++cyttsp5_start_wd_timer %d\n",cd->watchdog_interval); return; } mod_timer(&cd->watchdog_timer, jiffies + msecs_to_jiffies(cd->watchdog_interval)); } static void cyttsp5_stop_wd_timer(struct cyttsp5_core_data *cd) { if (!cd->watchdog_interval) { //printk("++++++cyttsp5_stop_wd_timer %d\n",cd->watchdog_interval); return; } /* * Ensure we wait until the watchdog timer * running on a different CPU finishes */ del_timer_sync(&cd->watchdog_timer); cancel_work_sync(&cd->watchdog_work); del_timer_sync(&cd->watchdog_timer); } static int start_fw_upgrade(void *data) { struct cyttsp5_core_data *cd = (struct cyttsp5_core_data *)data; call_atten_cb(cd, CY_ATTEN_LOADER, 0); return 0; } static void cyttsp5_watchdog_work(struct work_struct *work) { struct cyttsp5_core_data *cd = container_of(work, struct cyttsp5_core_data, watchdog_work); int rc; /*fix CDT207254 *if found the current sleep_state is SS_SLEEPING *then no need to request_exclusive, directly return */ if (cd->sleep_state == SS_SLEEPING) return; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); goto queue_startup; } rc = cyttsp5_hid_output_null_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); queue_startup: if (rc) { dev_err(cd->dev, "%s: failed to access device in watchdog timer r=%d\n", __func__, rc); /* Already tried FW upgrade because of watchdog but failed */ if (cd->startup_retry_count > CY_WATCHDOG_RETRY_COUNT) return; if (cd->startup_retry_count++ < CY_WATCHDOG_RETRY_COUNT) cyttsp5_queue_startup(cd); else kthread_run(start_fw_upgrade, cd, "cyttp5_loader"); return; } cyttsp5_start_wd_timer(cd); } static void cyttsp5_watchdog_timer(struct timer_list *t)//(unsigned long handle) { struct cyttsp5_core_data *cd = from_timer(cd, t, watchdog_timer); if (!cd) return; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Watchdog timer triggered\n", __func__); if (!work_pending(&cd->watchdog_work)) schedule_work(&cd->watchdog_work); } #endif static int cyttsp5_put_device_into_easy_wakeup_(struct cyttsp5_core_data *cd) { int rc; u8 status = 0; mutex_lock(&cd->system_lock); cd->wait_until_wake = 0; mutex_unlock(&cd->system_lock); rc = cyttsp5_hid_output_enter_easywake_state_(cd, cd->easy_wakeup_gesture, &status); if (rc || status == 0) return -EBUSY; return rc; } static int cyttsp5_put_device_into_deep_sleep_(struct cyttsp5_core_data *cd) { int rc; rc = cyttsp5_hid_cmd_set_power_(cd, HID_POWER_SLEEP); if (rc) rc = -EBUSY; return rc; } static int cyttsp5_put_device_into_sleep_(struct cyttsp5_core_data *cd) { int rc; //dev_err(cd->dev, "%s: \n", // __func__); if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) rc = cyttsp5_put_device_into_deep_sleep_(cd); else rc = cyttsp5_put_device_into_easy_wakeup_(cd); return rc; } static int cyttsp5_core_poweroff_device_(struct cyttsp5_core_data *cd) { int rc; //dev_err(cd->dev, "%s: irq_enabled=0x%x\n", // __func__, cd->irq_enabled); if (cd->irq_enabled) { cd->irq_enabled = false; disable_irq_nosync(cd->irq); } rc = cd->cpdata->power(cd->cpdata, 0, cd->dev, 0); if (rc < 0) dev_err(cd->dev, "%s: HW Power down fails r=%d\n", __func__, rc); return rc; } static int cyttsp5_core_sleep_(struct cyttsp5_core_data *cd) { int rc; mutex_lock(&cd->system_lock); if (cd->sleep_state == SS_SLEEP_OFF) { cd->sleep_state = SS_SLEEPING; } else { mutex_unlock(&cd->system_lock); return 1; } mutex_unlock(&cd->system_lock); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE /* Ensure watchdog and startup works stopped */ cyttsp5_stop_wd_timer(cd); #endif cancel_work_sync(&cd->startup_work); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_stop_wd_timer(cd); #endif //dev_err(cd->dev, "%s: flags=0x%x\n", // __func__, cd->cpdata->flags); if (cd->cpdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) rc = cyttsp5_core_poweroff_device_(cd); else rc = cyttsp5_put_device_into_sleep_(cd); mutex_lock(&cd->system_lock); cd->sleep_state = SS_SLEEP_ON; mutex_unlock(&cd->system_lock); return rc; } static int cyttsp5_core_sleep(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_core_sleep_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); else parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n", __func__); return rc; } static int cyttsp5_wakeup_host(struct cyttsp5_core_data *cd) { #ifndef EASYWAKE_TSG6 /* TSG5 EasyWake */ int rc = 0; int event_id; int size = get_unaligned_le16(&cd->input_buf[0]); /* Validate report */ if (size != 4 || cd->input_buf[2] != 4) rc = -EINVAL; cd->wake_initiated_by_device = 1; event_id = cd->input_buf[3]; parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: e=%d, rc=%d\n", __func__, event_id, rc); if (rc) { cyttsp5_core_sleep_(cd); goto exit; } /* attention WAKE */ call_atten_cb(cd, CY_ATTEN_WAKE, 0); exit: return rc; #else /* TSG6 FW1.3 EasyWake */ int rc = 0; int i = 0; int report_length; /* Validate report */ if (cd->input_buf[2] != 4) rc = -EINVAL; cd->wake_initiated_by_device = 1; parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: rc=%d\n", __func__, rc); if (rc) { cyttsp5_core_sleep_(cd); goto exit; } /* Get gesture id and gesture data length */ cd->gesture_id = cd->input_buf[3]; report_length = (cd->input_buf[1] << 8) | (cd->input_buf[0]); cd->gesture_data_length = report_length - 4; parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: gesture_id = %d, gesture_data_length = %d\n", __func__, cd->gesture_id, cd->gesture_data_length); for (i = 0; i < cd->gesture_data_length; i++) cd->gesture_data[i] = cd->input_buf[4 + i]; /* attention WAKE */ call_atten_cb(cd, CY_ATTEN_WAKE, 0); exit: return rc; #endif } static void cyttsp5_get_touch_axis(struct cyttsp5_core_data *cd, int *axis, int size, int max, u8 *data, int bofs) { int nbyte; int next; for (nbyte = 0, *axis = 0, next = 0; nbyte < size; nbyte++) { *axis = *axis + ((data[next] >> bofs) << (nbyte * 8)); next++; } *axis &= max - 1; } static int move_tracking_hetmap_data(struct cyttsp5_core_data *cd, struct cyttsp5_sysinfo *si) { #ifdef TTHE_TUNER_SUPPORT int size = get_unaligned_le16(&cd->input_buf[0]); if (size) tthe_print(cd, cd->input_buf, size, "THM="); #endif memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE); return 0; } static int move_sensor_data(struct cyttsp5_core_data *cd, struct cyttsp5_sysinfo *si) { #ifdef TTHE_TUNER_SUPPORT int size = get_unaligned_le16(&cd->input_buf[0]); if (size) tthe_print(cd, cd->input_buf, size, "sensor_monitor="); #endif memcpy(si->xy_mode, cd->input_buf, SENSOR_HEADER_SIZE); return 0; } static int move_button_data(struct cyttsp5_core_data *cd, struct cyttsp5_sysinfo *si) { #ifdef TTHE_TUNER_SUPPORT int size = get_unaligned_le16(&cd->input_buf[0]); if (size) tthe_print(cd, cd->input_buf, size, "OpModeData="); #endif memcpy(si->xy_mode, cd->input_buf, BTN_INPUT_HEADER_SIZE); cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE, "xy_mode"); memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE], BTN_REPORT_SIZE); cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_data, BTN_REPORT_SIZE, "xy_data"); return 0; } static int move_touch_data(struct cyttsp5_core_data *cd, struct cyttsp5_sysinfo *si) { int max_tch = si->sensing_conf_data.max_tch; int num_cur_tch; int length; struct cyttsp5_tch_abs_params *tch = &si->tch_hdr[CY_TCH_NUM]; #ifdef TTHE_TUNER_SUPPORT int size = get_unaligned_le16(&cd->input_buf[0]); if (size) tthe_print(cd, cd->input_buf, size, "OpModeData="); #endif if (si->desc.tch_header_size > CY_MAX_INPUT) { dev_err(cd->dev, "%s:%d: size=%d is larger than the max %d!!!\n", __func__, __LINE__, si->desc.tch_header_size, CY_MAX_INPUT); return -EINVAL; } memcpy(si->xy_mode, cd->input_buf, si->desc.tch_header_size); cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_mode, si->desc.tch_header_size, "xy_mode"); cyttsp5_get_touch_axis(cd, &num_cur_tch, tch->size, tch->max, si->xy_mode + 3 + tch->ofs, tch->bofs); if (unlikely(num_cur_tch > max_tch)) num_cur_tch = max_tch; length = num_cur_tch * si->desc.tch_record_size; if ((length + si->desc.tch_header_size) > CY_MAX_INPUT) { dev_err(cd->dev, "%s:%d: size=%d is larger than the max %d!!!\n", __func__, __LINE__, length + si->desc.tch_header_size, CY_MAX_INPUT); return -EINVAL; } memcpy(si->xy_data, &cd->input_buf[si->desc.tch_header_size], length); cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_data, length, "xy_data"); return 0; } static int parse_touch_input(struct cyttsp5_core_data *cd, int size) { struct cyttsp5_sysinfo *si = &cd->sysinfo; int report_id = cd->input_buf[2]; int rc = -EINVAL; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Received touch report\n", __func__); if (!si->ready) { dev_err(cd->dev, "%s: Need system information to parse touches\n", __func__); return 0; } if (!si->xy_mode || !si->xy_data) return rc; if (report_id == si->desc.tch_report_id) rc = move_touch_data(cd, si); else if (report_id == si->desc.btn_report_id) rc = move_button_data(cd, si); else if (report_id == HID_SENSOR_DATA_REPORT_ID) rc = move_sensor_data(cd, si); else if (report_id == HID_TRACKING_HEATMAP_REPOR_ID) rc = move_tracking_hetmap_data(cd, si); if (rc) return rc; /* attention IRQ */ call_atten_cb(cd, CY_ATTEN_IRQ, cd->mode); return 0; } static int parse_command_input(struct cyttsp5_core_data *cd, int size) { parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Received cmd interrupt\n", __func__); if (size > CY_MAX_INPUT) { dev_err(cd->dev, "%s:%d: size=%d is larger than the max %d!!!\n", __func__, __LINE__, size, CY_MAX_INPUT); return -EINVAL; } memcpy(cd->response_buf, cd->input_buf, size); mutex_lock(&cd->system_lock); cd->hid_cmd_state = 0; mutex_unlock(&cd->system_lock); wake_up(&cd->wait_q); return 0; } static int cyttsp5_parse_input(struct cyttsp5_core_data *cd) { int report_id; int is_command = 0; int size; //printk("*******cyttsp5_parse_input \n"); size = get_unaligned_le16(&cd->input_buf[0]); /* check reset */ if (size == 0) { parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Reset complete\n", __func__); memcpy(cd->response_buf, cd->input_buf, 2); mutex_lock(&cd->system_lock); if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) { mutex_unlock(&cd->system_lock); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Device Initiated Reset\n", __func__); return 0; } cd->hid_reset_cmd_state = 0; if (cd->hid_cmd_state == HID_OUTPUT_START_BOOTLOADER + 1 || cd->hid_cmd_state == HID_OUTPUT_BL_LAUNCH_APP + 1 || cd->hid_cmd_state == HID_OUTPUT_USER_CMD + 1) cd->hid_cmd_state = 0; wake_up(&cd->wait_q); mutex_unlock(&cd->system_lock); return 0; } else if (size == 2 || size >= CY_PIP_1P7_EMPTY_BUF || size > CY_MAX_INPUT) { /* * Before PIP 1.7, empty buffer is 0x0002; * From PIP 1.7, empty buffer is 0xFFXX */ if (size > CY_MAX_INPUT && size < CY_PIP_1P7_EMPTY_BUF) dev_err(cd->dev, "%s:%d: size=%d is larger than the max %d!!!\n", __func__, __LINE__, size, CY_MAX_INPUT); return 0; } report_id = cd->input_buf[2]; parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: report_id:%X\n", __func__, report_id); /* Check wake-up report */ if (report_id == HID_WAKEUP_REPORT_ID) { cyttsp5_wakeup_host(cd); return 0; } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE /* update watchdog expire time */ mod_timer_pending(&cd->watchdog_timer, jiffies + msecs_to_jiffies(cd->watchdog_interval)); #endif if (report_id != cd->sysinfo.desc.tch_report_id && report_id != cd->sysinfo.desc.btn_report_id && report_id != HID_SENSOR_DATA_REPORT_ID && report_id != HID_TRACKING_HEATMAP_REPOR_ID) is_command = 1; if (unlikely(is_command)) { parse_command_input(cd, size); return 0; } parse_touch_input(cd, size); return 0; } static int cyttsp5_read_input(struct cyttsp5_core_data *cd) { struct device *dev = cd->dev; int rc; int t; //printk("**********cyttsp5_read_input \n"); /* added as workaround to CDT170960: easywake failure */ /* Interrupt for easywake, wait for bus controller to wake */ mutex_lock(&cd->system_lock); if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) { if (cd->sleep_state == SS_SLEEP_ON) { mutex_unlock(&cd->system_lock); if (!dev->power.is_suspended) goto read; t = wait_event_timeout(cd->wait_q, (cd->wait_until_wake == 1), msecs_to_jiffies(2000)); if (IS_TMO(t)) cyttsp5_queue_startup(cd); goto read; } } mutex_unlock(&cd->system_lock); read: rc = cyttsp5_adap_read_default_nosize(cd, cd->input_buf, CY_MAX_INPUT); if (rc) { dev_err(dev, "%s: Error getting report, r=%d\n", __func__, rc); return rc; } parade_debug(dev, DEBUG_LEVEL_2, "%s: Read input successfully\n", __func__); return rc; } static bool cyttsp5_check_irq_asserted(struct cyttsp5_core_data *cd) { #ifdef ENABLE_WORKAROUND_FOR_GLITCH_AFTER_BL_LAUNCH_APP /* * Workaround for FW defect, CDT165308 * bl_launch app creates a glitch in IRQ line */ if (cd->hid_cmd_state == HID_OUTPUT_BL_LAUNCH_APP + 1 && cd->cpdata->irq_stat){ /* * in X1S panel and GC1546 panel, the width for the INT * glitch is about 4us,the normal INT width of response * will last more than 200us, so use 10us delay * for distinguish the glitch the normal INT is enough. */ udelay(10); if (cd->cpdata->irq_stat(cd->cpdata, cd->dev) != CY_IRQ_ASSERTED_VALUE) return false; } #endif return true; } static irqreturn_t cyttsp5_irq(int irq, void *handle) { struct cyttsp5_core_data *cd = handle; int rc; //printk("&&&&&&cyttsp5_irq \n"); if (!cyttsp5_check_irq_asserted(cd)) return IRQ_HANDLED; pm_stay_awake(cd->dev); rc = cyttsp5_read_input(cd); if (!rc) cyttsp5_parse_input(cd); pm_relax(cd->dev); return IRQ_HANDLED; } int _cyttsp5_subscribe_attention(struct device *dev, enum cyttsp5_atten_type type, char *id, int (*func)(struct device *), int mode) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); struct atten_node *atten, *atten_new; atten_new = kzalloc(sizeof(*atten_new), GFP_KERNEL); if (!atten_new) return -ENOMEM; parade_debug(cd->dev, DEBUG_LEVEL_1, "%s from '%s'\n", __func__, dev_name(cd->dev)); spin_lock(&cd->spinlock); list_for_each_entry(atten, &cd->atten_list[type], node) { if (atten->id == id && atten->mode == mode) { spin_unlock(&cd->spinlock); kfree(atten_new); parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: %s=%p %s=%d\n", __func__, "already subscribed attention", dev, "mode", mode); return 0; } } atten_new->id = id; atten_new->dev = dev; atten_new->mode = mode; atten_new->func = func; list_add(&atten_new->node, &cd->atten_list[type]); spin_unlock(&cd->spinlock); return 0; } int _cyttsp5_unsubscribe_attention(struct device *dev, enum cyttsp5_atten_type type, char *id, int (*func)(struct device *), int mode) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); struct atten_node *atten, *atten_n; spin_lock(&cd->spinlock); list_for_each_entry_safe(atten, atten_n, &cd->atten_list[type], node) { if (atten->id == id && atten->mode == mode) { list_del(&atten->node); spin_unlock(&cd->spinlock); parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: %s=%p %s=%d\n", __func__, "unsub for atten->dev", atten->dev, "atten->mode", atten->mode); kfree(atten); return 0; } } spin_unlock(&cd->spinlock); return -ENODEV; } static int _cyttsp5_request_exclusive(struct device *dev, int timeout_ms) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); return request_exclusive(cd, (void *)dev, timeout_ms); } static int _cyttsp5_release_exclusive(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); return release_exclusive(cd, (void *)dev); } static int cyttsp5_reset(struct cyttsp5_core_data *cd) { int rc; /* reset hardware */ parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: reset hw...\n", __func__); rc = cyttsp5_hw_reset(cd); if (rc < 0) dev_err(cd->dev, "%s: %s dev='%s' r=%d\n", __func__, "Fail hw reset", dev_name(cd->dev), rc); return rc; } static int cyttsp5_reset_and_wait(struct cyttsp5_core_data *cd) { int rc; int t; mutex_lock(&cd->system_lock); cd->hid_reset_cmd_state = 1; mutex_unlock(&cd->system_lock); rc = cyttsp5_reset(cd); if (rc < 0) goto error; t = wait_event_timeout(cd->wait_q, (cd->hid_reset_cmd_state == 0), msecs_to_jiffies(CY_HID_RESET_TIMEOUT)); if (IS_TMO(t)) { dev_err(cd->dev, "%s: reset timed out\n", __func__); rc = -ETIME; goto error; } goto exit; error: mutex_lock(&cd->system_lock); cd->hid_reset_cmd_state = 0; mutex_unlock(&cd->system_lock); exit: return rc; } /* * returns err if refused or timeout(core uses fixed timeout period) occurs; * blocks until ISR occurs */ static int _cyttsp5_request_reset(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); int rc; mutex_lock(&cd->system_lock); cd->hid_reset_cmd_state = 1; mutex_unlock(&cd->system_lock); rc = cyttsp5_reset(cd); if (rc < 0) { dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); mutex_lock(&cd->system_lock); cd->hid_reset_cmd_state = 0; mutex_unlock(&cd->system_lock); } return rc; } /* * returns err if refused ; if no error then restart has completed * and system is in normal operating mode */ static int _cyttsp5_request_restart(struct device *dev, bool wait) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); cyttsp5_queue_startup(cd); if (wait) wait_event(cd->wait_q, cd->startup_state == STARTUP_NONE); return 0; } /* * returns NULL if sysinfo has not been acquired from the device yet */ struct cyttsp5_sysinfo *_cyttsp5_request_sysinfo(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (cd->sysinfo.ready) return &cd->sysinfo; return NULL; } static struct cyttsp5_loader_platform_data *_cyttsp5_request_loader_pdata( struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); return cd->pdata->loader_pdata; } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE static int _cyttsp5_request_start_wd(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); cyttsp5_start_wd_timer(cd); return 0; } static int _cyttsp5_request_stop_wd(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); cyttsp5_stop_wd_timer(cd); return 0; } #endif static int cyttsp5_core_wake_device_from_deep_sleep_( struct cyttsp5_core_data *cd) { int rc; rc = cyttsp5_hid_cmd_set_power_(cd, HID_POWER_ON); if (rc) rc = -EAGAIN; /* Prevent failure on sequential wake/sleep requests from OS */ msleep(20); return rc; } static int cyttsp5_core_wake_device_(struct cyttsp5_core_data *cd) { if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) { mutex_lock(&cd->system_lock); cd->wait_until_wake = 1; mutex_unlock(&cd->system_lock); wake_up(&cd->wait_q); msleep(20); if (cd->wake_initiated_by_device) { cd->wake_initiated_by_device = 0; return 0; } } return cyttsp5_core_wake_device_from_deep_sleep_(cd); } static int cyttsp5_restore_parameters_(struct cyttsp5_core_data *cd) { struct param_node *param; int rc = 0; if (!(cd->cpdata->flags & CY_CORE_FLAG_RESTORE_PARAMETERS)) goto exit; spin_lock(&cd->spinlock); list_for_each_entry(param, &cd->param_list, node) { spin_unlock(&cd->spinlock); parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Parameter id:%d value:%d\n", __func__, param->id, param->value); rc = cyttsp5_hid_output_set_param_(cd, param->id, param->value, param->size); if (rc) goto exit; spin_lock(&cd->spinlock); } spin_unlock(&cd->spinlock); exit: return rc; } static int _fast_startup(struct cyttsp5_core_data *cd) { int retry = CY_CORE_STARTUP_RETRY_COUNT; int rc; reset: if (retry != CY_CORE_STARTUP_RETRY_COUNT) parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Retry %d\n", __func__, CY_CORE_STARTUP_RETRY_COUNT - retry); rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); if (rc < 0) { dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); if (cd->mode == CY_MODE_BOOTLOADER) { dev_info(cd->dev, "%s: Bootloader mode\n", __func__); rc = cyttsp5_hid_output_bl_launch_app_(cd); if (rc < 0) { dev_err(cd->dev, "%s: Error on launch app r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); if (rc < 0) { dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); if (cd->mode == CY_MODE_BOOTLOADER) { if (retry--) goto reset; goto exit; } } rc = cyttsp5_restore_parameters_(cd); if (rc) dev_err(cd->dev, "%s: failed to restore parameters rc=%d\n", __func__, rc); exit: return rc; } static int cyttsp5_core_poweron_device_(struct cyttsp5_core_data *cd) { struct device *dev = cd->dev; int rc; rc = cd->cpdata->power(cd->cpdata, 1, dev, 0); if (rc < 0) { dev_err(dev, "%s: HW Power up fails r=%d\n", __func__, rc); goto exit; } if (!cd->irq_enabled) { cd->irq_enabled = true; enable_irq(cd->irq); } rc = _fast_startup(cd); exit: return rc; } static int cyttsp5_core_wake_(struct cyttsp5_core_data *cd) { int rc; mutex_lock(&cd->system_lock); if (cd->sleep_state == SS_SLEEP_ON) { cd->sleep_state = SS_WAKING; } else { mutex_unlock(&cd->system_lock); return 1; } mutex_unlock(&cd->system_lock); if (cd->cpdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) rc = cyttsp5_core_poweron_device_(cd); else rc = cyttsp5_core_wake_device_(cd); mutex_lock(&cd->system_lock); cd->sleep_state = SS_SLEEP_OFF; mutex_unlock(&cd->system_lock); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_start_wd_timer(cd); #endif return rc; } static int cyttsp5_core_wake(struct cyttsp5_core_data *cd) { int rc; rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); return rc; } rc = cyttsp5_core_wake_(cd); if (release_exclusive(cd, cd->dev) < 0) dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); else parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n", __func__); return rc; } static int cyttsp5_get_ic_crc_(struct cyttsp5_core_data *cd, u8 ebid) { struct cyttsp5_sysinfo *si = &cd->sysinfo; int rc; u8 status; u16 calculated_crc = 0; u16 stored_crc = 0; rc = cyttsp5_hid_output_suspend_scanning_(cd); if (rc) goto error; rc = cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, &status, &calculated_crc, &stored_crc); if (rc) goto exit; if (status) { rc = -EINVAL; goto exit; } si->ttconfig.crc = stored_crc; exit: cyttsp5_hid_output_resume_scanning_(cd); error: parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: CRC: ebid:%d, crc:0x%04X\n", __func__, ebid, si->ttconfig.crc); return rc; } static int cyttsp5_check_and_deassert_int(struct cyttsp5_core_data *cd) { u16 size; u8 buf[2]; u8 *p; u8 retry = 3; int rc; do { rc = cyttsp5_adap_read_default(cd, buf, 2); if (rc < 0) return rc; size = get_unaligned_le16(&buf[0]); if (size == 2 || size == 0 || size >= CY_PIP_1P7_EMPTY_BUF) return 0; p = kzalloc(size, GFP_KERNEL); if (!p) return -ENOMEM; rc = cyttsp5_adap_read_default(cd, p, size); kfree(p); if (rc < 0) return rc; } while (retry--); return -EINVAL; } static int cyttsp5_startup_(struct cyttsp5_core_data *cd, bool reset) { int retry = CY_CORE_STARTUP_RETRY_COUNT; int rc; bool detected = false; #ifdef TTHE_TUNER_SUPPORT tthe_print(cd, NULL, 0, "enter startup"); #endif #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_stop_wd_timer(cd); #endif //printk("++++++++++cyttsp5_startup_ start reset=%d\n", reset); reset: if (retry != CY_CORE_STARTUP_RETRY_COUNT) parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Retry %d\n", __func__, CY_CORE_STARTUP_RETRY_COUNT - retry); rc = cyttsp5_check_and_deassert_int(cd); if (reset || retry != CY_CORE_STARTUP_RETRY_COUNT) { /* reset hardware */ //printk("++++++++++cyttsp5_startup_ cyttsp5_reset_and_wait\n"); rc = cyttsp5_reset_and_wait(cd); if (rc < 0) { dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", __func__, rc); if (retry--) goto reset; //printk("++++++++++cyttsp5_startup_ goto exit\n"); goto exit; } } //printk("++++++++++cyttsp5_startup_ next\n"); rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); if (rc < 0) { dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); detected = true; /* Switch to bootloader mode to get Panel ID */ if (cd->mode == CY_MODE_OPERATIONAL) { rc = cyttsp5_hid_output_start_bootloader_(cd); if (rc < 0) { dev_err(cd->dev, "%s: Error on start bootloader r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } dev_info(cd->dev, "%s: Bootloader mode\n", __func__); } cyttsp5_hid_output_bl_get_panel_id_(cd, &cd->panel_id); parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Panel ID: 0x%02X\n", __func__, cd->panel_id); rc = cyttsp5_hid_output_bl_launch_app_(cd); if (rc < 0) { dev_err(cd->dev, "%s: Error on launch app r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); if (rc < 0) { dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); if (cd->mode == CY_MODE_BOOTLOADER) { if (retry--) goto reset; goto exit; } mutex_lock(&cd->system_lock); /* Read descriptor lengths */ cd->hid_core.hid_report_desc_len = le16_to_cpu(cd->hid_desc.report_desc_len); cd->hid_core.hid_max_input_len = le16_to_cpu(cd->hid_desc.max_input_len); cd->hid_core.hid_max_output_len = le16_to_cpu(cd->hid_desc.max_output_len); cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); if (cd->mode == CY_MODE_OPERATIONAL) dev_info(cd->dev, "%s: Operational mode\n", __func__); else if (cd->mode == CY_MODE_BOOTLOADER) dev_info(cd->dev, "%s: Bootloader mode\n", __func__); else if (cd->mode == CY_MODE_UNKNOWN) { dev_err(cd->dev, "%s: Unknown mode\n", __func__); rc = -ENODEV; mutex_unlock(&cd->system_lock); if (retry--) goto reset; goto exit; } mutex_unlock(&cd->system_lock); dev_info(cd->dev, "%s: Reading report descriptor\n", __func__); rc = cyttsp5_get_report_descriptor_(cd); if (rc < 0) { dev_err(cd->dev, "%s: Error on getting report descriptor r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } if (!cd->features.easywake) cd->easy_wakeup_gesture = CY_CORE_EWG_NONE; rc = cyttsp5_hid_output_get_sysinfo_(cd); if (rc) { dev_err(cd->dev, "%s: Error on getting sysinfo r=%d\n", __func__, rc); if (retry--) goto reset; goto exit; } dev_info(cd->dev, "cyttsp5 Protocol Version: %d.%d\n", cd->sysinfo.cydata.pip_ver_major, cd->sysinfo.cydata.pip_ver_minor); /* Read config version directly if PIP version < 1.2 */ if (!IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) { rc = cyttsp5_get_config_ver_(cd); if (rc) dev_err(cd->dev, "%s: failed to read config version rc=%d\n", __func__, rc); } rc = cyttsp5_get_ic_crc_(cd, CY_TCH_PARM_EBID); if (rc) dev_err(cd->dev, "%s: failed to crc data rc=%d\n", __func__, rc); rc = cyttsp5_restore_parameters_(cd); if (rc) dev_err(cd->dev, "%s: failed to restore parameters rc=%d\n", __func__, rc); /* attention startup */ call_atten_cb(cd, CY_ATTEN_STARTUP, 0); exit: if (!rc) cd->startup_retry_count = 0; #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_start_wd_timer(cd); #endif if (!detected) rc = -ENODEV; #ifdef TTHE_TUNER_SUPPORT tthe_print(cd, NULL, 0, "exit startup"); #endif return rc; } static int cyttsp5_startup(struct cyttsp5_core_data *cd, bool reset) { int rc; mutex_lock(&cd->system_lock); cd->startup_state = STARTUP_RUNNING; mutex_unlock(&cd->system_lock); rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); if (rc < 0) { dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", __func__, cd->exclusive_dev, cd->dev); goto exit; } rc = cyttsp5_startup_(cd, reset); if (release_exclusive(cd, cd->dev) < 0) /* Don't return fail code, mode is already changed. */ dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); else parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n", __func__); exit: mutex_lock(&cd->system_lock); cd->startup_state = STARTUP_NONE; mutex_unlock(&cd->system_lock); /* Wake the waiters for end of startup */ wake_up(&cd->wait_q); return rc; } static void cyttsp5_startup_work_function(struct work_struct *work) { struct cyttsp5_core_data *cd = container_of(work, struct cyttsp5_core_data, startup_work); int rc; rc = cyttsp5_startup(cd, true); if (rc < 0) dev_err(cd->dev, "%s: Fail queued startup r=%d\n", __func__, rc); } /* * CONFIG_PM_RUNTIME option is removed in 3.19.0. */ #if defined(CONFIG_PM_RUNTIME) || \ (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE) static int cyttsp5_core_rt_suspend(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); int rc; //printk("cyttsp5_core_rt_suspend\n"); rc = cyttsp5_core_sleep(cd); if (rc < 0) { dev_err(dev, "%s: Error on sleep\n", __func__); return -EAGAIN; } return 0; } static int cyttsp5_core_rt_resume(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); int rc; //printk("cyttsp5_core_rt_resume\n"); rc = cyttsp5_core_wake(cd); if (rc < 0) { dev_err(dev, "%s: Error on wake\n", __func__); return -EAGAIN; } return 0; } #endif #if defined(CONFIG_PM_SLEEP) static int cyttsp5_core_suspend(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (cd->is_suspend) return 0; cyttsp5_core_sleep(cd); return 0; } static int cyttsp5_core_resume(struct device *dev) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); if (cd->is_suspend) return 0; cyttsp5_core_wake(cd); return 0; } static int cyttsp5_core_early_suspend(struct tp_device *tp_d) { struct cyttsp5_core_data *cd = container_of(tp_d, struct cyttsp5_core_data, tp); cyttsp5_core_sleep(cd); if (!cd->irq_disabled) { disable_irq(cd->irq); cd->irq_disabled = 1; } gpio_direction_output(cd->cpdata->rst_gpio, 0); gpio_direction_output(cd->cpdata->irq_gpio, 0); if (cd->supply) regulator_disable(cd->supply); cd->is_suspend = 1; return 0; } static int cyttsp5_core_late_resume(struct tp_device *tp_d) { struct cyttsp5_core_data *cd = container_of(tp_d, struct cyttsp5_core_data, tp); int ret; if (cd->supply) { ret = regulator_enable(cd->supply); if (ret < 0) dev_err(cd->dev, "failed to enable cyttsp5 power supply\n"); } gpio_direction_input(cd->cpdata->irq_gpio); cyttsp5_hw_hard_reset(cd); if (cd->irq_disabled) { enable_irq(cd->irq); cd->irq_disabled = 0; } cyttsp5_core_wake(cd); cd->is_suspend = 0; return 0; } #endif #if NEED_SUSPEND_NOTIFIER static int cyttsp5_pm_notifier(struct notifier_block *nb, unsigned long action, void *data) { struct cyttsp5_core_data *cd = container_of(nb, struct cyttsp5_core_data, pm_notifier); if (action == PM_SUSPEND_PREPARE) { parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Suspend prepare\n", __func__); /* * If not runtime PM suspended, either call runtime * PM suspend callback or wait until it finishes */ if (!pm_runtime_suspended(cd->dev)) pm_runtime_suspend(cd->dev); (void) cyttsp5_core_suspend(cd->dev); } return NOTIFY_DONE; } #endif const struct dev_pm_ops cyttsp5_pm_ops = { SET_LATE_SYSTEM_SLEEP_PM_OPS(cyttsp5_core_suspend, cyttsp5_core_resume) SET_RUNTIME_PM_OPS(cyttsp5_core_rt_suspend, cyttsp5_core_rt_resume, NULL) }; EXPORT_SYMBOL_GPL(cyttsp5_pm_ops); /* * Show Firmware version via sysfs */ static ssize_t cyttsp5_ic_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); struct cyttsp5_cydata *cydata = &cd->sysinfo.cydata; return sprintf(buf, "%s: 0x%02X\n" "%s: 0x%02X\n" "%s: 0x%08X\n" "%s: 0x%04X\n" "%s: 0x%02X\n" "%s: 0x%02X\n" "%s: 0x%02X\n" "%s: 0x%02X\n", "Firmware Major Version", cydata->fw_ver_major, "Firmware Minor Version", cydata->fw_ver_minor, "Revision Control Number", cydata->revctrl, "Firmware Configuration Version", cydata->fw_ver_conf, "Bootloader Major Version", cydata->bl_ver_major, "Bootloader Minor Version", cydata->bl_ver_minor, "Protocol Major Version", cydata->pip_ver_major, "Protocol Minor Version", cydata->pip_ver_minor); } /* * Show Driver version via sysfs */ static ssize_t cyttsp5_drv_ver_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, CY_MAX_PRBUF_SIZE, "Driver: %s\nVersion: %s\nDate: %s\n", cy_driver_core_name, cy_driver_core_version, cy_driver_core_date); } /* * HW reset via sysfs */ static ssize_t cyttsp5_hw_reset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); int rc; rc = cyttsp5_startup(cd, true); if (rc < 0) dev_err(dev, "%s: HW reset failed r=%d\n", __func__, rc); return size; } /* * Show IRQ status via sysfs */ static ssize_t cyttsp5_hw_irq_stat_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); int retval; if (cd->cpdata->irq_stat) { retval = cd->cpdata->irq_stat(cd->cpdata, dev); switch (retval) { case 0: return snprintf(buf, CY_MAX_PRBUF_SIZE, "Interrupt line is LOW.\n"); case 1: return snprintf(buf, CY_MAX_PRBUF_SIZE, "Interrupt line is HIGH.\n"); default: return snprintf(buf, CY_MAX_PRBUF_SIZE, "Function irq_stat() returned %d.\n", retval); } } return snprintf(buf, CY_MAX_PRBUF_SIZE, "Function irq_stat() undefined.\n"); } /* * Show IRQ enable/disable status via sysfs */ static ssize_t cyttsp5_drv_irq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); ssize_t ret; mutex_lock(&cd->system_lock); if (cd->irq_enabled) ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "Driver interrupt is ENABLED\n"); else ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "Driver interrupt is DISABLED\n"); mutex_unlock(&cd->system_lock); return ret; } /* * Enable/disable IRQ via sysfs */ static ssize_t cyttsp5_drv_irq_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); unsigned long value; int retval = 0; retval = kstrtoul(buf, 10, &value); if (retval < 0) { dev_err(dev, "%s: Invalid value\n", __func__); goto cyttsp5_drv_irq_store_error_exit; } mutex_lock(&cd->system_lock); switch (value) { case 0: if (cd->irq_enabled) { cd->irq_enabled = false; /* Disable IRQ */ disable_irq_nosync(cd->irq); dev_info(dev, "%s: Driver IRQ now disabled\n", __func__); } else dev_info(dev, "%s: Driver IRQ already disabled\n", __func__); break; case 1: if (cd->irq_enabled == false) { cd->irq_enabled = true; /* Enable IRQ */ enable_irq(cd->irq); dev_info(dev, "%s: Driver IRQ now enabled\n", __func__); } else dev_info(dev, "%s: Driver IRQ already enabled\n", __func__); break; default: dev_err(dev, "%s: Invalid value\n", __func__); } mutex_unlock(&(cd->system_lock)); cyttsp5_drv_irq_store_error_exit: return size; } /* * Gets user input from sysfs and parse it * return size of parsed output buffer */ #define CY_MAX_CONFIG_BYTES_DEC 256 #define CYTTSP5_INPUT_ELEM_SZ_DEC 10 static int cyttsp5_ic_parse_input_dec(struct device *dev, const char *buf, size_t buf_size, u32 *ic_buf, size_t ic_buf_size) { const char *pbuf = buf; unsigned long value; char scan_buf[CYTTSP5_INPUT_ELEM_SZ_DEC]; u32 i = 0; u32 j; int last = 0; int ret; parade_debug(dev, DEBUG_LEVEL_1, "%s: pbuf=%p buf=%p size=%zu %s=%zu buf=%s\n", __func__, pbuf, buf, buf_size, "scan buf size", (size_t)CYTTSP5_INPUT_ELEM_SZ_DEC, buf); while (pbuf <= (buf + buf_size)) { if (i >= CY_MAX_CONFIG_BYTES_DEC) { dev_err(dev, "%s: %s size=%d max=%d\n", __func__, "Max cmd size exceeded", i, CY_MAX_CONFIG_BYTES_DEC); return -EINVAL; } if (i >= ic_buf_size) { dev_err(dev, "%s: %s size=%d buf_size=%zu\n", __func__, "Buffer size exceeded", i, ic_buf_size); return -EINVAL; } while (((*pbuf == ' ') || (*pbuf == ',')) && (pbuf < (buf + buf_size))) { last = *pbuf; pbuf++; } if (pbuf >= (buf + buf_size)) break; memset(scan_buf, 0, CYTTSP5_INPUT_ELEM_SZ_DEC); if ((last == ',') && (*pbuf == ',')) { dev_err(dev, "%s: %s \",,\" not allowed.\n", __func__, "Invalid data format."); return -EINVAL; } for (j = 0; j < (CYTTSP5_INPUT_ELEM_SZ_DEC - 1) && (pbuf < (buf + buf_size)) && (*pbuf != ' ') && (*pbuf != ','); j++) { last = *pbuf; scan_buf[j] = *pbuf++; } ret = kstrtoul(scan_buf, 10, &value); if (ret < 0) { dev_err(dev, "%s: Invalid data format.\n", __func__); return ret; } ic_buf[i] = value; i++; } return i; } /* * Debugging options via sysfs */ static ssize_t cyttsp5_drv_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); unsigned long value; int rc; u8 return_data[8]; static u8 wd_disabled; u32 input_data[2]; int length; /*maximal input two data*/ length = cyttsp5_ic_parse_input_dec(dev, buf, size, input_data, 3); if (length <= 0) { dev_err(dev, "%s: %s failed\n", __func__, "cyttsp5_ic_parse_input_dec"); goto cyttsp5_drv_debug_store_exit; } value = input_data[0]; /* Start watchdog timer command */ if (value == CY_DBG_HID_START_WD) { dev_info(dev, "%s: start watchdog (cd=%p)\n", __func__, cd); wd_disabled = 0; #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_start_wd_timer(cd); #endif goto cyttsp5_drv_debug_store_exit; } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE /* Stop watchdog timer temporarily */ cyttsp5_stop_wd_timer(cd); #endif if (value == CY_DBG_HID_STOP_WD) { dev_info(dev, "%s: stop watchdog (cd=%p)\n", __func__, cd); wd_disabled = 1; goto cyttsp5_drv_debug_store_exit; } switch (value) { case CY_DBG_SUSPEND: dev_info(dev, "%s: SUSPEND (cd=%p)\n", __func__, cd); rc = cyttsp5_core_sleep(cd); if (rc) dev_err(dev, "%s: Suspend failed rc=%d\n", __func__, rc); else dev_info(dev, "%s: Suspend succeeded\n", __func__); break; case CY_DBG_RESUME: dev_info(dev, "%s: RESUME (cd=%p)\n", __func__, cd); rc = cyttsp5_core_wake(cd); if (rc) dev_err(dev, "%s: Resume failed rc=%d\n", __func__, rc); else dev_info(dev, "%s: Resume succeeded\n", __func__); break; case CY_DBG_SOFT_RESET: dev_info(dev, "%s: SOFT RESET (cd=%p)\n", __func__, cd); rc = cyttsp5_hw_soft_reset(cd); break; case CY_DBG_RESET: dev_info(dev, "%s: HARD RESET (cd=%p)\n", __func__, cd); rc = cyttsp5_hw_hard_reset(cd); break; case CY_DBG_HID_RESET: dev_info(dev, "%s: hid_reset (cd=%p)\n", __func__, cd); cyttsp5_hid_cmd_reset(cd); break; case CY_DBG_HID_SET_POWER_ON: dev_info(dev, "%s: hid_set_power_on (cd=%p)\n", __func__, cd); cyttsp5_hid_cmd_set_power(cd, HID_POWER_ON); wd_disabled = 0; break; case CY_DBG_HID_SET_POWER_SLEEP: dev_info(dev, "%s: hid_set_power_off (cd=%p)\n", __func__, cd); wd_disabled = 1; cyttsp5_hid_cmd_set_power(cd, HID_POWER_SLEEP); break; case CY_DBG_HID_NULL: dev_info(dev, "%s: hid_null (cd=%p)\n", __func__, cd); cyttsp5_hid_output_null(cd); break; case CY_DBG_HID_ENTER_BL: dev_info(dev, "%s: start_bootloader (cd=%p)\n", __func__, cd); cyttsp5_hid_output_start_bootloader(cd); break; case CY_DBG_HID_SYSINFO: dev_info(dev, "%s: get_sysinfo (cd=%p)\n", __func__, cd); cyttsp5_hid_output_get_sysinfo(cd); break; case CY_DBG_HID_SUSPEND_SCAN: dev_info(dev, "%s: suspend_scanning (cd=%p)\n", __func__, cd); cyttsp5_hid_output_suspend_scanning(cd); break; case CY_DBG_HID_RESUME_SCAN: dev_info(dev, "%s: resume_scanning (cd=%p)\n", __func__, cd); cyttsp5_hid_output_resume_scanning(cd); break; case HID_OUTPUT_BL_VERIFY_APP_INTEGRITY: dev_info(dev, "%s: verify app integ (cd=%p)\n", __func__, cd); cyttsp5_hid_output_bl_verify_app_integrity(cd, &return_data[0]); break; case HID_OUTPUT_BL_GET_INFO: dev_info(dev, "%s: bl get info (cd=%p)\n", __func__, cd); cyttsp5_hid_output_bl_get_information(cd, return_data); break; case HID_OUTPUT_BL_PROGRAM_AND_VERIFY: dev_info(dev, "%s: program and verify (cd=%p)\n", __func__, cd); cyttsp5_hid_output_bl_program_and_verify(cd, 0, NULL); break; case HID_OUTPUT_BL_LAUNCH_APP: dev_info(dev, "%s: launch app (cd=%p)\n", __func__, cd); cyttsp5_hid_output_bl_launch_app(cd); break; case HID_OUTPUT_BL_INITIATE_BL: dev_info(dev, "%s: initiate bl (cd=%p)\n", __func__, cd); cyttsp5_hid_output_bl_initiate_bl(cd, 0, NULL, 0, NULL); break; #ifdef TTHE_TUNER_SUPPORT case CY_TTHE_TUNER_EXIT: cd->tthe_exit = 1; wake_up(&cd->wait_q); kfree(cd->tthe_buf); cd->tthe_buf = NULL; cd->tthe_exit = 0; break; case CY_TTHE_BUF_CLEAN: if (cd->tthe_buf) memset(cd->tthe_buf, 0, CY_MAX_PRBUF_SIZE); else dev_info(dev, "%s : tthe_buf not existed\n", __func__); break; #endif case CY_DBG_REPORT_LEVEL: mutex_lock(&cd->system_lock); cd->debug_level = input_data[1]; dev_info(dev, "%s: Set debug_level: %d\n", __func__, cd->debug_level); mutex_unlock(&(cd->system_lock)); break; case CY_DBG_WATCHDOG_INTERVAL: mutex_lock(&cd->system_lock); if (input_data[1] > 0) cd->watchdog_interval = input_data[1]; dev_info(dev, "%s: Set watchdog_interval: %d\n", __func__, cd->watchdog_interval); mutex_unlock(&(cd->system_lock)); break; case CY_DBG_SHOW_TIMESTAMP: mutex_lock(&cd->system_lock); cd->show_timestamp = input_data[1]; dev_info(dev, "%s: Set show_timestamp: %d\n", __func__, cd->show_timestamp); mutex_unlock(&(cd->system_lock)); break; default: dev_err(dev, "%s: Invalid value\n", __func__); } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE /* Enable watchdog timer */ if (!wd_disabled) cyttsp5_start_wd_timer(cd); #endif cyttsp5_drv_debug_store_exit: return size; } /* * Show system status on deep sleep status via sysfs */ static ssize_t cyttsp5_sleep_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); ssize_t ret; mutex_lock(&cd->system_lock); if (cd->sleep_state == SS_SLEEP_ON) ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "off\n"); else ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "on\n"); mutex_unlock(&cd->system_lock); return ret; } static ssize_t cyttsp5_easy_wakeup_gesture_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); ssize_t ret; mutex_lock(&cd->system_lock); ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n", cd->easy_wakeup_gesture); mutex_unlock(&cd->system_lock); return ret; } static ssize_t cyttsp5_easy_wakeup_gesture_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); unsigned long value; int ret; if (!cd->features.easywake) return -EINVAL; ret = kstrtoul(buf, 10, &value); if (ret < 0) return ret; if (value > 0xFF) return -EINVAL; pm_runtime_get_sync(dev); mutex_lock(&cd->system_lock); if (cd->sysinfo.ready && IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) cd->easy_wakeup_gesture = (u8)value; else ret = -ENODEV; mutex_unlock(&cd->system_lock); pm_runtime_put(dev); if (ret) return ret; return size; } #ifdef EASYWAKE_TSG6 /* * Show easywake gesture id via sysfs */ static ssize_t cyttsp5_easy_wakeup_gesture_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); ssize_t ret; mutex_lock(&cd->system_lock); ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n", cd->gesture_id); mutex_unlock(&cd->system_lock); return ret; } /* * Show easywake gesture data via sysfs * The format: * x1(LSB), x1(MSB),y1(LSB), y1(MSB),x2(LSB), x2(MSB),y2(LSB), y2(MSB),... */ static ssize_t cyttsp5_easy_wakeup_gesture_data_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); ssize_t ret = 0; int i; mutex_lock(&cd->system_lock); for (i = 0; i < cd->gesture_data_length; i++) ret += snprintf(buf + ret, CY_MAX_PRBUF_SIZE - ret, "0x%02X\n", cd->gesture_data[i]); ret += snprintf(buf + ret, CY_MAX_PRBUF_SIZE - ret, "(%d bytes)\n", cd->gesture_data_length); mutex_unlock(&cd->system_lock); return ret; } #endif /* Show Panel ID via sysfs */ static ssize_t cyttsp5_panel_id_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); ssize_t ret; ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n", cd->panel_id); return ret; } /* Show platform data via sysfs */ static ssize_t cyttsp5_platform_data_show(struct device *dev, struct device_attribute *attr, char *buf) { struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); ssize_t ret; ret = sprintf(buf, "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n" "%s: %d\n", "Interrupt GPIO", pdata->core_pdata->irq_gpio, "Reset GPIO", pdata->core_pdata->rst_gpio, "Level trigger delay (us)", pdata->core_pdata->level_irq_udelay, "HID descriptor register", pdata->core_pdata->hid_desc_register, "Vendor ID", pdata->core_pdata->vendor_id, "Product ID", pdata->core_pdata->product_id, "Easy wakeup gesture", pdata->core_pdata->easy_wakeup_gesture, "Vkeys x", pdata->mt_pdata->vkeys_x, "Vkeys y", pdata->mt_pdata->vkeys_y, "Core data flags", pdata->core_pdata->flags, "MT data flags", pdata->mt_pdata->flags, "Loader data flags", pdata->loader_pdata->flags); return ret; } static struct device_attribute attributes[] = { __ATTR(ic_ver, S_IRUGO, cyttsp5_ic_ver_show, NULL), __ATTR(drv_ver, S_IRUGO, cyttsp5_drv_ver_show, NULL), __ATTR(hw_reset, S_IWUSR, NULL, cyttsp5_hw_reset_store), __ATTR(hw_irq_stat, S_IRUSR, cyttsp5_hw_irq_stat_show, NULL), __ATTR(drv_irq, S_IRUSR | S_IWUSR, cyttsp5_drv_irq_show, cyttsp5_drv_irq_store), __ATTR(drv_debug, S_IWUSR, NULL, cyttsp5_drv_debug_store), __ATTR(sleep_status, S_IRUSR, cyttsp5_sleep_status_show, NULL), __ATTR(easy_wakeup_gesture, S_IRUSR | S_IWUSR, cyttsp5_easy_wakeup_gesture_show, cyttsp5_easy_wakeup_gesture_store), #ifdef EASYWAKE_TSG6 __ATTR(easy_wakeup_gesture_id, S_IRUSR, cyttsp5_easy_wakeup_gesture_id_show, NULL), __ATTR(easy_wakeup_gesture_data, S_IRUSR, cyttsp5_easy_wakeup_gesture_data_show, NULL), #endif __ATTR(panel_id, S_IRUGO, cyttsp5_panel_id_show, NULL), __ATTR(platform_data, S_IRUGO, cyttsp5_platform_data_show, NULL), }; static int add_sysfs_interfaces(struct device *dev) { int i; for (i = 0; i < ARRAY_SIZE(attributes); i++) if (device_create_file(dev, attributes + i)) goto undo; return 0; undo: for (i--; i >= 0; i--) device_remove_file(dev, attributes + i); dev_err(dev, "%s: failed to create sysfs interface\n", __func__); return -ENODEV; } static void remove_sysfs_interfaces(struct device *dev) { u32 i; for (i = 0; i < ARRAY_SIZE(attributes); i++) device_remove_file(dev, attributes + i); } /* * ttdl_restart via sysfs */ static ssize_t cyttsp5_ttdl_restart_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int rc; struct i2c_client *client = (struct i2c_client *)container_of(dev, struct i2c_client, dev); if (is_cyttsp5_probe_success) { dev_err(dev, "%s: previous cyttsp5_probe is successful, do nothing\n", __func__); return size; } if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(dev, "%s I2C functionality not Supported\n", __func__); return -EIO; } #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT rc = cyttsp5_devtree_create_and_get_pdata(dev); if (rc < 0) return rc; #endif rc = cyttsp5_probe(cyttsp5_bus_ops_save, &client->dev, client->irq, 512); if (!rc) { is_cyttsp5_probe_success = true; dev_err(dev, "%s restart successful\n", __func__); } else { is_cyttsp5_probe_success = false; dev_err(dev, "%s: ttdl restart failed r=%d\n", __func__, rc); } #ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT if (rc) cyttsp5_devtree_clean_pdata(dev); #endif return size; } static DEVICE_ATTR(ttdl_restart, S_IWUSR, NULL, cyttsp5_ttdl_restart_store); #ifdef TTHE_TUNER_SUPPORT static int tthe_debugfs_open(struct inode *inode, struct file *filp) { struct cyttsp5_core_data *cd = inode->i_private; filp->private_data = inode->i_private; if (cd->tthe_buf) return -EBUSY; cd->tthe_buf = kzalloc(CY_MAX_PRBUF_SIZE, GFP_KERNEL); if (!cd->tthe_buf) return -ENOMEM; return 0; } static int tthe_debugfs_close(struct inode *inode, struct file *filp) { struct cyttsp5_core_data *cd = filp->private_data; filp->private_data = NULL; kfree(cd->tthe_buf); cd->tthe_buf = NULL; return 0; } static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { struct cyttsp5_core_data *cd = filp->private_data; int size; int ret; wait_event_interruptible(cd->wait_q, cd->tthe_buf_len != 0 || cd->tthe_exit); mutex_lock(&cd->tthe_lock); if (cd->tthe_exit) { mutex_unlock(&cd->tthe_lock); return 0; } if (count > cd->tthe_buf_len) size = cd->tthe_buf_len; else size = count; if (!size) { mutex_unlock(&cd->tthe_lock); return 0; } ret = copy_to_user(buf, cd->tthe_buf, cd->tthe_buf_len); if (ret == size) return -EFAULT; size -= ret; cd->tthe_buf_len -= size; mutex_unlock(&cd->tthe_lock); *ppos += size; return size; } static const struct file_operations tthe_debugfs_fops = { .open = tthe_debugfs_open, .release = tthe_debugfs_close, .read = tthe_debugfs_read, }; #endif static struct cyttsp5_core_nonhid_cmd _cyttsp5_core_nonhid_cmd = { .start_bl = _cyttsp5_request_hid_output_start_bl, .suspend_scanning = _cyttsp5_request_hid_output_suspend_scanning, .resume_scanning = _cyttsp5_request_hid_output_resume_scanning, .get_param = _cyttsp5_request_hid_output_get_param, .set_param = _cyttsp5_request_hid_output_set_param, .verify_config_block_crc = _cyttsp5_request_hid_output_verify_config_block_crc, .get_config_row_size = _cyttsp5_request_hid_output_get_config_row_size, .get_data_structure = _cyttsp5_request_hid_output_get_data_structure, .run_selftest = _cyttsp5_request_hid_output_run_selftest, .get_selftest_result = _cyttsp5_request_hid_output_get_selftest_result, .calibrate_idacs = _cyttsp5_request_hid_output_calibrate_idacs, .initialize_baselines = _cyttsp5_request_hid_output_initialize_baselines, .exec_panel_scan = _cyttsp5_request_hid_output_exec_panel_scan, .retrieve_panel_scan = _cyttsp5_request_hid_output_retrieve_panel_scan, .write_conf_block = _cyttsp5_request_hid_output_write_conf_block, .user_cmd = _cyttsp5_request_hid_output_user_cmd, .get_bl_info = _cyttsp5_request_hid_output_bl_get_information, .initiate_bl = _cyttsp5_request_hid_output_bl_initiate_bl, .launch_app = _cyttsp5_request_hid_output_launch_app, .prog_and_verify = _cyttsp5_request_hid_output_bl_program_and_verify, .verify_app_integrity = _cyttsp5_request_hid_output_bl_verify_app_integrity, .get_panel_id = _cyttsp5_request_hid_output_bl_get_panel_id, }; static struct cyttsp5_core_commands _cyttsp5_core_commands = { .subscribe_attention = _cyttsp5_subscribe_attention, .unsubscribe_attention = _cyttsp5_unsubscribe_attention, .request_exclusive = _cyttsp5_request_exclusive, .release_exclusive = _cyttsp5_release_exclusive, .request_reset = _cyttsp5_request_reset, .request_restart = _cyttsp5_request_restart, .request_sysinfo = _cyttsp5_request_sysinfo, .request_loader_pdata = _cyttsp5_request_loader_pdata, #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE .request_stop_wd = _cyttsp5_request_stop_wd, .request_start_wd = _cyttsp5_request_start_wd, #endif .request_get_hid_desc = _cyttsp5_request_get_hid_desc, .request_get_mode = _cyttsp5_request_get_mode, #ifdef TTHE_TUNER_SUPPORT .request_tthe_print = _cyttsp5_request_tthe_print, #endif .nonhid_cmd = &_cyttsp5_core_nonhid_cmd, }; struct cyttsp5_core_commands *cyttsp5_get_commands(void) { return &_cyttsp5_core_commands; } EXPORT_SYMBOL_GPL(cyttsp5_get_commands); static DEFINE_MUTEX(core_list_lock); static LIST_HEAD(core_list); static DEFINE_MUTEX(module_list_lock); static LIST_HEAD(module_list); static int core_number; static int cyttsp5_probe_module(struct cyttsp5_core_data *cd, struct cyttsp5_module *module) { struct module_node *module_node; int rc = 0; module_node = kzalloc(sizeof(*module_node), GFP_KERNEL); if (!module_node) return -ENOMEM; module_node->module = module; mutex_lock(&cd->module_list_lock); list_add(&module_node->node, &cd->module_list); mutex_unlock(&cd->module_list_lock); rc = module->probe(cd->dev, &module_node->data); if (rc) { /* * Remove from the list when probe fails * in order not to call release */ mutex_lock(&cd->module_list_lock); list_del(&module_node->node); mutex_unlock(&cd->module_list_lock); kfree(module_node); goto exit; } exit: return rc; } static void cyttsp5_release_module(struct cyttsp5_core_data *cd, struct cyttsp5_module *module) { struct module_node *m, *m_n; mutex_lock(&cd->module_list_lock); list_for_each_entry_safe(m, m_n, &cd->module_list, node) if (m->module == module) { module->release(cd->dev, m->data); list_del(&m->node); kfree(m); break; } mutex_unlock(&cd->module_list_lock); } static void cyttsp5_probe_modules(struct cyttsp5_core_data *cd) { struct cyttsp5_module *m; int rc = 0; mutex_lock(&module_list_lock); list_for_each_entry(m, &module_list, node) { rc = cyttsp5_probe_module(cd, m); if (rc) dev_err(cd->dev, "%s: Probe fails for module %s\n", __func__, m->name); } mutex_unlock(&module_list_lock); } static void cyttsp5_release_modules(struct cyttsp5_core_data *cd) { struct cyttsp5_module *m; mutex_lock(&module_list_lock); list_for_each_entry(m, &module_list, node) cyttsp5_release_module(cd, m); mutex_unlock(&module_list_lock); } struct cyttsp5_core_data *cyttsp5_get_core_data(char *id) { struct cyttsp5_core_data *d; list_for_each_entry(d, &core_list, node) if (!strncmp(d->core_id, id, 20)) return d; return NULL; } EXPORT_SYMBOL_GPL(cyttsp5_get_core_data); static void cyttsp5_add_core(struct device *dev) { struct cyttsp5_core_data *d; struct cyttsp5_core_data *cd = dev_get_drvdata(dev); mutex_lock(&core_list_lock); list_for_each_entry(d, &core_list, node) if (d->dev == dev) goto unlock; list_add(&cd->node, &core_list); unlock: mutex_unlock(&core_list_lock); } static void cyttsp5_del_core(struct device *dev) { struct cyttsp5_core_data *d, *d_n; mutex_lock(&core_list_lock); list_for_each_entry_safe(d, d_n, &core_list, node) if (d->dev == dev) { list_del(&d->node); goto unlock; } unlock: mutex_unlock(&core_list_lock); } int cyttsp5_register_module(struct cyttsp5_module *module) { struct cyttsp5_module *m; struct cyttsp5_core_data *cd; int rc = 0; if (!module || !module->probe || !module->release) return -EINVAL; mutex_lock(&module_list_lock); list_for_each_entry(m, &module_list, node) if (m == module) { rc = -EEXIST; goto unlock; } list_add(&module->node, &module_list); /* Probe the module for each core */ mutex_lock(&core_list_lock); list_for_each_entry(cd, &core_list, node) cyttsp5_probe_module(cd, module); mutex_unlock(&core_list_lock); unlock: mutex_unlock(&module_list_lock); return rc; } EXPORT_SYMBOL_GPL(cyttsp5_register_module); void cyttsp5_unregister_module(struct cyttsp5_module *module) { struct cyttsp5_module *m, *m_n; struct cyttsp5_core_data *cd; if (!module) return; mutex_lock(&module_list_lock); /* Release the module for each core */ mutex_lock(&core_list_lock); list_for_each_entry(cd, &core_list, node) cyttsp5_release_module(cd, module); mutex_unlock(&core_list_lock); list_for_each_entry_safe(m, m_n, &module_list, node) if (m == module) { list_del(&m->node); break; } mutex_unlock(&module_list_lock); } EXPORT_SYMBOL_GPL(cyttsp5_unregister_module); void *cyttsp5_get_module_data(struct device *dev, struct cyttsp5_module *module) { struct cyttsp5_core_data *cd = dev_get_drvdata(dev); struct module_node *m; void *data = NULL; mutex_lock(&cd->module_list_lock); list_for_each_entry(m, &cd->module_list, node) if (m->module == module) { data = m->data; break; } mutex_unlock(&cd->module_list_lock); return data; } EXPORT_SYMBOL(cyttsp5_get_module_data); #if 0 //def CONFIG_HAS_EARLYSUSPEND static void cyttsp5_early_suspend(struct early_suspend *h) { struct cyttsp5_core_data *cd = container_of(h, struct cyttsp5_core_data, es); call_atten_cb(cd, CY_ATTEN_SUSPEND, 0); } static void cyttsp5_late_resume(struct early_suspend *h) { struct cyttsp5_core_data *cd = container_of(h, struct cyttsp5_core_data, es); call_atten_cb(cd, CY_ATTEN_RESUME, 0); } static void cyttsp5_setup_early_suspend(struct cyttsp5_core_data *cd) { cd->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; cd->es.suspend = cyttsp5_early_suspend; cd->es.resume = cyttsp5_late_resume; register_early_suspend(&cd->es); } #endif #if 0 //defined(CONFIG_FB) static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { struct cyttsp5_core_data *cd = container_of(self, struct cyttsp5_core_data, fb_notifier); struct fb_event *evdata = data; int *blank; if (event != FB_EVENT_BLANK || !evdata) goto exit; blank = evdata->data; if (*blank == FB_BLANK_UNBLANK) { dev_info(cd->dev, "%s: UNBLANK!\n", __func__); if (cd->fb_state != FB_ON) { call_atten_cb(cd, CY_ATTEN_RESUME, 0); cd->fb_state = FB_ON; } } else if (*blank == FB_BLANK_POWERDOWN) { dev_info(cd->dev, "%s: POWERDOWN!\n", __func__); if (cd->fb_state != FB_OFF) { call_atten_cb(cd, CY_ATTEN_SUSPEND, 0); cd->fb_state = FB_OFF; } } exit: return 0; } static void cyttsp5_setup_fb_notifier(struct cyttsp5_core_data *cd) { int rc; cd->fb_state = FB_ON; cd->fb_notifier.notifier_call = fb_notifier_callback; rc = fb_register_client(&cd->fb_notifier); if (rc) dev_err(cd->dev, "Unable to register fb_notifier: %d\n", rc); } #endif static int cyttsp5_setup_irq_gpio(struct cyttsp5_core_data *cd) { struct device *dev = cd->dev; unsigned long irq_flags; int rc; //reg, val; /* Initialize IRQ */ cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); //printk("%%%% cyttsp5_setup_irq_gpio irq=%d\n", cd->irq); if (cd->irq < 0) return -EINVAL; cd->irq_enabled = true; parade_debug(dev, DEBUG_LEVEL_1, "%s: initialize threaded irq=%d\n", __func__, cd->irq); if (cd->cpdata->level_irq_udelay > 0) /* use level triggered interrupts */ irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; else /* use edge triggered interrupts */ irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT ; rc = request_threaded_irq(cd->irq, NULL, cyttsp5_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND, dev_name(dev), cd); if (rc < 0) dev_err(dev, "%s: Error, could not request irq\n", __func__); return rc; } int cyttsp5_probe(const struct cyttsp5_bus_ops *ops, struct device *dev, u16 irq, size_t xfer_buf_size) { struct cyttsp5_core_data *cd; struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); enum cyttsp5_atten_type type; int rc = 0; /* Set default values on first probe */ if (cyttsp5_first_probe) { cyttsp5_first_probe = false; is_cyttsp5_probe_success = false; cyttsp5_bus_ops_save = NULL; } if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { dev_err(dev, "%s: Missing platform data\n", __func__); rc = -ENODEV; goto error_no_pdata; } if (pdata->core_pdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) { if (!pdata->core_pdata->power) { dev_err(dev, "%s: Missing platform data function\n", __func__); rc = -ENODEV; goto error_no_pdata; } } /* get context and debug print buffers */ cd = kzalloc(sizeof(*cd), GFP_KERNEL); if (!cd) { rc = -ENOMEM; goto error_alloc_data; } cd->supply = devm_regulator_get(dev, "cytp"); if (cd->supply) { dev_info(dev, "cyttsp5 touch supply = %dmv\n", regulator_get_voltage(cd->supply)); rc = regulator_enable(cd->supply); if (rc < 0) dev_err(dev, "failed to enable cyttsp5 power supply\n"); } /* Initialize device info */ cd->dev = dev; cd->pdata = pdata; cd->cpdata = pdata->core_pdata; cd->bus_ops = ops; cd->debug_level = CY_INITIAL_DEBUG_LEVEL; //2019-03-22 interval is 0, solve reboot huge cd->watchdog_interval = 0;//CY_WATCHDOG_TIMEOUT; cd->show_timestamp = CY_INITIAL_SHOW_TIME_STAMP; scnprintf(cd->core_id, 20, "%s%d", CYTTSP5_CORE_NAME, core_number++); /* Initialize mutexes and spinlocks */ mutex_init(&cd->module_list_lock); mutex_init(&cd->system_lock); mutex_init(&cd->adap_lock); mutex_init(&cd->hid_report_lock); spin_lock_init(&cd->spinlock); /* Initialize module list */ INIT_LIST_HEAD(&cd->module_list); /* Initialize attention lists */ for (type = 0; type < CY_ATTEN_NUM_ATTEN; type++) INIT_LIST_HEAD(&cd->atten_list[type]); /* Initialize parameter list */ INIT_LIST_HEAD(&cd->param_list); /* Initialize wait queue */ init_waitqueue_head(&cd->wait_q); /* Initialize works */ INIT_WORK(&cd->startup_work, cyttsp5_startup_work_function); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE INIT_WORK(&cd->watchdog_work, cyttsp5_watchdog_work); #endif /* Initialize HID specific data */ cd->hid_core.hid_vendor_id = (cd->cpdata->vendor_id) ? cd->cpdata->vendor_id : CY_HID_VENDOR_ID; cd->hid_core.hid_product_id = (cd->cpdata->product_id) ? cd->cpdata->product_id : CY_HID_APP_PRODUCT_ID; cd->hid_core.hid_desc_register = cpu_to_le16(cd->cpdata->hid_desc_register); /* Set platform easywake value */ cd->easy_wakeup_gesture = cd->cpdata->easy_wakeup_gesture; /* Set Panel ID to Not Enabled */ cd->panel_id = PANEL_ID_NOT_ENABLED; dev_set_drvdata(dev, cd); cyttsp5_add_core(dev); /*create ttdl_restart sysfs node is probe failed*/ if (!is_cyttsp5_probe_success) device_create_file(dev, &dev_attr_ttdl_restart); /* * Save the pointer to a global value, which will be used * in ttdl_restart function */ cyttsp5_bus_ops_save = ops; /* Call platform detect function */ if (cd->cpdata->detect) { dev_info(cd->dev, "%s: Detect HW\n", __func__); rc = cd->cpdata->detect(cd->cpdata, cd->dev, cyttsp5_platform_detect_read); if (rc) { dev_info(cd->dev, "%s: No HW detected\n", __func__); rc = -ENODEV; goto error_detect; } } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE /* Setup watchdog timer */ timer_setup(&cd->watchdog_timer, cyttsp5_watchdog_timer, (unsigned long)cd); #endif rc = cyttsp5_setup_irq_gpio(cd); if (rc < 0) { dev_err(dev, "%s: Error, could not setup IRQ\n", __func__); goto error_setup_irq; } parade_debug(dev, DEBUG_LEVEL_1, "%s: add sysfs interfaces\n", __func__); rc = add_sysfs_interfaces(dev); if (rc < 0) { dev_err(dev, "%s: Error, fail sysfs init\n", __func__); goto error_attr_create; } #ifdef TTHE_TUNER_SUPPORT mutex_init(&cd->tthe_lock); cd->tthe_debugfs = debugfs_create_file(CYTTSP5_TTHE_TUNER_FILE_NAME, 0644, NULL, cd, &tthe_debugfs_fops); #endif rc = device_init_wakeup(dev, 1); if (rc < 0) dev_err(dev, "%s: Error, device_init_wakeup rc:%d\n", __func__, rc); enable_irq_wake(cd->irq); cd->irq_wake = 1; pm_runtime_get_noresume(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); /* * call startup directly to ensure that the device * is tested before leaving the probe */ parade_debug(dev, DEBUG_LEVEL_1, "%s: call startup\n", __func__); rc = cyttsp5_startup(cd, false); pm_runtime_put_sync(dev); /* Do not fail probe if startup fails but the device is detected */ if (rc == -ENODEV) { dev_err(cd->dev, "%s: Fail initial startup r=%d\n", __func__, rc); goto error_startup; } rc = cyttsp5_mt_probe(dev); if (rc < 0) { dev_err(dev, "%s: Error, fail mt probe\n", __func__); goto error_startup; } rc = cyttsp5_btn_probe(dev); if (rc < 0) { dev_err(dev, "%s: Error, fail btn probe\n", __func__); goto error_startup_mt; } rc = cyttsp5_proximity_probe(dev); if (rc < 0) { dev_err(dev, "%s: Error, fail proximity probe\n", __func__); goto error_startup_btn; } /* Probe registered modules */ cyttsp5_probe_modules(cd); //#ifdef CONFIG_HAS_EARLYSUSPEND // cyttsp5_setup_early_suspend(cd); //#elif defined(CONFIG_FB) // cyttsp5_setup_fb_notifier(cd); //#endif #if NEED_SUSPEND_NOTIFIER cd->pm_notifier.notifier_call = cyttsp5_pm_notifier; register_pm_notifier(&cd->pm_notifier); #endif cd->tp.tp_resume = cyttsp5_core_late_resume; cd->tp.tp_suspend = cyttsp5_core_early_suspend; tp_register_fb(&cd->tp); if (!priv_data) { priv_data = cd; } #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE INIT_DELAYED_WORK(&cd->watchdog_enable_work, watchdog_delay_enable); schedule_delayed_work(&cd->watchdog_enable_work, msecs_to_jiffies(25 * 1000)); #endif is_cyttsp5_probe_success = true; return 0; error_startup_btn: cyttsp5_btn_release(dev); error_startup_mt: cyttsp5_mt_release(dev); error_startup: pm_runtime_disable(dev); #if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE) device_wakeup_disable(dev); #endif device_init_wakeup(dev, 0); cancel_work_sync(&cd->startup_work); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_stop_wd_timer(cd); #endif cyttsp5_free_si_ptrs(cd); remove_sysfs_interfaces(dev); error_attr_create: free_irq(cd->irq, cd); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE del_timer(&cd->watchdog_timer); #endif error_setup_irq: error_detect: if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, 0, dev); cyttsp5_del_core(dev); dev_set_drvdata(dev, NULL); kfree(cd); error_alloc_data: error_no_pdata: dev_err(dev, "%s failed.\n", __func__); is_cyttsp5_probe_success = false; return rc; } EXPORT_SYMBOL_GPL(cyttsp5_probe); int cyttsp5_release(struct cyttsp5_core_data *cd) { struct device *dev = cd->dev; /* Release successfully probed modules */ cyttsp5_release_modules(cd); cyttsp5_proximity_release(dev); cyttsp5_btn_release(dev); cyttsp5_mt_release(dev); //#ifdef CONFIG_HAS_EARLYSUSPEND // unregister_early_suspend(&cd->es); //#elif defined(CONFIG_FB) // fb_unregister_client(&cd->fb_notifier); //#endif #if NEED_SUSPEND_NOTIFIER unregister_pm_notifier(&cd->pm_notifier); #endif /* * Suspend the device before freeing the startup_work and stopping * the watchdog since sleep function restarts watchdog on failure */ pm_runtime_suspend(dev); pm_runtime_disable(dev); cancel_work_sync(&cd->startup_work); #ifdef CYTTSP_WATCHDOG_DELAY_ENBALE cyttsp5_stop_wd_timer(cd); #endif #if (KERNEL_VERSION(3, 16, 0) > LINUX_VERSION_CODE) device_wakeup_disable(dev); #endif device_init_wakeup(dev, 0); #ifdef TTHE_TUNER_SUPPORT mutex_lock(&cd->tthe_lock); cd->tthe_exit = 1; wake_up(&cd->wait_q); mutex_unlock(&cd->tthe_lock); debugfs_remove(cd->tthe_debugfs); #endif remove_sysfs_interfaces(dev); free_irq(cd->irq, cd); if (cd->cpdata->init) cd->cpdata->init(cd->cpdata, 0, dev); dev_set_drvdata(dev, NULL); cyttsp5_del_core(dev); cyttsp5_free_si_ptrs(cd); cyttsp5_free_hid_reports(cd); kfree(cd); priv_data = NULL; return 0; } EXPORT_SYMBOL_GPL(cyttsp5_release); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver"); MODULE_AUTHOR("Parade Technologies ");