| /* |
| * Goodix Touchscreen Driver |
| * Copyright (C) 2020 - 2021 Goodix, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be a reference |
| * to you, when you are integrating the GOODiX's CTP IC into your system, |
| * 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. |
| * |
| */ |
| #include "goodix_ts_core.h" |
| |
| bool debug_log_flag = false; |
| |
| /***************************************************************************** |
| * goodix_append_checksum |
| * @summary |
| * Calculate data checksum with the specified mode. |
| * |
| * @param data |
| * data need to be calculate |
| * @param len |
| * data length |
| * @param mode |
| * calculate for u8 or u16 checksum |
| * @return |
| * return the data checksum value. |
| * |
| *****************************************************************************/ |
| u32 goodix_append_checksum(u8 *data, int len, int mode) |
| { |
| u32 checksum = 0; |
| int i; |
| |
| checksum = 0; |
| if (mode == CHECKSUM_MODE_U8_LE) { |
| for (i = 0; i < len; i++) |
| checksum += data[i]; |
| } else { |
| for (i = 0; i < len; i += 2) |
| checksum += (data[i] + (data[i + 1] << 8)); |
| } |
| |
| if (mode == CHECKSUM_MODE_U8_LE) { |
| data[len] = checksum & 0xff; |
| data[len + 1] = (checksum >> 8) & 0xff; |
| return 0xFFFF & checksum; |
| } |
| data[len] = checksum & 0xff; |
| data[len + 1] = (checksum >> 8) & 0xff; |
| data[len + 2] = (checksum >> 16) & 0xff; |
| data[len + 3] = (checksum >> 24) & 0xff; |
| return checksum; |
| } |
| |
| /* checksum_cmp: check data valid or not |
| * @data: data need to be check |
| * @size: data length need to be check(include the checksum bytes) |
| * @mode: compare with U8 or U16 mode |
| */ |
| int checksum_cmp(const u8 *data, int size, int mode) |
| { |
| u32 cal_checksum = 0; |
| u32 r_checksum = 0; |
| u32 i; |
| bool is_valid = !is_risk_data(data, size); /* [GOOG] */ |
| |
| if (mode == CHECKSUM_MODE_U8_LE) { |
| if (size < 2) |
| return 1; |
| for (i = 0; i < size - 2; i++) |
| cal_checksum += data[i]; |
| cal_checksum &= 0xFFFF; |
| r_checksum = data[size - 2] + (data[size - 1] << 8); |
| return cal_checksum == r_checksum && is_valid ? 0 : 1; |
| } |
| |
| if (size < 4) |
| return 1; |
| for (i = 0; i < size - 4; i += 2) |
| cal_checksum += data[i] + (data[i + 1] << 8); |
| r_checksum = data[size - 4] + (data[size - 3] << 8) + |
| (data[size - 2] << 16) + (data[size - 1] << 24); |
| return cal_checksum == r_checksum && is_valid ? 0 : 1; |
| } |
| |
| /* return 1 if all data is zero or ff |
| * else return 0 |
| */ |
| int is_risk_data(const u8 *data, int size) |
| { |
| int i; |
| int zero_count = 0; |
| int ff_count = 0; |
| |
| for (i = 0; i < size; i++) { |
| if (data[i] == 0) |
| zero_count++; |
| else if (data[i] == 0xFF) |
| ff_count++; |
| } |
| if (zero_count == size || ff_count == size) { |
| ts_info("warning data is all %s\n", |
| zero_count == size ? "0x00" : "0xFF"); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* get config id form config file */ |
| #define CONFIG_ID_OFFSET 30 |
| u32 goodix_get_file_config_id(u8 *ic_config) |
| { |
| if (!ic_config) |
| return 0; |
| return le32_to_cpup((__le32 *)&ic_config[CONFIG_ID_OFFSET]); |
| } |
| |
| /* |
| * matrix transpose: |
| * [GOOG] |
| * Add 'dest' as optional target(pa/2221748). |
| */ |
| void goodix_rotate_abcd2cbad(int tx, int rx, s16 *src, s16 *dest) |
| { |
| s16 *temp_buf = dest; |
| int size = tx * rx; |
| int x; |
| s16 *curr; |
| int index_org = 0; |
| |
| /* |
| * [GOOG] |
| * If no 'dest' assign, 'src' will be overwrote. |
| */ |
| if (dest == NULL) { |
| temp_buf = kcalloc(size, sizeof(s16), GFP_KERNEL); |
| if (!temp_buf) { |
| ts_err("%s: malloc failed!\n", __func__); |
| return; |
| } |
| } |
| |
| curr = temp_buf; |
| for (x = 0; x < rx; x++) { |
| for (index_org = x; index_org < size; index_org += rx) { |
| *curr = src[index_org]; |
| curr++; |
| } |
| } |
| |
| if (dest == NULL) { |
| memcpy(src, temp_buf, size * sizeof(s16)); |
| kfree(temp_buf); |
| } |
| /*~[GOOG] */ |
| } |
| |
| /* get ic type */ |
| int goodix_get_ic_type( |
| struct device_node *node, struct goodix_bus_interface *bus_inf) |
| { |
| const struct property *prop; |
| char ic_name[128] = { 0 }; |
| int i; |
| |
| prop = of_find_property(node, "compatible", NULL); |
| if (!prop || !prop->value || prop->length > sizeof(ic_name)) { |
| ts_err("invalid compatible property"); |
| return -EINVAL; |
| } |
| |
| memcpy(ic_name, prop->value, prop->length); |
| |
| /* replace string end flag with ';' */ |
| for (i = 0; i < prop->length - 1; i++) |
| if (ic_name[i] == 0) |
| ic_name[i] = ';'; |
| |
| ts_info("ic_name %s", ic_name); |
| |
| if (strstr(ic_name, "brl-a")) { |
| ts_info("ic type is brl-a"); |
| bus_inf->ic_type = IC_TYPE_BERLIN_A; |
| return 0; |
| } |
| |
| if (strstr(ic_name, "brl-b")) { |
| ts_info("ic type is brl-b"); |
| bus_inf->ic_type = IC_TYPE_BERLIN_B; |
| if (strstr(ic_name, "ga687x")) { |
| bus_inf->sub_ic_type = IC_TYPE_SUB_B2; |
| ts_info("sub ic type is brl-b2"); |
| } else if (strstr(ic_name, "gt7986")) { |
| bus_inf->sub_ic_type = IC_TYPE_SUB_GT7986; |
| ts_info("sub ic type is GT7986"); |
| } |
| return 0; |
| } |
| if (strstr(ic_name, "brl-d")) { |
| ts_info("ic type is brl-d"); |
| bus_inf->ic_type = IC_TYPE_BERLIN_D; |
| return 0; |
| } |
| if (strstr(ic_name, "nottingham")) { |
| ts_info("ic type is nottingham"); |
| bus_inf->ic_type = IC_TYPE_NOTTINGHAM; |
| return 0; |
| } |
| |
| ts_err("unsupported ic type %s", ic_name); |
| return -EINVAL; |
| } |