diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/Android.mk b/drivers/input/touchscreen/goodix_berlin_mmi/Android.mk similarity index 50% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/Android.mk rename to drivers/input/touchscreen/goodix_berlin_mmi/Android.mk index c926c05e14dc..abbd01d8f90a 100644 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/Android.mk +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Android.mk @@ -5,9 +5,4 @@ include $(CLEAR_VARS) LOCAL_MODULE := goodix_brl_mmi.ko LOCAL_MODULE_TAGS := optional LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -ifneq ($(findstring goodix_brl_i2c.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) - LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/goodix_brl_i2c.ko -else - LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/goodix_brl_spi.ko -endif include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild b/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild new file mode 100644 index 000000000000..aba626bc5169 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild @@ -0,0 +1,17 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := goodix_brl_mmi.o +goodix_brl_mmi-objs := \ + goodix_brl_i2c.o \ + goodix_brl_spi.o \ + goodix_ts_core.o \ + goodix_brl_hw.o \ + goodix_cfg_bin.o \ + goodix_ts_utils.o \ + goodix_brl_fwupdate.o \ + goodix_ts_gesture.o \ + goodix_ts_inspect.o \ + goodix_ts_tools.o diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig b/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig old mode 100755 new mode 100644 index 621a2fb2076d..9eb1c3999c10 --- a/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig @@ -3,48 +3,18 @@ # menuconfig TOUCHSCREEN_GOODIX_BRL tristate "Goodix berlin touchscreen" - default y help Say Y here if you have a Goodix berlin series touch controller to your system. + If build module, say M. If unsure, say N. if TOUCHSCREEN_GOODIX_BRL -config TOUCHSCREEN_GOODIX_BRL_I2C - tristate "support I2C bus connection" - depends on I2C - help - Say Y here if the touchscreen is connected via I2C bus. - - To compile this driver as a module, choose M here. config TOUCHSCREEN_GOODIX_BRL_SPI - tristate "support SPI bus connection" - depends on SPI_MASTER + bool "support SPI bus connection" help - Say Y here if the touchscreen is connected via SPI bus. - - To compile this driver as a module, choose M here. - -config TOUCHSCREEN_GOODIX_TS_TOOLS - tristate "Goodix debug tools support" - default y - help - Say Y here to enable debug tools. - - If unsure, say N. - - To compile this driver as a module, choose M here. - -config TOUCHSCREEN_GOODIX_TS_GESTURE - tristate "Goodix gesture support" - default y - help - Say Y here to enable gesture. - - If unsure, say N. - - To compile this driver as a module, choose M here. + Say Y here if the touchscreen is connected via SPI bus. endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Makefile b/drivers/input/touchscreen/goodix_berlin_mmi/Makefile old mode 100755 new mode 100644 index c313a5915b70..bfbdc14740ef --- a/drivers/input/touchscreen/goodix_berlin_mmi/Makefile +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Makefile @@ -1,6 +1,14 @@ -obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL_I2C) += goodix_brl_i2c.o -obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI) += goodix_brl_spi.o -obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_core.o -goodix_core-y := goodix_ts_core.o goodix_brl_hw.o goodix_cfg_bin.o goodix_ts_utils.o goodix_brl_fwupdate.o -obj-$(CONFIG_TOUCHSCREEN_GOODIX_TS_TOOLS) += goodix_ts_tools.o -obj-$(CONFIG_TOUCHSCREEN_GOODIX_TS_GESTURE) += goodix_ts_gesture.o +all: modules + +modules: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +%: + $(MAKE) -C $(KERNEL_SRC) M=$(M) $@ $(KBUILD_OPTIONS) + +clean: + rm -f *.o *.ko *.mod.c *.mod.o *~ .*.cmd Module.symvers + rm -rf .tmp_versions diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/Android.mk b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/Android.mk deleted file mode 100644 index 4366c1404a73..000000000000 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/Android.mk +++ /dev/null @@ -1,14 +0,0 @@ -DLKM_DIR := motorola/kernel/modules -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -ifneq ($(findstring goodix_brl_i2c.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) - KERNEL_CFLAGS += CONFIG_USE_TOUCHSCREEN_MMI_I2C=y - LOCAL_MODULE := goodix_brl_i2c.ko -else - LOCAL_MODULE := goodix_brl_spi.ko -endif -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) - -include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/Kbuild b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/Kbuild deleted file mode 100644 index 82610e5f6d5e..000000000000 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/Kbuild +++ /dev/null @@ -1,11 +0,0 @@ -# add -Wall to try to catch everything we can. -EXTRA_CFLAGS += -Wall -EXTRA_CFLAGS += \ - -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ - -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/goodix_berlin_mmi/include - -ifneq ($(filter m y,$(CONFIG_USE_TOUCHSCREEN_MMI_I2C)),) - obj-m = goodix_brl_i2c.o -else - obj-m = goodix_brl_spi.o -endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/Kbuild b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/Kbuild deleted file mode 100644 index caa0146d2193..000000000000 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/Kbuild +++ /dev/null @@ -1,11 +0,0 @@ -# add -Wall to try to catch everything we can. -EXTRA_CFLAGS += -Wall -EXTRA_CFLAGS += \ - -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ - -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/goodix_berlin_mmi/include - -obj-m += goodix_brl_mmi.o - -goodix_brl_mmi-objs := goodix_brl_fwupdate.o goodix_brl_hw.o goodix_cfg_bin.o goodix_ts_core.o goodix_ts_utils.o - -KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../goodix_brl_bus/Module.symvers diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/Android.mk b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/Android.mk deleted file mode 100644 index 2fb097c7c676..000000000000 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/Android.mk +++ /dev/null @@ -1,16 +0,0 @@ -DLKM_DIR := motorola/kernel/modules -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := goodix_brl_tools_mmi.ko -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/goodix_brl_mmi.ko -include $(DLKM_DIR)/AndroidKernelModule.mk - -include $(CLEAR_VARS) -LOCAL_MODULE := goodix_brl_gesture_mmi.ko -LOCAL_MODULE_TAGS := optional -LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) -LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/goodix_brl_mmi.ko -include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/Kbuild b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/Kbuild deleted file mode 100644 index 22d39364408e..000000000000 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/Kbuild +++ /dev/null @@ -1,14 +0,0 @@ -# add -Wall to try to catch everything we can. -EXTRA_CFLAGS += -Wall -EXTRA_CFLAGS += \ - -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ - -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/goodix_berlin_mmi/include - -obj-m += goodix_brl_tools_mmi.o -goodix_brl_tools_mmi-objs = goodix_ts_tools.o - -obj-m += goodix_brl_gesture_mmi.o -goodix_brl_gesture_mmi-objs = goodix_ts_gesture.o - -KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../goodix_brl_core/Module.symvers - diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_brl_fwupdate.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_fwupdate.c old mode 100755 new mode 100644 similarity index 66% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_brl_fwupdate.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_fwupdate.c index cbef9025fb7d..8bafbd38620c --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_brl_fwupdate.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_fwupdate.c @@ -16,56 +16,130 @@ */ #include "goodix_ts_core.h" -#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" +#define BUS_TYPE_SPI 1 +#define BUS_TYPE_I2C 0 -#define BUS_TYPE_SPI 0x01 -#define BUS_TYPE_I2C 0 +#define GOODIX_BUS_RETRY_TIMES 3 -#define GOODIX_BUS_RETRY_TIMES 3 +#define FW_HEADER_SIZE_BRA 256 +#define FW_HEADER_SIZE 512 +#define FW_SUBSYS_INFO_SIZE 10 +#define FW_SUBSYS_INFO_OFFSET_BRA 36 +#define FW_SUBSYS_INFO_OFFSET 42 +#define FW_SUBSYS_MAX_NUM 47 -#define FW_HEADER_SIZE 256 -#define FW_SUBSYS_INFO_SIZE 10 -#define FW_SUBSYS_INFO_OFFSET 36 -#define FW_SUBSYS_MAX_NUM 28 +#define ISP_MAX_BUFFERSIZE 4096 -#define ISP_MAX_BUFFERSIZE (1024 * 4) +#define FW_PID_LEN 8 +#define FW_VID_LEN 4 +#define FLASH_CMD_LEN 11 -#define NO_NEED_UPDATE 99 +#define FW_FILE_CHECKSUM_OFFSET 8 +#define CONFIG_DATA_TYPE 4 -#define FW_PID_LEN 8 -#define FW_VID_LEN 4 -#define FLASH_CMD_LEN 11 +#define ISP_RAM_ADDR_BRA 0x18400 +#define ISP_RAM_ADDR_BRB 0x57000 +#define ISP_RAM_ADDR_BRD 0x23800 +#define HW_REG_CPU_RUN_FROM 0x10000 +#define FLASH_CMD_REG_BRA 0x10400 +#define FLASH_CMD_REG_BRB 0x13400 +#define FLASH_CMD_REG_BRD 0x12400 +#define HW_REG_ISP_BUFFER_BRA 0x10410 +#define HW_REG_ISP_BUFFER_BRB 0x13410 +#define HW_REG_ISP_BUFFER_BRD 0x12410 +#define CONFIG_DATA_ADDR_BRA 0x3E000 +#define CONFIG_DATA_ADDR_BRB 0x40000 +#define CONFIG_DATA_ADDR_BRD 0x3E000 +#define GOODIX_CFG_ID_ADDR_BRA 0x1006E +#define GOODIX_CFG_ID_ADDR_BRB 0x10076 +#define GOODIX_CFG_ID_ADDR_BRD 0x10076 -#define FW_FILE_CHECKSUM_OFFSET 8 -#define CONFIG_ID_OFFSET 30 -#define CONFIG_DATA_TYPE 4 +#define HOLD_CPU_REG_W 0x0002 +#define HOLD_CPU_REG_R 0x2000 +#define MISCTL_REG_BRA 0xD807 +#define MISCTL_REG_BRB 0xD80B +#define MISCTL_REG_BRD 0xD804 +#define ENABLE_MISCTL_BRA 0x08 +#define ENABLE_MISCTL_BRB 0x40 +#define ENABLE_MISCTL_BRD 0x20700000 +#define ESD_KEY_REG 0xCC58 +#define WATCH_DOG_REG_BRA 0xCC54 +#define WATCH_DOG_REG_BRB 0xD054 +#define WATCH_DOG_REG_BRD 0xD040 -#define ISP_RAM_ADDR 0x18400 -#define HW_REG_CPU_RUN_FROM 0x10000 -#define FLASH_CMD_REG 0x10400 -#define HW_REG_ISP_BUFFER 0x10410 -#define CONFIG_DATA_ADDR 0x3E000 +#define FLASH_CMD_TYPE_READ 0xAA +#define FLASH_CMD_TYPE_WRITE 0xBB +#define FLASH_CMD_ACK_CHK_PASS 0xEE +#define FLASH_CMD_ACK_CHK_ERROR 0x33 +#define FLASH_CMD_ACK_IDLE 0x11 +#define FLASH_CMD_W_STATUS_CHK_PASS 0x22 +#define FLASH_CMD_W_STATUS_CHK_FAIL 0x33 +#define FLASH_CMD_W_STATUS_ADDR_ERR 0x44 +#define FLASH_CMD_W_STATUS_WRITE_ERR 0x55 +#define FLASH_CMD_W_STATUS_WRITE_OK 0xEE -#define HOLD_CPU_REG_W 0x0002 -#define HOLD_CPU_REG_R 0x2000 -#define MISCTL_REG 0xD807 -#define ESD_KEY_REG 0xCC58 -#define WATCH_DOG_REG 0xCC54 +#define CHIP_TYPE_BRA 0x96 +#define CHIP_TYPE_BRB 0x97 +#define CHIP_TYPE_BRD 0x98 -#define FLASH_CMD_TYPE_READ 0xAA -#define FLASH_CMD_TYPE_WRITE 0xBB -#define FLASH_CMD_ACK_CHK_PASS 0xEE -#define FLASH_CMD_ACK_CHK_ERROR 0x33 -#define FLASH_CMD_ACK_IDLE 0x11 -#define FLASH_CMD_W_STATUS_CHK_PASS 0x22 -#define FLASH_CMD_W_STATUS_CHK_FAIL 0x33 -#define FLASH_CMD_W_STATUS_ADDR_ERR 0x44 -#define FLASH_CMD_W_STATUS_WRITE_ERR 0x55 -#define FLASH_CMD_W_STATUS_WRITE_OK 0xEE +struct update_info_t { + int header_size; + int subsys_info_offset; + u32 isp_ram_reg; + u32 flash_cmd_reg; + u32 isp_buffer_reg; + u32 config_data_reg; + u32 misctl_reg; + u32 watch_dog_reg; + u32 config_id_reg; + u32 enable_misctl_val; +}; + +/* berlinA update into */ +struct update_info_t update_bra = { + FW_HEADER_SIZE_BRA, + FW_SUBSYS_INFO_OFFSET_BRA, + ISP_RAM_ADDR_BRA, + FLASH_CMD_REG_BRA, + HW_REG_ISP_BUFFER_BRA, + CONFIG_DATA_ADDR_BRA, + MISCTL_REG_BRA, + WATCH_DOG_REG_BRA, + GOODIX_CFG_ID_ADDR_BRA, + ENABLE_MISCTL_BRA, +}; + +/* berlinB update info */ +struct update_info_t update_brb = { + FW_HEADER_SIZE, + FW_SUBSYS_INFO_OFFSET, + ISP_RAM_ADDR_BRB, + FLASH_CMD_REG_BRB, + HW_REG_ISP_BUFFER_BRB, + CONFIG_DATA_ADDR_BRB, + MISCTL_REG_BRB, + WATCH_DOG_REG_BRB, + GOODIX_CFG_ID_ADDR_BRB, + ENABLE_MISCTL_BRB, +}; + +/* berlinD update info */ +struct update_info_t update_brd = { + FW_HEADER_SIZE, + FW_SUBSYS_INFO_OFFSET, + ISP_RAM_ADDR_BRD, + FLASH_CMD_REG_BRD, + HW_REG_ISP_BUFFER_BRD, + CONFIG_DATA_ADDR_BRD, + MISCTL_REG_BRD, + WATCH_DOG_REG_BRD, + GOODIX_CFG_ID_ADDR_BRD, + ENABLE_MISCTL_BRD, +}; /** - * fw_subsys_info - subsytem firmware infomation + * fw_subsys_info - subsytem firmware information * @type: sybsystem type * @size: firmware size * @flash_addr: flash address @@ -106,14 +180,14 @@ struct firmware_summary { u8 protocol_ver; u8 bus_type; u8 flash_protect; - u8 reserved[2]; + // u8 reserved[8]; struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; }; #pragma pack() /** * firmware_data - firmware data structure - * @fw_summary: firmware infomation + * @fw_summary: firmware information * @firmware: firmware data structure */ struct firmware_data { @@ -128,24 +202,40 @@ struct config_data { #pragma pack(1) struct goodix_flash_cmd { - union { - struct { - u8 status; - u8 ack; - u8 len; - u8 cmd; - u8 fw_type; - u16 fw_len; - u32 fw_addr; - //u16 checksum; + union { + struct { + u8 status; + u8 ack; + u8 len; + u8 cmd; + u8 fw_type; + u16 fw_len; + u32 fw_addr; + //u16 checksum; + }; + u8 buf[16]; }; - u8 buf[16]; - }; }; #pragma pack() +enum update_status { + UPSTA_NOTWORK = 0, + UPSTA_PREPARING, + UPSTA_UPDATING, + UPSTA_SUCCESS, + UPSTA_FAILED +}; + +enum compare_status { + COMPARE_EQUAL = 0, + COMPARE_NOCODE, + COMPARE_PIDMISMATCH, + COMPARE_FW_NOTEQUAL, + COMPARE_CFG_NOTEQUAL, +}; + /** - * fw_update_ctrl - sturcture used to control the + * fw_update_ctrl - structure used to control the * firmware update process * @initialized: struct init state * @mode: indicate weather reflash config or not, fw data source, @@ -163,10 +253,13 @@ struct fw_update_ctrl { int initialized; char fw_name[GOODIX_MAX_STR_LABLE_LEN]; int mode; + enum update_status status; + int spend_time; struct firmware_data fw_data; struct goodix_ic_config *ic_config; struct goodix_ts_core *core_data; + struct update_info_t *update_info; struct bin_attribute attr_fwimage; struct kobject *kobj; @@ -211,8 +304,8 @@ static int goodix_reg_read(unsigned int addr, } /** - * goodix_parse_firmware - parse firmware header infomation - * and subsystem infomation from firmware data buffer + * goodix_parse_firmware - parse firmware header information + * and subsystem information from firmware data buffer * * @fw_data: firmware struct, contains firmware header info * and firmware data. @@ -226,13 +319,19 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) struct firmware_summary *fw_summary; unsigned int i, fw_offset, info_offset; u32 checksum; + int ic_type = + goodix_fw_update_ctrl.core_data->bus->ic_type; + int subsys_info_offset = + goodix_fw_update_ctrl.update_info->subsys_info_offset; + int header_size = + goodix_fw_update_ctrl.update_info->header_size; int r = 0; fw_summary = &fw_data->fw_summary; /* copy firmware head info */ firmware = fw_data->firmware; - if (firmware->size < FW_SUBSYS_INFO_OFFSET) { + if (firmware->size < subsys_info_offset) { ts_err("Invalid firmware size:%zu", firmware->size); r = -EINVAL; goto err_size; @@ -249,7 +348,7 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) } for (i = FW_FILE_CHECKSUM_OFFSET, checksum = 0; - i < firmware->size; i+=2) + i < firmware->size; i += 2) checksum += firmware->data[i] + (firmware->data[i+1] << 8); /* byte order change, and check */ @@ -268,9 +367,9 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) } /* parse subsystem info */ - fw_offset = FW_HEADER_SIZE; + fw_offset = header_size; for (i = 0; i < fw_summary->subsys_num; i++) { - info_offset = FW_SUBSYS_INFO_OFFSET + + info_offset = subsys_info_offset + i * FW_SUBSYS_INFO_SIZE; fw_summary->subsys[i].type = firmware->data[info_offset]; @@ -289,9 +388,9 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) } ts_info("Firmware package protocol: V%u", fw_summary->protocol_ver); - ts_info("Fimware PID:GT%s", fw_summary->fw_pid); - ts_info("Fimware VID:%*ph", 4, fw_summary->fw_vid); - ts_info("Firmware chip type:%02X", fw_summary->chip_type); + ts_info("Firmware PID:GT%s", fw_summary->fw_pid); + ts_info("Firmware VID:%*ph", 4, fw_summary->fw_vid); + ts_info("Firmware chip type:0x%02X", fw_summary->chip_type); ts_info("Firmware bus type:%s", (fw_summary->bus_type & BUS_TYPE_SPI) ? "SPI" : "I2C"); ts_info("Firmware size:%u", fw_summary->size); @@ -307,6 +406,20 @@ static int goodix_parse_firmware(struct firmware_data *fw_data) ts_debug("Subsystem Ptr:%p", fw_summary->subsys[i].data); } + if (fw_summary->chip_type == CHIP_TYPE_BRA && + ic_type != IC_TYPE_BERLIN_A) { + ts_err("ic type mismatch!"); + r = -EINVAL; + } else if (fw_summary->chip_type == CHIP_TYPE_BRB && + ic_type != IC_TYPE_BERLIN_B) { + ts_err("ic type mismatch!"); + r = -EINVAL; + } else if (fw_summary->chip_type == CHIP_TYPE_BRD && + ic_type != IC_TYPE_BERLIN_D) { + ts_err("ic type mismatch!"); + r = -EINVAL; + } + err_size: return r; } @@ -314,39 +427,61 @@ err_size: /** * goodix_fw_version_compare - compare the active version with * firmware file version. - * @fwu_ctrl: firmware infomation to be compared + * @fwu_ctrl: firmware information to be compared * return: 0 equal, < 0 unequal */ +#define GOODIX_NOCODE "NOCODE" static int goodix_fw_version_compare(struct fw_update_ctrl *fwu_ctrl) { int ret = 0; - struct goodix_fw_version fw_version = {{0}}; + struct goodix_fw_version fw_version; struct firmware_summary *fw_summary = &fwu_ctrl->fw_data.fw_summary; + u32 config_id_reg = goodix_fw_update_ctrl.update_info->config_id_reg; + u32 file_cfg_id; + u32 ic_cfg_id; + /* compare fw_version */ ret = get_fw_version_info(&fw_version); - if (ret) { - ts_info("failed get active fw version"); + if (ret) return -EINVAL; + + if (!memcmp(fw_version.rom_pid, GOODIX_NOCODE, 6) || + !memcmp(fw_version.patch_pid, GOODIX_NOCODE, 6)) { + ts_info("there is no code in the chip"); + return COMPARE_NOCODE; } if (memcmp(fw_version.patch_pid, fw_summary->fw_pid, FW_PID_LEN)) { - ts_err("Product ID mismatch:%s:%s", + ts_err("Product ID mismatch:%s != %s", fw_version.patch_pid, fw_summary->fw_pid); - return -EINVAL; + return COMPARE_PIDMISMATCH; } - ret = memcmp(fw_version.patch_vid, fw_summary->fw_vid, FW_VID_LEN); if (ret) { ts_info("active firmware version:%*ph", FW_VID_LEN, fw_version.patch_vid); ts_info("firmware file version: %*ph", FW_VID_LEN, fw_summary->fw_vid); - return -EINVAL; + return COMPARE_FW_NOTEQUAL; + } + ts_info("fw_version equal"); + + /* compare config id */ + if (fwu_ctrl->ic_config && fwu_ctrl->ic_config->len > 0) { + file_cfg_id = + goodix_get_file_config_id(fwu_ctrl->ic_config->data); + goodix_reg_read(config_id_reg, + (u8 *)&ic_cfg_id, sizeof(ic_cfg_id)); + if (ic_cfg_id != file_cfg_id) { + ts_info("ic_cfg_id:0x%x != file_cfg_id:0x%x", + ic_cfg_id, file_cfg_id); + return COMPARE_CFG_NOTEQUAL; + } + ts_info("config_id equal"); } - ts_info("Firmware version equal"); - return 0; + return COMPARE_EQUAL; } /** @@ -359,7 +494,8 @@ static int goodix_fw_version_compare(struct fw_update_ctrl *fwu_ctrl) * return: 0 write success and confirm ok * < 0 failed */ -static int goodix_reg_write_confirm(unsigned int addr, unsigned char *data, unsigned int len) +static int goodix_reg_write_confirm(unsigned int addr, + unsigned char *data, unsigned int len) { u8 *cfm = NULL; u8 cfm_buf[32]; @@ -367,10 +503,8 @@ static int goodix_reg_write_confirm(unsigned int addr, unsigned char *data, unsi if (len > sizeof(cfm_buf)) { cfm = kzalloc(len, GFP_KERNEL); - if (!cfm) { - ts_err("Mem alloc failed"); + if (!cfm) return -ENOMEM; - } } else { cfm = &cfm_buf[0]; } @@ -401,23 +535,25 @@ exit: /** - * goodix_load_isp - load ISP program to deivce ram + * goodix_load_isp - load ISP program to device ram * @dev: pointer to touch device * @fw_data: firmware data * return 0 ok, <0 error */ static int goodix_load_isp(struct firmware_data *fw_data) { - struct goodix_fw_version isp_fw_version = {{0}}; + struct goodix_fw_version isp_fw_version; struct fw_subsys_info *fw_isp; + u32 isp_ram_reg = goodix_fw_update_ctrl.update_info->isp_ram_reg; u8 reg_val[8] = {0x00}; int r; + memset(&isp_fw_version, 0, sizeof(isp_fw_version)); fw_isp = &fw_data->fw_summary.subsys[0]; ts_info("Loading ISP start"); - r = goodix_reg_write_confirm(ISP_RAM_ADDR, - (u8 *)fw_isp->data, fw_isp->size); + r = goodix_reg_write_confirm(isp_ram_reg, + (u8 *)fw_isp->data, fw_isp->size); if (r < 0) { ts_err("Loading ISP error"); return r; @@ -459,6 +595,9 @@ static int goodix_load_isp(struct firmware_data *fw_data) */ static int goodix_update_prepare(struct fw_update_ctrl *fwu_ctrl) { + u32 misctl_reg = fwu_ctrl->update_info->misctl_reg; + u32 watch_dog_reg = fwu_ctrl->update_info->watch_dog_reg; + u32 enable_misctl_val = fwu_ctrl->update_info->enable_misctl_val; u8 reg_val[4] = {0}; u8 temp_buf[64] = {0}; int retry = 20; @@ -494,52 +633,59 @@ static int goodix_update_prepare(struct fw_update_ctrl *fwu_ctrl) ts_info("Success hold CPU"); /* enable misctl clock */ - reg_val[0] = 0x08; - r = goodix_reg_write(MISCTL_REG, reg_val, 1); + if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_D) + goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 4); + else + goodix_reg_write(misctl_reg, (u8 *)&enable_misctl_val, 1); ts_info("enbale misctl clock"); - /* open ESD_KEY */ - retry = 20; - do { - reg_val[0] = 0x95; - r = goodix_reg_write(ESD_KEY_REG, reg_val, 1); - r |= goodix_reg_read(ESD_KEY_REG, temp_buf, 1); - if (!r && temp_buf[0] == 0x01) - break; - usleep_range(1000, 1100); - ts_info("retry %d enable esd key, 0x%x", retry, temp_buf[0]); - } while (--retry); - if (!retry) { - ts_err("Failed to enable esd key, return =%d", r); - return -2; + if (fwu_ctrl->core_data->bus->ic_type == IC_TYPE_BERLIN_A) { + /* open ESD_KEY */ + retry = 20; + do { + reg_val[0] = 0x95; + r = goodix_reg_write(ESD_KEY_REG, reg_val, 1); + r |= goodix_reg_read(ESD_KEY_REG, temp_buf, 1); + if (!r && temp_buf[0] == 0x01) + break; + usleep_range(1000, 1100); + ts_info("retry %d enable esd key, 0x%x", + retry, temp_buf[0]); + } while (--retry); + if (!retry) { + ts_err("Failed to enable esd key, return =%d", r); + return -2; + } + ts_info("success enable esd key"); } - ts_info("success enable esd key"); /* disable watch dog */ reg_val[0] = 0x00; - r = goodix_reg_write(WATCH_DOG_REG, reg_val, 1); + r = goodix_reg_write(watch_dog_reg, reg_val, 1); ts_info("disable watch dog"); /* load ISP code and run form isp */ r = goodix_load_isp(&fwu_ctrl->fw_data); if (r < 0) - ts_err("Failed lode and run isp"); + ts_err("Failed load and run isp"); return r; } -/* goodix_send_flash_cmd: send command to read or write flash data - * @flash_cmd: command need to send. - * */ +/* goodix_send_flash_cmd: send command to read or write flash data + * @flash_cmd: command need to send. + */ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) { int i, ret, retry; struct goodix_flash_cmd tmp_cmd; + u32 flash_cmd_reg = goodix_fw_update_ctrl.update_info->flash_cmd_reg; ts_info("try send flash cmd:%*ph", (int)sizeof(flash_cmd->buf), flash_cmd->buf); memset(tmp_cmd.buf, 0, sizeof(tmp_cmd)); - ret = goodix_reg_write(FLASH_CMD_REG, flash_cmd->buf, sizeof(flash_cmd->buf)); + ret = goodix_reg_write(flash_cmd_reg, + flash_cmd->buf, sizeof(flash_cmd->buf)); if (ret) { ts_err("failed send flash cmd %d", ret); return ret; @@ -547,14 +693,17 @@ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) retry = 5; for (i = 0; i < retry; i++) { - ret = goodix_reg_read(FLASH_CMD_REG, tmp_cmd.buf, sizeof(tmp_cmd.buf)); + ret = goodix_reg_read(flash_cmd_reg, + tmp_cmd.buf, sizeof(tmp_cmd.buf)); if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS) break; usleep_range(5000, 5100); - ts_info("flash cmd ack error retry %d, ack 0x%x, ret %d", i, tmp_cmd.ack, ret); + ts_info("flash cmd ack error retry %d, ack 0x%x, ret %d", + i, tmp_cmd.ack, ret); } if (tmp_cmd.ack != FLASH_CMD_ACK_CHK_PASS) { - ts_err("flash cmd ack error, ack 0x%x, ret %d", tmp_cmd.ack, ret); + ts_err("flash cmd ack error, ack 0x%x, ret %d", + tmp_cmd.ack, ret); ts_err("data:%*ph", (int)sizeof(tmp_cmd.buf), tmp_cmd.buf); return -EINVAL; } @@ -563,7 +712,8 @@ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) msleep(80); retry = 20; for (i = 0; i < retry; i++) { - ret = goodix_reg_read(FLASH_CMD_REG, tmp_cmd.buf, sizeof(tmp_cmd.buf)); + ret = goodix_reg_read(flash_cmd_reg, + tmp_cmd.buf, sizeof(tmp_cmd.buf)); if (!ret && tmp_cmd.ack == FLASH_CMD_ACK_CHK_PASS && tmp_cmd.status == FLASH_CMD_W_STATUS_WRITE_OK) { ts_info("flash status check pass"); @@ -572,7 +722,7 @@ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) ts_info("flash cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", i, tmp_cmd.ack, tmp_cmd.status, ret); - msleep(15); + msleep(20); } ts_err("flash cmd status error %d, ack 0x%x, status 0x%x, ret %d", @@ -583,21 +733,21 @@ static int goodix_send_flash_cmd(struct goodix_flash_cmd *flash_cmd) } switch (tmp_cmd.status) { - case FLASH_CMD_W_STATUS_CHK_PASS: - ts_err("data check pass, but failed get follow-up results"); - return -EFAULT; - case FLASH_CMD_W_STATUS_CHK_FAIL: - ts_err("data check failed, please retry"); - return -EAGAIN; - case FLASH_CMD_W_STATUS_ADDR_ERR: - ts_err("flash target addr error, please check"); - return -EFAULT; - case FLASH_CMD_W_STATUS_WRITE_ERR: - ts_err("flash data write err, please retry"); - return -EAGAIN; - default: - ts_err("unknown status"); - return -EFAULT; + case FLASH_CMD_W_STATUS_CHK_PASS: + ts_err("data check pass, but failed get follow-up results"); + return -EFAULT; + case FLASH_CMD_W_STATUS_CHK_FAIL: + ts_err("data check failed, please retry"); + return -EAGAIN; + case FLASH_CMD_W_STATUS_ADDR_ERR: + ts_err("flash target addr error, please check"); + return -EFAULT; + case FLASH_CMD_W_STATUS_WRITE_ERR: + ts_err("flash data write err, please retry"); + return -EAGAIN; + default: + ts_err("unknown status"); + return -EFAULT; } } @@ -606,10 +756,11 @@ static int goodix_flash_package(u8 subsys_type, u8 *pkg, { int ret, retry; struct goodix_flash_cmd flash_cmd; + u32 isp_buffer_reg = goodix_fw_update_ctrl.update_info->isp_buffer_reg; retry = 2; do { - ret = goodix_reg_write_confirm(HW_REG_ISP_BUFFER, pkg, pkg_len); + ret = goodix_reg_write(isp_buffer_reg, pkg, pkg_len); if (ret < 0) { ts_err("Failed to write firmware packet"); return ret; @@ -628,7 +779,8 @@ static int goodix_flash_package(u8 subsys_type, u8 *pkg, ret = goodix_send_flash_cmd(&flash_cmd); if (!ret) { - ts_info("success write package to 0x%x, len %d", flash_addr, pkg_len - 4); + ts_info("success write package to 0x%x, len %d", + flash_addr, pkg_len - 4); return 0; } } while (ret == -EAGAIN && --retry); @@ -643,12 +795,12 @@ static int goodix_flash_package(u8 subsys_type, u8 *pkg, * packets, the max size of packet is limited to * @{ISP_MAX_BUFFERSIZE} * @dev: pointer to touch device - * @subsys: subsystem infomation + * @subsys: subsystem information * return: 0 ok, < 0 error */ static int goodix_flash_subsystem(struct fw_subsys_info *subsys) { - u16 data_size, offset; + u32 data_size, offset; u32 total_size; //TODO: confirm flash addr ,<< 8?? u32 subsys_base_addr = subsys->flash_addr; @@ -706,11 +858,13 @@ static int goodix_flash_firmware(struct fw_update_ctrl *fw_ctrl) struct firmware_summary *fw_summary; struct fw_subsys_info *fw_x; struct fw_subsys_info subsys_cfg = {0}; + u32 config_data_reg = fw_ctrl->update_info->config_data_reg; int retry = GOODIX_BUS_RETRY_TIMES; int i, r = 0, fw_num; - /* start from subsystem 1, - * subsystem 0 is the ISP program */ + /* start from subsystem 1, + * subsystem 0 is the ISP program + */ fw_summary = &fw_data->fw_summary; fw_num = fw_summary->subsys_num; @@ -719,7 +873,7 @@ static int goodix_flash_firmware(struct fw_update_ctrl *fw_ctrl) if (fw_ctrl->ic_config && fw_ctrl->ic_config->len) { subsys_cfg.data = fw_ctrl->ic_config->data; subsys_cfg.size = fw_ctrl->ic_config->len; - subsys_cfg.flash_addr = CONFIG_DATA_ADDR; + subsys_cfg.flash_addr = config_data_reg; subsys_cfg.type = CONFIG_DATA_TYPE; r = goodix_flash_subsystem(&subsys_cfg); if (r) { @@ -759,12 +913,15 @@ exit_flash: */ static int goodix_update_finish(struct fw_update_ctrl *fwu_ctrl) { + int ret; + if (goodix_fw_update_reset(100)) ts_err("reset abnormal"); - if (goodix_fw_version_compare(fwu_ctrl)) - return -EINVAL; + ret = goodix_fw_version_compare(fwu_ctrl); + if (ret == COMPARE_EQUAL || ret == COMPARE_CFG_NOTEQUAL) + return 0; - return 0; + return -EINVAL; } /** @@ -789,10 +946,12 @@ int goodix_fw_update_proc(struct fw_update_ctrl *fwu_ctrl) if (!ret) { ts_info("firmware upgraded"); return 0; - } + } else + ts_info("need to upgrade"); } start_update: + fwu_ctrl->status = UPSTA_PREPARING; do { ret = goodix_update_prepare(fwu_ctrl); if (ret) { @@ -806,14 +965,15 @@ start_update: } /* progress: 20%~100% */ + fwu_ctrl->status = UPSTA_UPDATING; ret = goodix_flash_firmware(fwu_ctrl); - if (ret == -EAGAIN && --retry1 > 0) { + if (ret < 0 && --retry1 > 0) { ts_err("Bus error, retry firmware update:%d", FW_UPDATE_RETRY - retry1); goto start_update; } if (ret) - ts_info("flash fw data enter error"); + ts_err("flash fw data enter error, ret:%d", ret); else ts_info("flash fw data success, need check version"); @@ -822,7 +982,7 @@ err_fw_prepare: if (!ret) ts_info("Firmware update successfully"); else - ts_err("Firmware update failed"); + ts_err("Firmware update failed, ret:%d", ret); return ret; } @@ -853,13 +1013,15 @@ static ssize_t goodix_sysfs_update_en_store( ts_info("set update mode:0x%x", buf[0]); if (buf[0] == '1') { - mode = UPDATE_MODE_FORCE|UPDATE_MODE_BLOCK|UPDATE_MODE_SRC_SYSFS; + mode = UPDATE_MODE_FORCE | UPDATE_MODE_BLOCK | + UPDATE_MODE_SRC_SYSFS; } else if (buf[0] == '2') { - mode = UPDATE_MODE_FORCE|UPDATE_MODE_BLOCK|UPDATE_MODE_SRC_REQUEST; + mode = UPDATE_MODE_FORCE | UPDATE_MODE_BLOCK | + UPDATE_MODE_SRC_REQUEST; } else if (buf[0] == '5') { - mode = UPDATE_MODE_FORCE|UPDATE_MODE_SRC_SYSFS; + mode = UPDATE_MODE_FORCE | UPDATE_MODE_SRC_SYSFS; } else if (buf[0] == '6') { - mode = UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST; + mode = UPDATE_MODE_FORCE | UPDATE_MODE_SRC_REQUEST; } else { ts_err("invalid update mode:0x%x", buf[0]); return -EINVAL; @@ -906,11 +1068,8 @@ static ssize_t goodix_sysfs_fwsize_store( /* use vmalloc to alloc huge memory */ fw = vmalloc(sizeof(*fw) + size); - if (fw == NULL) { - ts_err("Failed to alloc memory,size:%zu", - size + sizeof(*fw)); + if (!fw) return -ENOMEM; - } mutex_lock(&fw_ctrl->mutex); memset(fw, 0x00, sizeof(*fw) + size); data = (u8 **)&fw->data; @@ -949,21 +1108,61 @@ static ssize_t goodix_sysfs_fwimage_store(struct file *file, return count; } +/* return fw_update result */ +static ssize_t goodix_sysfs_result_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + char str[GOODIX_MAX_STR_LABLE_LEN] = {0}; + int r = -EINVAL; + + if (!fw_ctrl) + return r; + + switch (fw_ctrl->status) { + case UPSTA_PREPARING: + sprintf(str, "preparing"); + break; + case UPSTA_UPDATING: + sprintf(str, "updating"); + break; + case UPSTA_SUCCESS: + sprintf(str, "success"); + break; + case UPSTA_FAILED: + sprintf(str, "failed"); + break; + case UPSTA_NOTWORK: + default: + sprintf(str, "notwork"); + break; + } + + r = snprintf(buf, PAGE_SIZE, "result:%s spend_time:%dms\n", + str, fw_ctrl->spend_time); + + return r; +} static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); static DEVICE_ATTR(fwsize, 0664, goodix_sysfs_fwsize_show, goodix_sysfs_fwsize_store); +static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); static struct attribute *goodix_fwu_attrs[] = { &dev_attr_update_en.attr, &dev_attr_fwsize.attr, + &dev_attr_result.attr }; -static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, struct fw_update_ctrl *fw_ctrl) +static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, + struct fw_update_ctrl *fw_ctrl) { int ret = 0, i; - fw_ctrl->kobj = kobject_create_and_add("fwupdate", &core_data->pdev->dev.kobj); + fw_ctrl->kobj = kobject_create_and_add("fwupdate", + &core_data->pdev->dev.kobj); if (!fw_ctrl->kobj) { ts_err("failed create sub dir for fwupdate"); return -EINVAL; @@ -982,7 +1181,7 @@ static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, struct fw_upda } fw_ctrl->attr_fwimage.attr.name = "fwimage"; - fw_ctrl->attr_fwimage.attr.mode = S_IRUGO | S_IWUGO; + fw_ctrl->attr_fwimage.attr.mode = 0666; fw_ctrl->attr_fwimage.size = 0; fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store; ret = sysfs_create_bin_file(fw_ctrl->kobj, &fw_ctrl->attr_fwimage); @@ -1025,14 +1224,24 @@ static int goodix_request_firmware(struct firmware_data *fw_data, container_of(fw_data, struct fw_update_ctrl, fw_data); struct device *dev = &(fw_ctrl->core_data->pdev->dev); int r; + int retry = GOODIX_RETRY_3; ts_info("Request firmware image [%s]", name); - r = request_firmware(&fw_data->firmware, name, dev); - if (r < 0) + + while (retry--) { + r = request_firmware(&fw_data->firmware, name, dev); + if (!r) + break; + ts_info("get fw bin retry:[%d]", GOODIX_RETRY_3 - retry); + msleep(200); + } + if (retry < 0) { ts_err("Firmware image [%s] not available,errno:%d", name, r); - else - ts_info("Firmware image [%s] is ready", name); - return r; + return r; + } + + ts_info("Firmware image [%s] is ready", name); + return 0; } /** @@ -1051,8 +1260,12 @@ static int goodix_fw_update_thread(void *data) { struct fw_update_ctrl *fwu_ctrl = data; struct firmware *temp_firmware = NULL; + ktime_t start, end; int r = -EINVAL; + start = ktime_get(); + fwu_ctrl->spend_time = 0; + fwu_ctrl->status = UPSTA_NOTWORK; mutex_lock(&fwu_ctrl->mutex); if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { @@ -1095,11 +1308,17 @@ out: if (r) { ts_err("fw update failed, %d", r); + fwu_ctrl->status = UPSTA_FAILED; goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); } else { ts_info("fw update success"); + fwu_ctrl->status = UPSTA_SUCCESS; goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); } + + end = ktime_get(); + fwu_ctrl->spend_time = ktime_to_ms(ktime_sub(end, start)); + return r; } @@ -1147,7 +1366,7 @@ int goodix_fw_update_init(struct goodix_ts_core *core_data) goodix_fw_update_ctrl.core_data = core_data; goodix_fw_update_ctrl.mode = 0; - strlcpy(goodix_fw_update_ctrl.fw_name, TS_DEFAULT_FIRMWARE, + strlcpy(goodix_fw_update_ctrl.fw_name, core_data->board_data.fw_name, sizeof(goodix_fw_update_ctrl.fw_name)); ret = goodix_fw_sysfs_init(core_data, &goodix_fw_update_ctrl); @@ -1155,6 +1374,13 @@ int goodix_fw_update_init(struct goodix_ts_core *core_data) ts_err("failed create fwupate sysfs node"); return ret; } + if (core_data->bus->ic_type == IC_TYPE_BERLIN_A) + goodix_fw_update_ctrl.update_info = &update_bra; + else if (core_data->bus->ic_type == IC_TYPE_BERLIN_B) + goodix_fw_update_ctrl.update_info = &update_brb; + else + goodix_fw_update_ctrl.update_info = &update_brd; + goodix_fw_update_ctrl.initialized = 1; return 0; } diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_brl_hw.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_hw.c old mode 100755 new mode 100644 similarity index 61% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_brl_hw.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_hw.c index 9c798fd3b74a..1b31219eec3c --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_brl_hw.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_hw.c @@ -16,6 +16,32 @@ */ #include "goodix_ts_core.h" +/* berlin_A SPI mode setting */ +#define GOODIX_SPI_MODE_REG 0xC900 +#define GOODIX_SPI_NORMAL_MODE_0 0x01 + +/* berlin_A D12 setting */ +#define GOODIX_REG_CLK_STA0 0xD807 +#define GOODIX_CLK_STA0_ENABLE 0xFF +#define GOODIX_REG_CLK_STA1 0xD806 +#define GOODIX_CLK_STA1_ENABLE 0x77 +#define GOODIX_REG_TRIM_D12 0xD006 +#define GOODIX_TRIM_D12_LEVEL 0x3C +#define GOODIX_REG_RESET 0xD808 +#define GOODIX_RESET_EN 0xFA +#define HOLD_CPU_REG_W 0x0002 +#define HOLD_CPU_REG_R 0x2000 + +#define DEV_CONFIRM_VAL 0xAA +#define BOOTOPTION_ADDR 0x10000 +#define FW_VERSION_INFO_ADDR_BRA 0x1000C +#define FW_VERSION_INFO_ADDR 0x10014 + +#define GOODIX_IC_INFO_MAX_LEN 1024 +#define GOODIX_IC_INFO_ADDR_BRA 0x10068 +#define GOODIX_IC_INFO_ADDR 0x10070 + + enum brl_request_code { BRL_REQUEST_CODE_CONFIG = 0x01, BRL_REQUEST_CODE_REF_ERR = 0x02, @@ -23,49 +49,202 @@ enum brl_request_code { BRL_REQUEST_CODE_CLOCK = 0x04, }; +static int brl_select_spi_mode(struct goodix_ts_core *cd) +{ + int ret; + int i; + u8 w_value = GOODIX_SPI_NORMAL_MODE_0; + u8 r_value; + + if (cd->bus->bus_type == GOODIX_BUS_TYPE_I2C || + cd->bus->ic_type != IC_TYPE_BERLIN_A) + return 0; + + for (i = 0; i < GOODIX_RETRY_5; i++) { + cd->hw_ops->write(cd, GOODIX_SPI_MODE_REG, + &w_value, 1); + ret = cd->hw_ops->read(cd, GOODIX_SPI_MODE_REG, + &r_value, 1); + if (!ret && r_value == w_value) + return 0; + } + ts_err("failed switch SPI mode after reset, ret:%d r_value:%02x", ret, r_value); + return -EINVAL; +} + +static int brl_reset_after(struct goodix_ts_core *cd) +{ + u8 reg_val[2] = {0}; + u8 temp_buf[12] = {0}; + int ret; + int retry; + + if (cd->bus->ic_type != IC_TYPE_BERLIN_A) + return 0; + + ts_debug("IN"); + usleep_range(5000, 5100); + + /* select spi mode */ + ret = brl_select_spi_mode(cd); + if (ret < 0) + return ret; + + /* hold cpu */ + retry = GOODIX_RETRY_10; + while (retry--) { + reg_val[0] = 0x01; + reg_val[1] = 0x00; + ret = cd->hw_ops->write(cd, HOLD_CPU_REG_W, reg_val, 2); + ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[0], 4); + ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[4], 4); + ret |= cd->hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[8], 4); + if (!ret && !memcmp(&temp_buf[0], &temp_buf[4], 4) && + !memcmp(&temp_buf[4], &temp_buf[8], 4) && + !memcmp(&temp_buf[0], &temp_buf[8], 4)) { + break; + } + } + if (retry < 0) { + ts_err("failed to hold cpu, status:%*ph", 12, temp_buf); + return -EINVAL; + } + + /* enable sta0 clk */ + retry = GOODIX_RETRY_5; + while (retry--) { + reg_val[0] = GOODIX_CLK_STA0_ENABLE; + ret = cd->hw_ops->write(cd, GOODIX_REG_CLK_STA0, reg_val, 1); + ret |= cd->hw_ops->read(cd, GOODIX_REG_CLK_STA0, temp_buf, 1); + if (!ret && temp_buf[0] == GOODIX_CLK_STA0_ENABLE) + break; + } + if (retry < 0) { + ts_err("failed to enable group0 clock, ret:%d status:%02x", ret, temp_buf[0]); + return -EINVAL; + } + + /* enable sta1 clk */ + retry = GOODIX_RETRY_5; + while (retry--) { + reg_val[0] = GOODIX_CLK_STA1_ENABLE; + ret = cd->hw_ops->write(cd, GOODIX_REG_CLK_STA1, reg_val, 1); + ret |= cd->hw_ops->read(cd, GOODIX_REG_CLK_STA1, temp_buf, 1); + if (!ret && temp_buf[0] == GOODIX_CLK_STA1_ENABLE) + break; + } + if (retry < 0) { + ts_err("failed to enable group1 clock, ret:%d status:%02x", ret, temp_buf[0]); + return -EINVAL; + } + + /* set D12 level */ + retry = GOODIX_RETRY_5; + while (retry--) { + reg_val[0] = GOODIX_TRIM_D12_LEVEL; + ret = cd->hw_ops->write(cd, GOODIX_REG_TRIM_D12, reg_val, 1); + ret |= cd->hw_ops->read(cd, GOODIX_REG_TRIM_D12, temp_buf, 1); + if (!ret && temp_buf[0] == GOODIX_TRIM_D12_LEVEL) + break; + } + if (retry < 0) { + ts_err("failed to set D12, ret:%d status:%02x", ret, temp_buf[0]); + return -EINVAL; + } + + usleep_range(5000, 5100); + /* soft reset */ + reg_val[0] = GOODIX_RESET_EN; + ret = cd->hw_ops->write(cd, GOODIX_REG_RESET, reg_val, 1); + if (ret < 0) + return ret; + + /* select spi mode */ + ret = brl_select_spi_mode(cd); + if (ret < 0) + return ret; + + ts_debug("OUT"); + + return 0; +} + static int brl_power_on(struct goodix_ts_core *cd, bool on) { int ret = 0; + int iovdd_gpio = cd->board_data.iovdd_gpio; + int avdd_gpio = cd->board_data.avdd_gpio; + int reset_gpio = cd->board_data.reset_gpio; - if (!cd->avdd) + if (!cd->avdd || !cd->iovdd) { + /* if no regulator set, only set the reset gpio value */ + if (on) { + if (avdd_gpio > 0 && iovdd_gpio > 0) { + gpio_direction_output(iovdd_gpio, 1); + usleep_range(3000, 3100); + gpio_direction_output(avdd_gpio, 1); + usleep_range(15000, 15100); + } + gpio_direction_output(reset_gpio, 1); + ret = brl_reset_after(cd); + if (ret < 0) { + ts_err("reset_after process failed"); + gpio_direction_output(reset_gpio, 0); + if (avdd_gpio > 0 && iovdd_gpio > 0) { + gpio_direction_output(iovdd_gpio, 0); + gpio_direction_output(avdd_gpio, 0); + } + return ret; + } + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + } else { + gpio_direction_output(reset_gpio, 0); + if (avdd_gpio > 0 && iovdd_gpio > 0) { + gpio_direction_output(iovdd_gpio, 0); + gpio_direction_output(avdd_gpio, 0); + } + usleep_range(10000, 11000); + } return 0; + } - if (cd->iovdd && on) { + if (on) { + /* must guarantee iovdd enbaled before avdd */ ret = regulator_enable(cd->iovdd); if (ret) { ts_err("Failed to enable iovdd:%d", ret); return ret; } usleep_range(3000, 3100); - } - /* must promise iovdd enbaled before avdd */ - if (cd->avdd && on) { ret = regulator_enable(cd->avdd); - if (!ret) { - ts_info("regulator enable SUCCESS"); - usleep_range(15000, 15100); - return 0; - } - if (cd->iovdd) + if (ret) { regulator_disable(cd->iovdd); - ts_err("Failed to enable avdd:%d", ret); - return ret; + ts_err("Failed to enable avdd:%d", ret); + return ret; + } + ts_info("regulator enable SUCCESS"); + usleep_range(15000, 15100); + gpio_direction_output(reset_gpio, 1); + ret = brl_reset_after(cd); + if (ret < 0) { + ts_err("reset_after process failed,ret=%d", ret); + gpio_direction_output(reset_gpio, 0); + return ret; + } + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + return 0; } /*power off process */ - if (cd->iovdd) { - ret = regulator_disable(cd->iovdd); - if (ret) - ts_err("Failed to disable iovdd:%d", ret); - } + gpio_direction_output(reset_gpio, 0); + ret = regulator_disable(cd->iovdd); + if (ret) + ts_err("Failed to disable iovdd:%d", ret); + ret = regulator_disable(cd->avdd); + if (ret) + ts_err("Failed to disable avdd:%d", ret); + usleep_range(10000, 11000); - if (cd->avdd) { - ret = regulator_disable(cd->avdd); - if (!ret) - ts_info("regulator disable SUCCESS"); - else - ts_err("Failed to disable analog power:%d", ret); - } return ret; } @@ -103,40 +282,48 @@ int brl_gesture(struct goodix_ts_core *cd, int gesture_type) static int brl_dev_confirm(struct goodix_ts_core *cd) { - return 0; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + int ret = 0; + int retry = GOODIX_RETRY_3; + u8 tx_buf[8] = {0}; + u8 rx_buf[8] = {0}; + + memset(tx_buf, DEV_CONFIRM_VAL, sizeof(tx_buf)); + while (retry--) { + ret = hw_ops->write(cd, BOOTOPTION_ADDR, + tx_buf, sizeof(tx_buf)); + if (ret < 0) + return ret; + ret = hw_ops->read(cd, BOOTOPTION_ADDR, + rx_buf, sizeof(rx_buf)); + if (ret < 0) + return ret; + if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) + break; + usleep_range(5000, 5100); + } + + if (retry < 0) { + ret = -EINVAL; + ts_err("device confirm failed, rx_buf:%*ph", 8, rx_buf); + } + + return ret; } -#define GOODIX_SPI_MODE_REG 0xC900 -#define GOODIX_SPI_NORMAL_MODE_0 0x1 -#define SWITCH_MODE_RETRY 30 static int brl_reset(struct goodix_ts_core *cd, int delay) { - struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; - u8 w_value = GOODIX_SPI_NORMAL_MODE_0; - u8 r_value = 0; - int ret, i; + ts_info("chip_reset"); gpio_direction_output(cd->board_data.reset_gpio, 0); - udelay(2000); + usleep_range(2000, 2100); gpio_direction_output(cd->board_data.reset_gpio, 1); if (delay < 20) usleep_range(delay * 1000, delay * 1000 + 100); else msleep(delay); - if (cd->bus->bus_type == GOODIX_BUS_TYPE_I2C) - return 0; - - for (i = 0; i < SWITCH_MODE_RETRY; i++) { - hw_ops->write(cd, GOODIX_SPI_MODE_REG, - &w_value, 1); - ret = hw_ops->read(cd, GOODIX_SPI_MODE_REG, - &r_value, 1); - if (!ret && r_value == w_value) - return 0; - } - ts_err("failed switch SPI mode after reset"); - return -EINVAL; + return brl_select_spi_mode(cd); } static int brl_irq_enbale(struct goodix_ts_core *cd, bool enable) @@ -180,7 +367,8 @@ static int brl_write(struct goodix_ts_core *cd, unsigned int addr, #define CMD_ACK_OK 0x80 #define GOODIX_CMD_RETRY 6 -static int brl_send_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd) +static int brl_send_cmd(struct goodix_ts_core *cd, + struct goodix_ts_cmd *cmd) { int ret, retry, i; struct goodix_ts_cmd cmd_ack; @@ -190,7 +378,7 @@ static int brl_send_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd) cmd->state = 0; cmd->ack = 0; goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, - CHECKSUM_MODE_U8_LE); + CHECKSUM_MODE_U8_LE); ts_debug("cmd data %*ph", cmd->len, &(cmd->buf[2])); retry = 0; @@ -204,7 +392,7 @@ static int brl_send_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd) for (i = 0; i < GOODIX_CMD_RETRY; i++) { /* check command result */ ret = hw_ops->read(cd, misc->cmd_addr, - cmd_ack.buf, sizeof(cmd_ack)); + cmd_ack.buf, sizeof(cmd_ack)); if (ret < 0) { ts_err("failed read command ack, %d", ret); return ret; @@ -215,7 +403,6 @@ static int brl_send_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd) usleep_range(2000, 2100); return 0; } - if (cmd_ack.ack == CMD_ACK_BUSY || cmd_ack.ack == 0x00) { usleep_range(1000, 1100); @@ -225,9 +412,9 @@ static int brl_send_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cmd) usleep_range(10000, 11000); usleep_range(1000, 1100); break; - } + } } - ts_info("failed get valid cmd ack"); + ts_err("failed get valid cmd ack"); return -EINVAL; } @@ -254,17 +441,18 @@ struct goodix_config_head { }; #pragma pack() -#define CONFIG_CND_LEN 4 -#define CONFIG_CMD_START 0x04 -#define CONFIG_CMD_WRITE 0x05 -#define CONFIG_CMD_EXIT 0x06 -#define CONFIG_CMD_READ_START 0x07 -#define CONFIG_CMD_READ_EXIT 0x08 +#define CONFIG_CND_LEN 4 +#define CONFIG_CMD_START 0x04 +#define CONFIG_CMD_WRITE 0x05 +#define CONFIG_CMD_EXIT 0x06 +#define CONFIG_CMD_READ_START 0x07 +#define CONFIG_CMD_READ_EXIT 0x08 -#define CONFIG_CMD_STATUS_PASS 0x80 -#define CONFIG_CMD_WAIT_RETRY 20 +#define CONFIG_CMD_STATUS_PASS 0x80 +#define CONFIG_CMD_WAIT_RETRY 20 -static int wait_cmd_status(struct goodix_ts_core *cd, u8 target_status, int retry) +static int wait_cmd_status(struct goodix_ts_core *cd, + u8 target_status, int retry) { struct goodix_ts_cmd cmd_ack; struct goodix_ic_info_misc *misc = &cd->ic_info.misc; @@ -272,21 +460,23 @@ static int wait_cmd_status(struct goodix_ts_core *cd, u8 target_status, int retr int i, ret; for (i = 0; i < retry; i++) { - ret = hw_ops->read(cd, misc->cmd_addr, cmd_ack.buf, sizeof(cmd_ack)); + ret = hw_ops->read(cd, misc->cmd_addr, cmd_ack.buf, + sizeof(cmd_ack)); if (!ret && cmd_ack.state == target_status) { ts_debug("status check pass"); return 0; } - - ts_info("cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", - i, cmd_ack.ack, cmd_ack.state, ret); ts_debug("cmd buf %*ph", (int)sizeof(cmd_ack), cmd_ack.buf); - msleep(15); + msleep(20); } + + ts_err("cmd status not ready, retry %d, ack 0x%x, status 0x%x, ret %d", + i, cmd_ack.ack, cmd_ack.state, ret); return -EINVAL; } -static int send_cfg_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cfg_cmd) +static int send_cfg_cmd(struct goodix_ts_core *cd, + struct goodix_ts_cmd *cfg_cmd) { int ret; @@ -295,7 +485,8 @@ static int send_cfg_cmd(struct goodix_ts_core *cd, struct goodix_ts_cmd *cfg_cmd ts_err("failed write cfg prepare cmd %d", ret); return ret; } - ret = wait_cmd_status(cd, CONFIG_CMD_STATUS_PASS, CONFIG_CMD_WAIT_RETRY); + ret = wait_cmd_status(cd, CONFIG_CMD_STATUS_PASS, + CONFIG_CMD_WAIT_RETRY); if (ret) { ts_err("failed wait for fw ready for config, %d", ret); return ret; @@ -313,15 +504,13 @@ static int brl_send_config(struct goodix_ts_core *cd, u8 *cfg, int len) if (len > misc->fw_buffer_max_len) { ts_err("config len exceed limit %d > %d", - len , misc->fw_buffer_max_len); + len, misc->fw_buffer_max_len); return -EINVAL; } tmp_buf = kzalloc(len, GFP_KERNEL); - if (!tmp_buf) { - ts_err("failed alloc malloc"); + if (!tmp_buf) return -ENOMEM; - } cfg_cmd.len = CONFIG_CND_LEN; cfg_cmd.cmd = CONFIG_CMD_START; @@ -375,8 +564,8 @@ exit: } /* - *return: return config length on succes, other wise return < 0 - * */ + * return: return config length on succes, other wise return < 0 + **/ static int brl_read_config(struct goodix_ts_core *cd, u8 *cfg, int size) { int ret; @@ -448,30 +637,33 @@ exit: return cfg_head.cfg_len + sizeof(cfg_head); } -#define GOODIX_READ_VERSION_RETRY 5 -#define FW_VERSION_INFO_ADDR 0x1000C -#define GOODIX_NORMAL_PID "9897" - /* - * return: 0 for no error. - * GOODIX_EBUS when encounter a bus error - * GOODIX_ECHECKSUM version checksum error - * GOODIX_EVERSION patch ID compare failed, - * in this case the sensorID is valid. -*/ + * return: 0 for no error. + * GOODIX_EBUS when encounter a bus error + * GOODIX_ECHECKSUM version checksum error + * GOODIX_EVERSION patch ID compare failed, + * in this case the sensorID is valid. + */ static int brl_read_version(struct goodix_ts_core *cd, struct goodix_fw_version *version) { int ret, i; + u32 fw_addr; struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; u8 buf[sizeof(struct goodix_fw_version)] = {0}; + u8 temp_pid[8] = {0}; - for (i = 0; i < GOODIX_READ_VERSION_RETRY; i++) { - ret = hw_ops->read(cd, FW_VERSION_INFO_ADDR, buf, sizeof(buf)); + if (cd->bus->ic_type == IC_TYPE_BERLIN_A) + fw_addr = FW_VERSION_INFO_ADDR_BRA; + else + fw_addr = FW_VERSION_INFO_ADDR; + + for (i = 0; i < 3; i++) { + ret = hw_ops->read(cd, fw_addr, buf, sizeof(buf)); if (ret) { ts_info("read fw version: %d, retry %d", ret, i); ret = -GOODIX_EBUS; - msleep(5); + usleep_range(5000, 5100); continue; } @@ -481,23 +673,22 @@ static int brl_read_version(struct goodix_ts_core *cd, ts_info("invalid fw version: checksum error!"); ts_info("fw version:%*ph", (int)sizeof(buf), buf); ret = -GOODIX_ECHECKSUM; - msleep(10); + usleep_range(10000, 11000); } if (ret) { ts_err("failed get valied fw version"); return ret; } memcpy(version, buf, sizeof(*version)); - ts_info("rom_pid:%*ph", (int)sizeof(version->rom_pid), version->rom_pid); - ts_info("rom_vid:%*ph", (int)sizeof(version->rom_vid), version->rom_vid); - ts_info("pid:%*ph", (int)sizeof(version->patch_pid), version->patch_pid); - ts_info("vid:%*ph", (int)sizeof(version->patch_vid), version->patch_vid); + memcpy(temp_pid, version->rom_pid, sizeof(version->rom_pid)); + ts_info("rom_pid:%s", temp_pid); + ts_info("rom_vid:%*ph", (int)sizeof(version->rom_vid), + version->rom_vid); + ts_info("pid:%s", version->patch_pid); + ts_info("vid:%*ph", (int)sizeof(version->patch_vid), + version->patch_vid); ts_info("sensor_id:%d", version->sensor_id); - if (memcmp(GOODIX_NORMAL_PID, version->patch_pid, - strlen(GOODIX_NORMAL_PID))) { - ts_info("abnormal patch id detected"); - //return GOODIX_EVERSION; - } + return 0; } @@ -519,11 +710,16 @@ static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) data += sizeof(struct goodix_ic_info_version); memcpy(feature, data, sizeof(*feature)); - feature->freqhop_feature = le16_to_cpu(feature->freqhop_feature); - feature->calibration_feature = le16_to_cpu(feature->calibration_feature); - feature->gesture_feature = le16_to_cpu(feature->gesture_feature); - feature->side_touch_feature = le16_to_cpu(feature->side_touch_feature); - feature->stylus_feature = le16_to_cpu(feature->stylus_feature); + feature->freqhop_feature = + le16_to_cpu(feature->freqhop_feature); + feature->calibration_feature = + le16_to_cpu(feature->calibration_feature); + feature->gesture_feature = + le16_to_cpu(feature->gesture_feature); + feature->side_touch_feature = + le16_to_cpu(feature->side_touch_feature); + feature->stylus_feature = + le16_to_cpu(feature->stylus_feature); data += sizeof(struct goodix_ic_info_feature); parm->drv_num = *(data++); @@ -537,7 +733,8 @@ static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) return -EINVAL; } for (i = 0; i < parm->active_scan_rate_num; i++) - parm->active_scan_rate[i] = le16_to_cpup((__le16 *)(data + i * 2)); + parm->active_scan_rate[i] = + le16_to_cpup((__le16 *)(data + i * 2)); data += parm->active_scan_rate_num * 2; parm->mutual_freq_num = *(data++); @@ -547,7 +744,8 @@ static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) return -EINVAL; } for (i = 0; i < parm->mutual_freq_num; i++) - parm->mutual_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + parm->mutual_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); data += parm->mutual_freq_num * 2; parm->self_tx_freq_num = *(data++); @@ -557,7 +755,8 @@ static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) return -EINVAL; } for (i = 0; i < parm->self_tx_freq_num; i++) - parm->self_tx_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + parm->self_tx_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); data += parm->self_tx_freq_num * 2; parm->self_rx_freq_num = *(data++); @@ -567,7 +766,8 @@ static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) return -EINVAL; } for (i = 0; i < parm->self_rx_freq_num; i++) - parm->self_rx_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + parm->self_rx_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); data += parm->self_rx_freq_num * 2; parm->stylus_freq_num = *(data++); @@ -577,7 +777,8 @@ static int convert_ic_info(struct goodix_ic_info *info, const u8 *data) return -EINVAL; } for (i = 0; i < parm->stylus_freq_num; i++) - parm->stylus_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + parm->stylus_freq[i] = + le16_to_cpup((__le16 *)(data + i * 2)); data += parm->stylus_freq_num * 2; memcpy(misc, data, sizeof(*misc)); @@ -628,26 +829,43 @@ static void print_ic_info(struct goodix_ic_info *ic_info) struct goodix_ic_info_param *parm = &ic_info->parm; struct goodix_ic_info_misc *misc = &ic_info->misc; - ts_info("ic_info_length: %d", ic_info->length); - ts_info("info_customer_id: 0x%01X", version->info_customer_id); - ts_info("info_version_id: 0x%01X", version->info_version_id); - ts_info("ic_die_id: 0x%01X", version->ic_die_id); - ts_info("ic_version_id: 0x%01X", version->ic_version_id); - ts_info("config_id: 0x%4X", version->config_id); - ts_info("config_version: 0x%01X", version->config_version); - ts_info("frame_data_customer_id: 0x%01X", version->frame_data_customer_id); - ts_info("frame_data_version_id: 0x%01X", version->frame_data_version_id); - ts_info("touch_data_customer_id: 0x%01X", version->touch_data_customer_id); - ts_info("touch_data_version_id: 0x%01X", version->touch_data_version_id); + ts_info("ic_info_length: %d", + ic_info->length); + ts_info("info_customer_id: 0x%01X", + version->info_customer_id); + ts_info("info_version_id: 0x%01X", + version->info_version_id); + ts_info("ic_die_id: 0x%01X", + version->ic_die_id); + ts_info("ic_version_id: 0x%01X", + version->ic_version_id); + ts_info("config_id: 0x%4X", + version->config_id); + ts_info("config_version: 0x%01X", + version->config_version); + ts_info("frame_data_customer_id: 0x%01X", + version->frame_data_customer_id); + ts_info("frame_data_version_id: 0x%01X", + version->frame_data_version_id); + ts_info("touch_data_customer_id: 0x%01X", + version->touch_data_customer_id); + ts_info("touch_data_version_id: 0x%01X", + version->touch_data_version_id); - ts_info("freqhop_feature: 0x%04X", feature->freqhop_feature); - ts_info("calibration_feature: 0x%04X", feature->calibration_feature); - ts_info("gesture_feature: 0x%04X", feature->gesture_feature); - ts_info("side_touch_feature: 0x%04X", feature->side_touch_feature); - ts_info("stylus_feature: 0x%04X", feature->stylus_feature); + ts_info("freqhop_feature: 0x%04X", + feature->freqhop_feature); + ts_info("calibration_feature: 0x%04X", + feature->calibration_feature); + ts_info("gesture_feature: 0x%04X", + feature->gesture_feature); + ts_info("side_touch_feature: 0x%04X", + feature->side_touch_feature); + ts_info("stylus_feature: 0x%04X", + feature->stylus_feature); ts_info("Drv*Sen,Button,Force num: %d x %d, %d, %d", - parm->drv_num, parm->sen_num, parm->button_num, parm->force_num); + parm->drv_num, parm->sen_num, + parm->button_num, parm->force_num); ts_info("Cmd: 0x%04X, %d", misc->cmd_addr, misc->cmd_max_len); @@ -659,32 +877,38 @@ static void print_ic_info(struct goodix_ic_info *ic_info) misc->fw_buffer_addr, misc->fw_buffer_max_len); ts_info("Touch-Data: 0x%04X, %d", misc->touch_data_addr, misc->touch_data_head_len); - ts_info("point_struct_len: %d", misc->point_struct_len); + ts_info("point_struct_len: %d", + misc->point_struct_len); ts_info("mutual_rawdata_addr: 0x%04X", misc->mutual_rawdata_addr); ts_info("mutual_diffdata_addr: 0x%04X", misc->mutual_diffdata_addr); ts_info("self_rawdata_addr: 0x%04X", misc->self_rawdata_addr); - ts_info("self_rawdata_addr: 0x%04X", - misc->self_rawdata_addr); + ts_info("self_diffdata_addr: 0x%04X", + misc->self_diffdata_addr); ts_info("stylus_rawdata_addr: 0x%04X, %d", misc->stylus_rawdata_addr, misc->stylus_rawdata_len); - ts_info("esd_addr: 0x%04X", misc->esd_addr); + ts_info("esd_addr: 0x%04X", + misc->esd_addr); } -#define GOODIX_GET_IC_INFO_RETRY 3 -#define GOODIX_IC_INFO_MAX_LEN 1024 -#define GOODIX_IC_INFO_ADDR 0x10068 -static int brl_get_ic_info(struct goodix_ts_core *cd, struct goodix_ic_info *ic_info) +static int brl_get_ic_info(struct goodix_ts_core *cd, + struct goodix_ic_info *ic_info) { int ret, i; u16 length = 0; + u32 ic_addr; u8 afe_data[GOODIX_IC_INFO_MAX_LEN] = {0}; struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; - for (i = 0; i < GOODIX_GET_IC_INFO_RETRY; i++) { - ret = hw_ops->read(cd, GOODIX_IC_INFO_ADDR, + if (cd->bus->ic_type == IC_TYPE_BERLIN_A) + ic_addr = GOODIX_IC_INFO_ADDR_BRA; + else + ic_addr = GOODIX_IC_INFO_ADDR; + + for (i = 0; i < GOODIX_RETRY_3; i++) { + ret = hw_ops->read(cd, ic_addr, (u8 *)&length, sizeof(length)); if (ret) { ts_info("failed get ic info length, %d", ret); @@ -693,11 +917,12 @@ static int brl_get_ic_info(struct goodix_ts_core *cd, struct goodix_ic_info *ic_ } length = le16_to_cpu(length); if (length >= GOODIX_IC_INFO_MAX_LEN) { - ts_info("invalid ic info length %d, retry %d", length, i); + ts_info("invalid ic info length %d, retry %d", + length, i); continue; } - ret = hw_ops->read(cd, GOODIX_IC_INFO_ADDR, afe_data, length); + ret = hw_ops->read(cd, ic_addr, afe_data, length); if (ret) { ts_info("failed get ic info data, %d", ret); usleep_range(5000, 5100); @@ -710,14 +935,14 @@ static int brl_get_ic_info(struct goodix_ts_core *cd, struct goodix_ic_info *ic_ continue; } if (checksum_cmp((const uint8_t *)afe_data, - length, CHECKSUM_MODE_U8_LE)) { + length, CHECKSUM_MODE_U8_LE)) { ts_info("fw info checksum error!"); usleep_range(5000, 5100); continue; } break; } - if (i == GOODIX_GET_IC_INFO_RETRY) { + if (i == GOODIX_RETRY_3) { ts_err("failed get ic info"); return -EINVAL; } @@ -727,13 +952,16 @@ static int brl_get_ic_info(struct goodix_ts_core *cd, struct goodix_ic_info *ic_ ts_err("convert ic info encounter error"); return ret; } + print_ic_info(ic_info); + /* check some key info */ if (!ic_info->misc.cmd_addr || !ic_info->misc.fw_buffer_addr || !ic_info->misc.touch_data_addr) { ts_err("cmd_addr fw_buf_addr and touch_data_addr is null"); return -EINVAL; } + return 0; } @@ -761,35 +989,33 @@ static int brl_esd_check(struct goodix_ts_core *cd) esd_value = GOODIX_ESD_TICK_WRITE_DATA; ret = cd->hw_ops->write(cd, esd_addr, &esd_value, 1); if (ret) { - ts_err("falied refrash esd value"); + ts_err("failed refrash esd value"); return ret; } return 0; } -#define IRQ_EVENT_HEAD_LEN 8 -#define BYTES_PER_POINT 8 +#define IRQ_EVENT_HEAD_LEN 8 +#define BYTES_PER_POINT 8 #define COOR_DATA_CHECKSUM_SIZE 2 -#define GOODIX_TOUCH_EVENT 0x80 +#define GOODIX_TOUCH_EVENT 0x80 #define GOODIX_REQUEST_EVENT 0x40 #define GOODIX_GESTURE_EVENT 0x20 -#define POINT_TYPE_STYLUS_HOVER 0x01 -#define POINT_TYPE_STYLUS 0x03 +#define POINT_TYPE_STYLUS_HOVER 0x01 +#define POINT_TYPE_STYLUS 0x03 static void goodix_parse_finger(struct goodix_touch_data *touch_data, u8 *buf, int touch_num) { unsigned int id = 0, x = 0, y = 0, w = 0; - static u32 pre_finger_map; - u32 cur_finger_map = 0; u8 *coor_data; int i; coor_data = &buf[IRQ_EVENT_HEAD_LEN]; for (i = 0; i < touch_num; i++) { id = (coor_data[0] >> 4) & 0x0F; - if(id >= GOODIX_MAX_TOUCH){ + if (id >= GOODIX_MAX_TOUCH) { ts_info("invalid finger id =%d", id); touch_data->touch_num = 0; return; @@ -801,23 +1027,14 @@ static void goodix_parse_finger(struct goodix_touch_data *touch_data, touch_data->coords[id].x = x; touch_data->coords[id].y = y; touch_data->coords[id].w = w; - cur_finger_map |= (1 << id); coor_data += BYTES_PER_POINT; } - - /* process finger release */ - for (i = 0; i < GOODIX_MAX_TOUCH; i++) { - if (cur_finger_map & (1 << i)) - continue; - if (pre_finger_map & (1 << i)) - touch_data->coords[i].status = TS_RELEASE; - } - pre_finger_map = cur_finger_map; touch_data->touch_num = touch_num; } static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2}; -static void goodix_parse_pen(struct goodix_pen_data *pen_data, u8 *buf, int touch_num) +static void goodix_parse_pen(struct goodix_pen_data *pen_data, + u8 *buf, int touch_num) { unsigned int id = 0; static u8 pre_key_map; @@ -830,17 +1047,22 @@ static void goodix_parse_pen(struct goodix_pen_data *pen_data, u8 *buf, int touc if (touch_num) { pen_data->coords.status = TS_TOUCH; coor_data = &buf[IRQ_EVENT_HEAD_LEN + - BYTES_PER_POINT *(touch_num -1)]; + BYTES_PER_POINT * (touch_num - 1)]; id = (coor_data[0] >> 4) & 0x0F; pen_data->coords.x = le16_to_cpup((__le16 *)(coor_data + 2)); pen_data->coords.y = le16_to_cpup((__le16 *)(coor_data + 4)); pen_data->coords.p = le16_to_cpup((__le16 *)(coor_data + 6)); + /* attach pen info, such as TILT */ + if (touch_num > 1) { + pen_data->coords.tilt_x = coor_data[8]; + pen_data->coords.tilt_y = coor_data[9]; + } } else { pen_data->coords.status = TS_RELEASE; } - cur_key_map = 0; // TODO confirm stylus key info + cur_key_map = (buf[3] & 0x0F) >> 1; for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { if (!(cur_key_map & (1 << i))) continue; @@ -854,7 +1076,6 @@ static void goodix_parse_pen(struct goodix_pen_data *pen_data, u8 *buf, int touc pen_data->keys[i].code = goodix_pen_btn_code[i]; } pre_key_map = cur_key_map; - return; } static int goodix_touch_handler(struct goodix_ts_core *cd, @@ -870,9 +1091,8 @@ static int goodix_touch_handler(struct goodix_ts_core *cd, u8 touch_num = 0; int ret = 0; u8 point_type = 0; - - static u8 pre_finger_num = 0; - static u8 pre_pen_num = 0; + static u8 pre_finger_num; + static u8 pre_pen_num; /* clean event buffer */ memset(ts_event, 0, sizeof(*ts_event)); @@ -896,22 +1116,18 @@ static int goodix_touch_handler(struct goodix_ts_core *cd, } } - goodix_debugfs_printf("touch data:%*ph\n", - touch_num * BYTES_PER_POINT + 2, - &buffer[IRQ_EVENT_HEAD_LEN]); if (touch_num && checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN], touch_num * BYTES_PER_POINT + 2, CHECKSUM_MODE_U8_LE)) { ts_debug("touch data checksum error"); - goodix_debugfs_printf("touch data checksum error"); ts_debug("data:%*ph", touch_num * BYTES_PER_POINT + 2, &buffer[IRQ_EVENT_HEAD_LEN]); return -EINVAL; } if (touch_num > 0) - point_type = buffer[(touch_num - 1) * BYTES_PER_POINT + - IRQ_EVENT_HEAD_LEN] & 0x0F; + point_type = buffer[IRQ_EVENT_HEAD_LEN] & 0x0F; + if (touch_num > 0 && (point_type == POINT_TYPE_STYLUS || point_type == POINT_TYPE_STYLUS_HOVER)) { /* stylus info */ @@ -939,9 +1155,9 @@ static int goodix_touch_handler(struct goodix_ts_core *cd, } /* process custom info */ - if (buffer[3] & 0x01) { + if (buffer[3] & 0x01) ts_debug("TODO add custom info process function"); - } + return 0; } @@ -959,19 +1175,18 @@ static int brl_event_handler(struct goodix_ts_core *cd, BYTES_PER_POINT + COOR_DATA_CHECKSUM_SIZE; ret = hw_ops->read(cd, misc->touch_data_addr, pre_buf, pre_read_len); - if (ret) { ts_debug("failed get event head data"); return ret; } - goodix_debugfs_printf("touch_head:%*ph\n", - IRQ_EVENT_HEAD_LEN, pre_buf); + if (checksum_cmp(pre_buf, IRQ_EVENT_HEAD_LEN, CHECKSUM_MODE_U8_LE)) { ts_debug("touch head checksum err"); ts_debug("touch_head %*ph", IRQ_EVENT_HEAD_LEN, pre_buf); - goodix_debugfs_printf("touch head checksum err\n"); + ts_event->retry = 1; return -EINVAL; } + event_status = pre_buf[0]; if (event_status & GOODIX_TOUCH_EVENT) return goodix_touch_handler(cd, ts_event, @@ -1003,6 +1218,96 @@ static int brl_after_event_handler(struct goodix_ts_core *cd) &sync_clean, 1); } +#define GOODIX_CMD_RAWDATA 2 +#define GOODIX_CMD_COORD 0 +static int brl_get_capacitance_data(struct goodix_ts_core *cd, + struct ts_rawdata_info *info) +{ + int ret; + int retry = 20; + struct goodix_ts_cmd temp_cmd; + u32 flag_addr = cd->ic_info.misc.touch_data_addr; + u32 raw_addr = cd->ic_info.misc.mutual_rawdata_addr; + u32 diff_addr = cd->ic_info.misc.mutual_diffdata_addr; + int tx = cd->ic_info.parm.drv_num; + int rx = cd->ic_info.parm.sen_num; + int size = tx * rx; + u8 val; + + if (!info) { + ts_err("input null ptr"); + return -EIO; + } + + /* disable irq & close esd */ + brl_irq_enbale(cd, false); + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + + /* switch rawdata mode */ + temp_cmd.cmd = GOODIX_CMD_RAWDATA; + temp_cmd.len = 4; + ret = brl_send_cmd(cd, &temp_cmd); + if (ret < 0) { + ts_err("switch rawdata mode failed, exit!"); + goto exit; + } + + /* clean touch event flag */ + val = 0; + ret = brl_write(cd, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit!"); + goto exit; + } + + while (retry--) { + usleep_range(5000, 5100); + ret = brl_read(cd, flag_addr, &val, 1); + if (!ret && (val & GOODIX_TOUCH_EVENT)) + break; + } + if (retry < 0) { + ts_err("rawdata is not ready val:0x%02x, exit!", val); + goto exit; + } + + /* obtain rawdata & diff_rawdata */ + info->buff[0] = rx; + info->buff[1] = tx; + info->used_size = 2; + + ret = brl_read(cd, raw_addr, (u8 *)&info->buff[info->used_size], + size * sizeof(s16)); + if (ret < 0) { + ts_err("obtian raw_data failed, exit!"); + goto exit; + } + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + + ret = brl_read(cd, diff_addr, (u8 *)&info->buff[info->used_size], + size * sizeof(s16)); + if (ret < 0) { + ts_err("obtian diff_data failed, exit!"); + goto exit; + } + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + +exit: + /* switch coor mode */ + temp_cmd.cmd = GOODIX_CMD_COORD; + temp_cmd.len = 4; + brl_send_cmd(cd, &temp_cmd); + /* clean touch event flag */ + val = 0; + brl_write(cd, flag_addr, &val, 1); + /* enable irq & esd */ + brl_irq_enbale(cd, true); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + return ret; +} + static struct goodix_ts_hw_ops brl_hw_ops = { .power_on = brl_power_on, .dev_confirm = brl_dev_confirm, @@ -1021,6 +1326,7 @@ static struct goodix_ts_hw_ops brl_hw_ops = { .esd_check = brl_esd_check, .event_handler = brl_event_handler, .after_event_handler = brl_after_event_handler, + .get_capacitance_data = brl_get_capacitance_data, }; struct goodix_ts_hw_ops *goodix_get_hw_ops(void) diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/goodix_brl_i2c.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_i2c.c old mode 100755 new mode 100644 similarity index 90% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/goodix_brl_i2c.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_i2c.c index a9fa620675fb..92c8ce307814 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/goodix_brl_i2c.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_i2c.c @@ -20,8 +20,8 @@ #include "goodix_ts_core.h" -#define TS_DRIVER_NAME "gtx8_i2c" -#define I2C_MAX_TRANSFER_SIZE 255 +#define TS_DRIVER_NAME "gtx8_i2c" +#define I2C_MAX_TRANSFER_SIZE 60 #define GOODIX_BUS_RETRY_TIMES 3 #define GOODIX_REG_ADDR_SIZE 4 @@ -174,6 +174,12 @@ static int goodix_i2c_probe(struct i2c_client *client, if (!ret) return -EIO; + /* get ic type */ + ret = goodix_get_ic_type(client->dev.of_node); + if (ret < 0) + return ret; + + goodix_i2c_bus.ic_type = ret; goodix_i2c_bus.bus_type = GOODIX_BUS_TYPE_I2C; goodix_i2c_bus.dev = &client->dev; goodix_i2c_bus.read = goodix_i2c_read; @@ -188,14 +194,14 @@ static int goodix_i2c_probe(struct i2c_client *client, goodix_pdev->num_resources = 0; /* * you can find this platform dev in - * /sys/devices/platfrom/goodix_ts.0 + * /sys/devices/platform/goodix_ts.0 * goodix_pdev->dev.parent = &client->dev; */ goodix_pdev->dev.platform_data = &goodix_i2c_bus; goodix_pdev->dev.release = goodix_pdev_release; /* register platform device, then the goodix_ts_core - * module will probe the touch deivce. + * module will probe the touch device. */ ret = platform_device_register(goodix_pdev); if (ret) { @@ -220,8 +226,9 @@ static int goodix_i2c_remove(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id i2c_matchs[] = { - {.compatible = "goodix,brl-generic",}, {.compatible = "goodix,gt9897",}, + {.compatible = "goodix,gt9966",}, + {.compatible = "goodix,gt9916",}, {}, }; MODULE_DEVICE_TABLE(of, i2c_matchs); @@ -244,20 +251,14 @@ static struct i2c_driver goodix_i2c_driver = { .id_table = i2c_id_table, }; -int goodix_bus_init(void) +int goodix_i2c_bus_init(void) { ts_info("Goodix i2c driver init"); return i2c_add_driver(&goodix_i2c_driver); } -EXPORT_SYMBOL(goodix_bus_init); -void goodix_bus_exit(void) +void goodix_i2c_bus_exit(void) { ts_info("Goodix i2c driver exit"); i2c_del_driver(&goodix_i2c_driver); } -EXPORT_SYMBOL(goodix_bus_exit); - -MODULE_DESCRIPTION("Goodix Touchscreen Hardware Module"); -MODULE_AUTHOR("Goodix, Inc."); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/goodix_brl_spi.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_spi.c old mode 100755 new mode 100644 similarity index 70% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/goodix_brl_spi.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_spi.c index 8f357a7256d8..934e83c23a49 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_bus/goodix_brl_spi.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_spi.c @@ -34,14 +34,14 @@ static struct platform_device *goodix_pdev; struct goodix_bus_interface goodix_spi_bus; /** - * goodix_spi_read- read device register through spi bus + * goodix_spi_read_bra- read device register through spi bus * @dev: pointer to device data * @addr: register address * @data: read buffer * @len: bytes to read * return: 0 - read ok, < 0 - spi transter error */ -int goodix_spi_read(struct device *dev, unsigned int addr, +static int goodix_spi_read_bra(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len) { struct spi_device *spi = to_spi_device(dev); @@ -54,7 +54,7 @@ int goodix_spi_read(struct device *dev, unsigned int addr, rx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL); tx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL); if (!rx_buf || !tx_buf) { - ts_err("alloc tx/rx_buf failed, size:%d\n", + ts_err("alloc tx/rx_buf failed, size:%d", SPI_READ_PREFIX_LEN + len); return -ENOMEM; } @@ -62,7 +62,7 @@ int goodix_spi_read(struct device *dev, unsigned int addr, spi_message_init(&spi_msg); memset(&xfers, 0, sizeof(xfers)); - /*spi_read tx_buf format: 0xF1 + addr(2bytes) + data*/ + /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ tx_buf[0] = SPI_READ_FLAG; tx_buf[1] = (addr >> 24) & 0xFF; tx_buf[2] = (addr >> 16) & 0xFF; @@ -80,7 +80,7 @@ int goodix_spi_read(struct device *dev, unsigned int addr, spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) { - ts_err("spi transfer error:%d\n",ret); + ts_err("spi transfer error:%d",ret); goto exit; } memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN], len); @@ -91,6 +91,55 @@ exit: return ret; } +static int goodix_spi_read(struct device *dev, unsigned int addr, + unsigned char *data, unsigned int len) +{ + struct spi_device *spi = to_spi_device(dev); + u8 *rx_buf = NULL; + u8 *tx_buf = NULL; + struct spi_transfer xfers; + struct spi_message spi_msg; + int ret = 0; + + rx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL); + tx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL); + if (!rx_buf || !tx_buf) { + ts_err("alloc tx/rx_buf failed, size:%d", + SPI_READ_PREFIX_LEN - 1 + len); + return -ENOMEM; + } + + spi_message_init(&spi_msg); + memset(&xfers, 0, sizeof(xfers)); + + /*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/ + tx_buf[0] = SPI_READ_FLAG; + tx_buf[1] = (addr >> 24) & 0xFF; + tx_buf[2] = (addr >> 16) & 0xFF; + tx_buf[3] = (addr >> 8) & 0xFF; + tx_buf[4] = addr & 0xFF; + tx_buf[5] = 0xFF; + tx_buf[6] = 0xFF; + tx_buf[7] = 0xFF; + + xfers.tx_buf = tx_buf; + xfers.rx_buf = rx_buf; + xfers.len = SPI_READ_PREFIX_LEN - 1 + len; + xfers.cs_change = 0; + spi_message_add_tail(&xfers, &spi_msg); + ret = spi_sync(spi, &spi_msg); + if (ret < 0) { + ts_err("spi transfer error:%d",ret); + goto exit; + } + memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN - 1], len); + +exit: + kfree(rx_buf); + kfree(tx_buf); + return ret; +} + /** * goodix_spi_write- write device register through spi bus * @dev: pointer to device data @@ -99,7 +148,7 @@ exit: * @len: bytes to write * return: 0 - write ok; < 0 - spi transter error. */ -int goodix_spi_write(struct device *dev, unsigned int addr, +static int goodix_spi_write(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len) { struct spi_device *spi = to_spi_device(dev); @@ -110,7 +159,7 @@ int goodix_spi_write(struct device *dev, unsigned int addr, tx_buf = kzalloc(SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL); if (!tx_buf) { - ts_err("alloc tx_buf failed, size:%d\n", + ts_err("alloc tx_buf failed, size:%d", SPI_WRITE_PREFIX_LEN + len); return -ENOMEM; } @@ -130,7 +179,7 @@ int goodix_spi_write(struct device *dev, unsigned int addr, spi_message_add_tail(&xfers, &spi_msg); ret = spi_sync(spi, &spi_msg); if (ret < 0) - ts_err("spi transfer error:%d\n",ret); + ts_err("spi transfer error:%d",ret); kfree(tx_buf); return ret; @@ -151,7 +200,6 @@ static int goodix_spi_probe(struct spi_device *spi) /* init spi_device */ spi->mode = SPI_MODE_0; spi->bits_per_word = 8; - spi->max_speed_hz = 1000000; ret = spi_setup(spi); if (ret) { @@ -159,9 +207,18 @@ static int goodix_spi_probe(struct spi_device *spi) return ret; } + /* get ic type */ + ret = goodix_get_ic_type(spi->dev.of_node); + if (ret < 0) + return ret; + + goodix_spi_bus.ic_type = ret; goodix_spi_bus.bus_type = GOODIX_BUS_TYPE_SPI; goodix_spi_bus.dev = &spi->dev; - goodix_spi_bus.read = goodix_spi_read; + if (goodix_spi_bus.ic_type == IC_TYPE_BERLIN_A) + goodix_spi_bus.read = goodix_spi_read_bra; + else + goodix_spi_bus.read = goodix_spi_read; goodix_spi_bus.write = goodix_spi_write; /* ts core device */ goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); @@ -207,6 +264,8 @@ static int goodix_spi_remove(struct spi_device *spi) static const struct of_device_id spi_matchs[] = { {.compatible = "goodix,gt9897S",}, {.compatible = "goodix,gt9897T",}, + {.compatible = "goodix,gt9966S",}, + {.compatible = "goodix,gt9916S",}, {}, }; #endif @@ -227,20 +286,14 @@ static struct spi_driver goodix_spi_driver = { .remove = goodix_spi_remove, }; -int goodix_bus_init(void) +int goodix_spi_bus_init(void) { ts_info("Goodix spi driver init"); return spi_register_driver(&goodix_spi_driver); } -EXPORT_SYMBOL(goodix_bus_init); -void goodix_bus_exit(void) +void goodix_spi_bus_exit(void) { ts_info("Goodix spi driver exit"); spi_unregister_driver(&goodix_spi_driver); } -EXPORT_SYMBOL(goodix_bus_exit); - -MODULE_DESCRIPTION("Goodix Touchscreen Hardware Module"); -MODULE_AUTHOR("Goodix, Inc."); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_cfg_bin.c old mode 100755 new mode 100644 similarity index 84% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_cfg_bin.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_cfg_bin.c index 6c58ee86e1d3..c69f6c67bd86 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_cfg_bin.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_cfg_bin.c @@ -16,17 +16,16 @@ */ #include "goodix_ts_core.h" -#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" -#define TS_BIN_VERSION_START_INDEX 5 -#define TS_BIN_VERSION_LEN 4 +#define TS_BIN_VERSION_START_INDEX 5 +#define TS_BIN_VERSION_LEN 4 #define TS_CFG_BIN_HEAD_RESERVED_LEN 6 -#define TS_CFG_OFFSET_LEN 2 -#define TS_IC_TYPE_NAME_MAX_LEN 15 -#define TS_CFG_BIN_HEAD_LEN (sizeof(struct goodix_cfg_bin_head) + \ - TS_CFG_BIN_HEAD_RESERVED_LEN) -#define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info)) -#define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info)) -#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN) +#define TS_CFG_OFFSET_LEN 2 +#define TS_IC_TYPE_NAME_MAX_LEN 15 +#define TS_CFG_BIN_HEAD_LEN (sizeof(struct goodix_cfg_bin_head) + \ + TS_CFG_BIN_HEAD_RESERVED_LEN) +#define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info)) +#define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info)) +#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN) /*cfg block definitin*/ #define TS_CFG_BLOCK_PID_LEN 8 @@ -35,8 +34,8 @@ #define TS_CFG_BLOCK_FW_PATCH_LEN 4 #define TS_CFG_BLOCK_RESERVED_LEN 9 -#define TS_NORMAL_CFG 0x01 -#define TS_HIGH_SENSE_CFG 0x03 +#define TS_NORMAL_CFG 0x01 +#define TS_HIGH_SENSE_CFG 0x03 #define TS_RQST_FW_RETRY_TIMES 2 #pragma pack(1) @@ -101,20 +100,24 @@ struct goodix_cfg_bin { struct goodix_cfg_package *cfg_pkgs; }; -static int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bin) +static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name, + struct goodix_cfg_bin *cfg_bin) { const struct firmware *firmware = NULL; - char cfg_bin_name[GOODIX_MAX_STR_LABLE_LEN] = {0x00}; int ret; + int retry = GOODIX_RETRY_3; - /*get cfg_bin_name*/ - strlcpy(cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(cfg_bin_name)); - ts_info("cfg_bin_name:%s", cfg_bin_name); + ts_info("cfg_bin_name:%s", cfg_name); - ret = request_firmware(&firmware, cfg_bin_name, dev); - if (ret) { - ts_err("failed get cfg bin[%s] error:%d", - cfg_bin_name, ret); + while (retry--) { + ret = request_firmware(&firmware, cfg_name, dev); + if (!ret) + break; + ts_info("get cfg bin retry:[%d]", GOODIX_RETRY_3 - retry); + msleep(200); + } + if (retry < 0) { + ts_err("failed get cfg bin[%s] error:%d", cfg_name, ret); return ret; } @@ -126,7 +129,7 @@ static int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bi } cfg_bin->bin_data_len = firmware->size; - /*allocate memory for cfg_bin->bin_data*/ + /* allocate memory for cfg_bin->bin_data */ cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL); if (!cfg_bin->bin_data) { ret = -ENOMEM; @@ -209,8 +212,6 @@ static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) memcpy(&cfg_bin->cfg_pkgs[i].reg_info, &cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN], TS_PKG_REG_INFO_LEN); - /*TODO compatible little edition and big edition*/ - //goodix_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]); /*get configuration data*/ cfg_bin->cfg_pkgs[i].cfg = &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN]; @@ -290,10 +291,11 @@ err_out: static int goodix_get_config_data(struct goodix_ts_core *cd, u8 sensor_id) { struct goodix_cfg_bin cfg_bin = {0}; + char *cfg_name = cd->board_data.cfg_bin_name; int ret; /*get cfg_bin from file system*/ - ret = goodix_read_cfg_bin(&cd->pdev->dev, &cfg_bin); + ret = goodix_read_cfg_bin(&cd->pdev->dev, cfg_name, &cfg_bin); if (ret) { ts_err("failed get valid config bin data"); return ret; @@ -328,3 +330,5 @@ int goodix_get_config_proc(struct goodix_ts_core *cd) return goodix_get_config_data(cd, cd->fw_version.sensor_id); } + + diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_ts_core.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.c old mode 100755 new mode 100644 similarity index 69% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_ts_core.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.c index 749d2e9a1fa9..796514a53673 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_ts_core.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.c @@ -16,7 +16,8 @@ */ #include #include -#include +#include +#include #include #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) @@ -26,14 +27,10 @@ #include "goodix_ts_core.h" -#define GOOIDX_INPUT_PHYS "goodix_ts/input0" +#define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" +#define GOOIDX_INPUT_PHYS "goodix_ts/input0" struct goodix_module goodix_modules; - -#define CORE_MODULE_UNPROBED 0 -#define CORE_MODULE_PROB_SUCCESS 1 -#define CORE_MODULE_PROB_FAILED -1 -#define CORE_MODULE_REMOVED -2 int core_module_prob_sate = CORE_MODULE_UNPROBED; static int goodix_send_ic_config(struct goodix_ts_core *cd, int type); @@ -47,7 +44,6 @@ static int __do_register_ext_module(struct goodix_ext_module *module) struct goodix_ext_module *ext_module, *next; struct list_head *insert_point = &goodix_modules.head; - ts_info("__do_register_ext_module IN"); /* prority level *must* be set */ if (module->priority == EXTMOD_PRIO_RESERVED) { ts_err("Priority of module [%s] needs to be set", @@ -100,20 +96,6 @@ static void goodix_register_ext_module_work(struct work_struct *work) { container_of(work, struct goodix_ext_module, work); ts_info("module register work IN"); - if (core_module_prob_sate == CORE_MODULE_PROB_FAILED - || core_module_prob_sate == CORE_MODULE_REMOVED) { - ts_err("core layer state error %d", core_module_prob_sate); - return; - } - - if (core_module_prob_sate == CORE_MODULE_UNPROBED) { - /* waitting for core layer */ - if (!wait_for_completion_timeout(&goodix_modules.core_comp, - 25 * HZ)) { - ts_err("Module [%s] timeout", module->name); - return; - } - } /* driver probe failed */ if (core_module_prob_sate != CORE_MODULE_PROB_SUCCESS) { @@ -134,7 +116,6 @@ static void goodix_core_module_init(void) goodix_modules.initilized = true; INIT_LIST_HEAD(&goodix_modules.head); mutex_init(&goodix_modules.mutex); - init_completion(&goodix_modules.core_comp); } /** @@ -160,7 +141,6 @@ int goodix_register_ext_module(struct goodix_ext_module *module) ts_info("goodix_register_ext_module OUT"); return 0; } -EXPORT_SYMBOL_GPL(goodix_register_ext_module); /** * goodix_register_ext_module_no_wait @@ -179,45 +159,6 @@ int goodix_register_ext_module_no_wait(struct goodix_ext_module *module) } return __do_register_ext_module(module); } -EXPORT_SYMBOL_GPL(goodix_register_ext_module_no_wait); - -/* remove all registered ext module - * return 0 on success, otherwise return < 0 - */ -int goodix_unregister_all_module(void) -{ - int ret = 0; - struct goodix_ext_module *ext_module, *next; - - if (!goodix_modules.initilized) - return 0; - - if (!goodix_modules.core_data) - return 0; - - mutex_lock(&goodix_modules.mutex); - if (list_empty(&goodix_modules.head)) { - mutex_unlock(&goodix_modules.mutex); - return 0; - } - - list_for_each_entry_safe(ext_module, next, - &goodix_modules.head, list) { - if (ext_module->funcs && ext_module->funcs->exit) { - ret = ext_module->funcs->exit(goodix_modules.core_data, - ext_module); - if (ret) { - ts_err("failed register ext module, %d:%s", - ret, ext_module->name); - break; - } - } - ts_info("remove module: %s", ext_module->name); - list_del(&ext_module->list); - } - mutex_unlock(&goodix_modules.mutex); - return ret; -} /** * goodix_unregister_ext_module - interface for external module @@ -271,7 +212,6 @@ int goodix_unregister_ext_module(struct goodix_ext_module *module) module->name ? module->name : " "); return 0; } -EXPORT_SYMBOL_GPL(goodix_unregister_ext_module); static void goodix_ext_sysfs_release(struct kobject *kobj) { @@ -321,7 +261,6 @@ struct kobj_type *goodix_get_default_ktype(void) { return &goodix_ext_ktype; } -EXPORT_SYMBOL_GPL(goodix_get_default_ktype); struct kobject *goodix_get_default_kobj(void) { @@ -332,7 +271,6 @@ struct kobject *goodix_get_default_kobj(void) kobj = &goodix_modules.core_data->pdev->dev.kobj; return kobj; } -EXPORT_SYMBOL_GPL(goodix_get_default_kobj); /* show driver infomation */ static ssize_t goodix_ts_driver_info_show(struct device *dev, @@ -349,14 +287,17 @@ static ssize_t goodix_ts_chip_info_show(struct device *dev, struct goodix_ts_core *core_data = dev_get_drvdata(dev); struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; struct goodix_fw_version chip_ver; - int ret, cnt = -EINVAL; + u8 temp_pid[8] = {0}; + int ret; + int cnt = -EINVAL; if (hw_ops->read_version) { ret = hw_ops->read_version(core_data, &chip_ver); if (!ret) { - cnt = snprintf(&buf[cnt], PAGE_SIZE, + memcpy(temp_pid, chip_ver.rom_pid, sizeof(chip_ver.rom_pid)); + cnt = snprintf(&buf[0], PAGE_SIZE, "rom_pid:%s\nrom_vid:%02x%02x%02x\n", - chip_ver.rom_pid, chip_ver.rom_vid[0], + temp_pid, chip_ver.rom_vid[0], chip_ver.rom_vid[1], chip_ver.rom_vid[2]); cnt += snprintf(&buf[cnt], PAGE_SIZE, "patch_pid:%s\npatch_vid:%02x%02x%02x%02x\n", @@ -368,6 +309,16 @@ static ssize_t goodix_ts_chip_info_show(struct device *dev, } } + if (hw_ops->get_ic_info) { + ret = hw_ops->get_ic_info(core_data, &core_data->ic_info); + if (!ret) { + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "config_id:%x\n", core_data->ic_info.version.config_id); + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "config_version:%x\n", core_data->ic_info.version.config_version); + } + } + return cnt; } @@ -387,6 +338,284 @@ static ssize_t goodix_ts_reset_store(struct device *dev, return count; } +/* read config */ +static ssize_t goodix_ts_read_cfg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + int ret; + int i; + int offset; + char *cfg_buf = NULL; + + cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!cfg_buf) + return -ENOMEM; + + if (hw_ops->read_config) + ret = hw_ops->read_config(core_data, cfg_buf, PAGE_SIZE); + else + ret = -EINVAL; + + if (ret > 0) { + offset = 0; + for (i = 0; i < 200; i++) { // only print 200 bytes + offset += snprintf(&buf[offset], PAGE_SIZE - offset, + "%02x,", cfg_buf[i]); + if ((i + 1) % 20 == 0) + buf[offset++] = '\n'; + } + } + + kfree(cfg_buf); + if (ret <= 0) + return ret; + + return offset; +} + +static u8 ascii2hex(u8 a) +{ + s8 value = 0; + + if (a >= '0' && a <= '9') + value = a - '0'; + else if (a >= 'A' && a <= 'F') + value = a - 'A' + 0x0A; + else if (a >= 'a' && a <= 'f') + value = a - 'a' + 0x0A; + else + value = 0xff; + + return value; +} + +static int goodix_ts_convert_0x_data(const u8 *buf, int buf_size, + u8 *out_buf, int *out_buf_len) +{ + int i, m_size = 0; + int temp_index = 0; + u8 high, low; + + for (i = 0; i < buf_size; i++) { + if (buf[i] == 'x' || buf[i] == 'X') + m_size++; + } + + if (m_size <= 1) { + ts_err("cfg file ERROR, valid data count:%d", m_size); + return -EINVAL; + } + *out_buf_len = m_size; + + for (i = 0; i < buf_size; i++) { + if (buf[i] != 'x' && buf[i] != 'X') + continue; + + if (temp_index >= m_size) { + ts_err("exchange cfg data error, overflow," + "temp_index:%d,m_size:%d", + temp_index, m_size); + return -EINVAL; + } + high = ascii2hex(buf[i + 1]); + low = ascii2hex(buf[i + 2]); + if (high == 0xff || low == 0xff) { + ts_err("failed convert: 0x%x, 0x%x", + buf[i + 1], buf[i + 2]); + return -EINVAL; + } + out_buf[temp_index++] = (high << 4) + low; + } + return 0; +} + +/* send config */ +static ssize_t goodix_ts_send_cfg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + struct goodix_ic_config *config = NULL; + const struct firmware *cfg_img = NULL; + int en; + int ret; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + hw_ops->irq_enable(core_data, false); + + ret = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev); + if (ret < 0) { + ts_err("cfg file [%s] not available,errno:%d", + GOODIX_DEFAULT_CFG_NAME, ret); + goto exit; + } else { + ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME); + } + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + goto exit; + + if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size, + config->data, &config->len)) { + ts_err("convert config data FAILED"); + goto exit; + } + + if (hw_ops->send_config) { + ret = hw_ops->send_config(core_data, config->data, config->len); + if (ret < 0) + ts_err("send config failed"); + } + +exit: + hw_ops->irq_enable(core_data, true); + kfree(config); + if (cfg_img) + release_firmware(cfg_img); + + return count; +} + +/* reg read/write */ +static u32 rw_addr; +static u32 rw_len; +static u8 rw_flag; +static u8 store_buf[32]; +static u8 show_buf[PAGE_SIZE]; +static ssize_t goodix_ts_reg_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + int ret; + + if (!rw_addr || !rw_len) { + ts_err("address(0x%x) and length(%d) can't be null", + rw_addr, rw_len); + return -EINVAL; + } + + if (rw_flag != 1) { + ts_err("invalid rw flag %d, only support [1/2]", rw_flag); + return -EINVAL; + } + + ret = hw_ops->read(core_data, rw_addr, show_buf, rw_len); + if (ret < 0) { + ts_err("failed read addr(%x) length(%d)", rw_addr, rw_len); + return snprintf(buf, PAGE_SIZE, "failed read addr(%x), len(%d)\n", + rw_addr, rw_len); + } + + return snprintf(buf, PAGE_SIZE, "0x%x,%d {%*ph}\n", + rw_addr, rw_len, rw_len, show_buf); +} + +static ssize_t goodix_ts_reg_rw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + char *pos = NULL; + char *token = NULL; + long result = 0; + int ret; + int i; + + if (!buf || !count) { + ts_err("invalid parame"); + goto err_out; + } + + if (buf[0] == 'r') { + rw_flag = 1; + } else if (buf[0] == 'w') { + rw_flag = 2; + } else { + ts_err("string must start with 'r/w'"); + goto err_out; + } + + /* get addr */ + pos = (char *)buf; + pos += 2; + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid address info"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get addr info"); + goto err_out; + } + rw_addr = (u32)result; + ts_info("rw addr is 0x%x", rw_addr); + } + + /* get length */ + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid length info"); + goto err_out; + } else { + if (kstrtol(token, 0, &result)) { + ts_err("failed get length info"); + goto err_out; + } + rw_len = (u32)result; + ts_info("rw length info is %d", rw_len); + if (rw_len > sizeof(store_buf)) { + ts_err("data len > %lu", sizeof(store_buf)); + goto err_out; + } + } + + if (rw_flag == 1) + return count; + + for (i = 0; i < rw_len; i++) { + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid data info"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get data[%d] info", i); + goto err_out; + } + store_buf[i] = (u8)result; + ts_info("get data[%d]=0x%x", i, store_buf[i]); + } + } + ret = hw_ops->write(core_data, rw_addr, store_buf, rw_len); + if (ret < 0) { + ts_err("failed write addr(%x) data %*ph", rw_addr, + rw_len, store_buf); + goto err_out; + } + + ts_info("%s write to addr (%x) with data %*ph", + "success", rw_addr, rw_len, store_buf); + + return count; +err_out: + snprintf(show_buf, PAGE_SIZE, "%s\n", + "invalid params, format{r/w:4100:length:[41:21:31]}"); + return -EINVAL; + +} + /* show irq infomation */ static ssize_t goodix_ts_irq_info_show(struct device *dev, struct device_attribute *attr, @@ -423,7 +652,7 @@ static ssize_t goodix_ts_irq_info_show(struct device *dev, offset += r; r = snprintf(&buf[offset], PAGE_SIZE - offset, - "echo 0/1 > irq_info to disable/enable irq"); + "echo 0/1 > irq_info to disable/enable irq\n"); if (r < 0) return -EINVAL; @@ -449,17 +678,86 @@ static ssize_t goodix_ts_irq_info_store(struct device *dev, return count; } -static DEVICE_ATTR(driver_info, S_IRUGO, goodix_ts_driver_info_show, NULL); -static DEVICE_ATTR(chip_info, S_IRUGO, goodix_ts_chip_info_show, NULL); -static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, goodix_ts_reset_store); -static DEVICE_ATTR(irq_info, S_IRUGO | S_IWUSR | S_IWGRP, - goodix_ts_irq_info_show, goodix_ts_irq_info_store); +/* show esd status */ +static ssize_t goodix_ts_esd_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + int r = 0; + + r = snprintf(buf, PAGE_SIZE, "state:%s\n", + atomic_read(&ts_esd->esd_on) ? + "enabled" : "disabled"); + + return r; +} + +/* enable/disable esd */ +static ssize_t goodix_ts_esd_info_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + else + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + return count; +} + +/* debug level show */ +static ssize_t goodix_ts_debug_log_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int r = 0; + + r = snprintf(buf, PAGE_SIZE, "state:%s\n", + debug_log_flag ? + "enabled" : "disabled"); + + return r; +} + +/* debug level store */ +static ssize_t goodix_ts_debug_log_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + debug_log_flag = true; + else + debug_log_flag = false; + return count; +} + +static DEVICE_ATTR(driver_info, 0444, goodix_ts_driver_info_show, NULL); +static DEVICE_ATTR(chip_info, 0444, goodix_ts_chip_info_show, NULL); +static DEVICE_ATTR(reset, 0220, NULL, goodix_ts_reset_store); +static DEVICE_ATTR(send_cfg, 0220, NULL, goodix_ts_send_cfg_store); +static DEVICE_ATTR(read_cfg, 0444, goodix_ts_read_cfg_show, NULL); +static DEVICE_ATTR(reg_rw, 0664, goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); +static DEVICE_ATTR(irq_info, 0664, goodix_ts_irq_info_show, goodix_ts_irq_info_store); +static DEVICE_ATTR(esd_info, 0664, goodix_ts_esd_info_show, goodix_ts_esd_info_store); +static DEVICE_ATTR(debug_log, 0664, goodix_ts_debug_log_show, goodix_ts_debug_log_store); static struct attribute *sysfs_attrs[] = { &dev_attr_driver_info.attr, &dev_attr_chip_info.attr, &dev_attr_reset.attr, + &dev_attr_send_cfg.attr, + &dev_attr_read_cfg.attr, + &dev_attr_reg_rw.attr, &dev_attr_irq_info.attr, + &dev_attr_esd_info.attr, + &dev_attr_debug_log.attr, NULL, }; @@ -485,195 +783,92 @@ static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data) sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group); } -#ifdef CONFIG_DEBUG_FS -#define MAX_DEBUGFS_BUF_SIZE 4096 -static struct goodix_debug_fs { - wait_queue_head_t log_wait; - - struct dentry *rootdir; - char buf[MAX_DEBUGFS_BUF_SIZE]; - char *r_pos; - char *w_pos; - char *start_pos; - char *end_pos; - struct mutex mutex; -} goodix_dbgfs; - -#define MAX_LINE_SIZE 120 -void goodix_debugfs_printf(const char *fmt, ...) -{ - char tmp_buf[MAX_LINE_SIZE] = {0}; - char *buf_ptr; - va_list args; - int need_copy, copy_size; - - if (!goodix_dbgfs.rootdir) - return; - - va_start(args, fmt); - need_copy = vscnprintf(tmp_buf, - MAX_LINE_SIZE, fmt, args); - va_end(args); - - mutex_lock(&goodix_dbgfs.mutex); - buf_ptr = &tmp_buf[0]; - if (goodix_dbgfs.r_pos <= goodix_dbgfs.w_pos) { - if (need_copy <= goodix_dbgfs.end_pos - goodix_dbgfs.w_pos) { - memcpy(goodix_dbgfs.w_pos, buf_ptr, need_copy); - goodix_dbgfs.w_pos += need_copy; - if (goodix_dbgfs.w_pos >= goodix_dbgfs.end_pos) { - goodix_dbgfs.w_pos = goodix_dbgfs.start_pos; - /* modify r_pos */ - if (goodix_dbgfs.w_pos >= goodix_dbgfs.r_pos) - goodix_dbgfs.r_pos = - goodix_dbgfs.w_pos + 1; - } - } else { - /* roll back */ - copy_size = goodix_dbgfs.end_pos - goodix_dbgfs.w_pos; - memcpy(goodix_dbgfs.w_pos, buf_ptr, copy_size); - goodix_dbgfs.w_pos = goodix_dbgfs.start_pos; - buf_ptr += copy_size; - need_copy -= copy_size; - memcpy(goodix_dbgfs.w_pos, buf_ptr, need_copy); - goodix_dbgfs.w_pos += need_copy; - - /* modify r_pos */ - if (goodix_dbgfs.w_pos >= goodix_dbgfs.r_pos) - goodix_dbgfs.r_pos = goodix_dbgfs.w_pos + 1; - } - } else { - if (need_copy < goodix_dbgfs.end_pos - goodix_dbgfs.w_pos) { - memcpy(goodix_dbgfs.w_pos, buf_ptr, need_copy); - goodix_dbgfs.w_pos += need_copy; - if (goodix_dbgfs.w_pos >= goodix_dbgfs.r_pos) - goodix_dbgfs.r_pos = goodix_dbgfs.w_pos + 1; - } else { - /* roll back */ - copy_size = goodix_dbgfs.end_pos - goodix_dbgfs.w_pos; - memcpy(goodix_dbgfs.w_pos, buf_ptr, copy_size); - goodix_dbgfs.w_pos = goodix_dbgfs.start_pos; - goodix_dbgfs.r_pos = goodix_dbgfs.w_pos + 1; - buf_ptr += copy_size; - need_copy -= copy_size; - memcpy(goodix_dbgfs.w_pos, buf_ptr, need_copy); - goodix_dbgfs.w_pos += need_copy; - /* modify r_pos */ - if (goodix_dbgfs.w_pos >= goodix_dbgfs.r_pos) - goodix_dbgfs.r_pos = goodix_dbgfs.w_pos + 1; - } - } - - /* r_pos overflow */ - if (goodix_dbgfs.r_pos >= goodix_dbgfs.end_pos) - goodix_dbgfs.r_pos = goodix_dbgfs.start_pos; - mutex_unlock(&goodix_dbgfs.mutex); - wake_up_interruptible(&goodix_dbgfs.log_wait); -} -EXPORT_SYMBOL(goodix_debugfs_printf); - -static ssize_t goodix_debugfs_read(struct file *file, - char __user *buf, size_t size, loff_t *ppos) +/* prosfs create */ +static int rawdata_proc_show(struct seq_file *m, void *v) { + struct ts_rawdata_info *info; + struct goodix_ts_core *cd = m->private; + int tx; + int rx; int ret; - ssize_t valied_data = 0; - ssize_t copy_data = 0; + int i; + int index; - ret = wait_event_interruptible(goodix_dbgfs.log_wait, - goodix_dbgfs.r_pos != goodix_dbgfs.w_pos); - if (ret) - return -EAGAIN; - - mutex_lock(&goodix_dbgfs.mutex); - - if (goodix_dbgfs.r_pos < goodix_dbgfs.w_pos) { - valied_data = goodix_dbgfs.w_pos - goodix_dbgfs.r_pos; - copy_data = valied_data < size ? valied_data : size; - ret = copy_to_user(buf, goodix_dbgfs.r_pos, copy_data); - copy_data -= ret; - goodix_dbgfs.r_pos += copy_data; - goto out; + if (!m || !v || !cd) { + ts_err("rawdata_proc_show, input null ptr"); + return -EIO; } - /* to simplify code design, when divie read operation into two syscall */ - valied_data = goodix_dbgfs.end_pos - goodix_dbgfs.r_pos; - copy_data = valied_data < size ? valied_data : size; - ret = copy_to_user(buf, goodix_dbgfs.r_pos, copy_data); - copy_data -= ret; - goodix_dbgfs.r_pos += copy_data; - if (goodix_dbgfs.r_pos >= goodix_dbgfs.end_pos) - goodix_dbgfs.r_pos = goodix_dbgfs.start_pos; + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("Failed to alloc rawdata info memory"); + return -ENOMEM; + } -out: - mutex_unlock(&goodix_dbgfs.mutex); - return copy_data; + ret = cd->hw_ops->get_capacitance_data(cd, info); + if (ret < 0) { + ts_err("failed to get_capacitance_data, exit!"); + goto exit; + } + + rx = info->buff[0]; + tx = info->buff[1]; + seq_printf(m, "TX:%d RX:%d\n", tx, rx); + seq_printf(m, "mutual_rawdata:\n"); + index = 2; + for (i = 0; i < tx * rx; i++) { + seq_printf(m, "%5d,", info->buff[index + i]); + if ((i + 1) % tx == 0) + seq_printf(m, "\n"); + } + seq_printf(m, "mutual_diffdata:\n"); + index += tx * rx; + for (i = 0; i < tx * rx; i++) { + seq_printf(m, "%3d,", info->buff[index + i]); + if ((i + 1) % tx == 0) + seq_printf(m, "\n"); + } + +exit: + kfree(info); + return ret; } -static int goodix_debugfs_open(struct inode *inode, struct file *file) +static int rawdata_proc_open(struct inode *inode, struct file *file) { - file->private_data = inode->i_private; - return 0; + return single_open_size(file, rawdata_proc_show, PDE_DATA(inode), PAGE_SIZE * 10); } -static int goodix_debugfs_release(struct inode *inode, struct file *file) -{ - return 0; -} - -static const struct file_operations goodix_debugfs_ops = { - .owner = THIS_MODULE, - .open = goodix_debugfs_open, - .read = goodix_debugfs_read, - .release = goodix_debugfs_release, +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops rawdata_proc_fops = { + .proc_open = rawdata_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, }; - -static int goodix_debugfs_init(struct goodix_ts_core *cd) -{ - struct dentry *rootdir; - struct dentry *retval; - - rootdir = debugfs_create_dir("goodix_ts", NULL); - if (!rootdir) { - ts_err("failed create goodix debugfs dir"); - return -EINVAL; - } - retval = debugfs_create_file("event", S_IRUGO | S_IWUSR, rootdir, - cd, &goodix_debugfs_ops); - if (IS_ERR_OR_NULL(retval)) { - debugfs_remove_recursive(rootdir); - return -EINVAL; - } - init_waitqueue_head(&goodix_dbgfs.log_wait); - goodix_dbgfs.r_pos = goodix_dbgfs.buf; - goodix_dbgfs.w_pos = goodix_dbgfs.buf; - goodix_dbgfs.start_pos = goodix_dbgfs.buf; - goodix_dbgfs.end_pos = goodix_dbgfs.buf + MAX_DEBUGFS_BUF_SIZE; - goodix_dbgfs.rootdir = rootdir; - mutex_init(&goodix_dbgfs.mutex); - - return 0; -} - -static void goodix_debugfs_remove(struct goodix_ts_core *cd) -{ - if (!goodix_dbgfs.rootdir) - return; - debugfs_remove_recursive(goodix_dbgfs.rootdir); -} #else -static int goodix_debugfs_init(struct goodix_ts_core *cd) -{ - return -EINVAL; -} -static void goodix_debugfs_remove(struct goodix_ts_core *cd) -{ -} -void goodix_debugfs_printf(const char *fmt, ...) -{ - return; -} -EXPORT_SYMBOL(goodix_debugfs_printf); +static const struct file_operations rawdata_proc_fops = { + .open = rawdata_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; #endif + +static void goodix_ts_procfs_init(struct goodix_ts_core *core_data) +{ + if (!proc_mkdir("goodix_ts", NULL)) + return; + proc_create_data("goodix_ts/tp_capacitance_data", + 0666, NULL, &rawdata_proc_fops, core_data); +} + +static void goodix_ts_procfs_exit(struct goodix_ts_core *core_data) +{ + remove_proc_entry("goodix_ts/tp_capacitance_data", NULL); + remove_proc_entry("goodix_ts", NULL); +} + /* event notifier */ static BLOCKING_NOTIFIER_HEAD(ts_notifier_list); /** @@ -685,7 +880,6 @@ int goodix_ts_register_notifier(struct notifier_block *nb) { return blocking_notifier_chain_register(&ts_notifier_list, nb); } -EXPORT_SYMBOL(goodix_ts_register_notifier); /** * goodix_ts_unregister_client - unregister a client notifier @@ -696,7 +890,6 @@ int goodix_ts_unregister_notifier(struct notifier_block *nb) { return blocking_notifier_chain_unregister(&ts_notifier_list, nb); } -EXPORT_SYMBOL(goodix_ts_unregister_notifier); /** * fb_notifier_call_chain - notify clients of fb_events @@ -710,7 +903,6 @@ int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v) (unsigned long)evt, v); return ret; } -EXPORT_SYMBOL_GPL(goodix_ts_blocking_notify); #ifdef CONFIG_OF /** @@ -772,6 +964,24 @@ static int goodix_parse_dt(struct device_node *node, return -EINVAL; } + r = of_get_named_gpio(node, "goodix,avdd-gpio", 0); + if (r < 0) { + ts_info("can't find avdd-gpio, use other power supply"); + board_data->avdd_gpio = 0; + } else { + ts_info("get avdd-gpio[%d] from dt", r); + board_data->avdd_gpio = r; + } + + r = of_get_named_gpio(node, "goodix,iovdd-gpio", 0); + if (r < 0) { + ts_info("can't find iovdd-gpio, use other power supply"); + board_data->iovdd_gpio = 0; + } else { + ts_info("get iovdd-gpio[%d] from dt", r); + board_data->iovdd_gpio = r; + } + r = of_get_named_gpio(node, "goodix,reset-gpio", 0); if (r < 0) { ts_err("invalid reset-gpio in dt: %d", r); @@ -798,7 +1008,7 @@ static int goodix_parse_dt(struct device_node *node, memset(board_data->avdd_name, 0, sizeof(board_data->avdd_name)); r = of_property_read_string(node, "goodix,avdd-name", &name_tmp); if (!r) { - ts_info("avdd name form dt: %s", name_tmp); + ts_info("avdd name from dt: %s", name_tmp); if (strlen(name_tmp) < sizeof(board_data->avdd_name)) strncpy(board_data->avdd_name, name_tmp, sizeof(board_data->avdd_name)); @@ -811,7 +1021,7 @@ static int goodix_parse_dt(struct device_node *node, memset(board_data->iovdd_name, 0, sizeof(board_data->iovdd_name)); r = of_property_read_string(node, "goodix,iovdd-name", &name_tmp); if (!r) { - ts_info("iovdd name form dt: %s", name_tmp); + ts_info("iovdd name from dt: %s", name_tmp); if (strlen(name_tmp) < sizeof(board_data->iovdd_name)) strncpy(board_data->iovdd_name, name_tmp, sizeof(board_data->iovdd_name)); @@ -821,6 +1031,26 @@ static int goodix_parse_dt(struct device_node *node, sizeof(board_data->iovdd_name)); } + /* get firmware file name */ + r = of_property_read_string(node, "goodix,firmware-name", &name_tmp); + if (!r) { + ts_info("firmware name from dt: %s", name_tmp); + strncpy(board_data->fw_name, name_tmp, sizeof(board_data->fw_name)); + } else { + ts_info("can't find firmware name, use default: %s", TS_DEFAULT_FIRMWARE); + strncpy(board_data->fw_name, TS_DEFAULT_FIRMWARE, sizeof(board_data->fw_name)); + } + + /* get config file name */ + r = of_property_read_string(node, "goodix,config-name", &name_tmp); + if (!r) { + ts_info("config name from dt: %s", name_tmp); + strncpy(board_data->cfg_bin_name, name_tmp, sizeof(board_data->cfg_bin_name)); + } else { + ts_info("can't find config name, use default: %s", TS_DEFAULT_CFG_BIN); + strncpy(board_data->cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(board_data->cfg_bin_name)); + } + /* get xyz resolutions */ r = goodix_parse_dt_resolution(node, board_data); if (r) { @@ -847,6 +1077,8 @@ static void goodix_ts_report_pen(struct input_dev *dev, { int i; + mutex_lock(&dev->mutex); + if (pen_data->coords.status == TS_TOUCH) { input_report_key(dev, BTN_TOUCH, 1); input_report_key(dev, pen_data->coords.tool_type, 1); @@ -858,6 +1090,12 @@ static void goodix_ts_report_pen(struct input_dev *dev, input_report_abs(dev, ABS_X, pen_data->coords.x); input_report_abs(dev, ABS_Y, pen_data->coords.y); input_report_abs(dev, ABS_PRESSURE, pen_data->coords.p); + input_report_abs(dev, ABS_TILT_X, pen_data->coords.tilt_x); + input_report_abs(dev, ABS_TILT_Y, pen_data->coords.tilt_y); + ts_debug("pen_data:x %d, y %d, p%d, tilt_x %d tilt_y %d", + pen_data->coords.x, pen_data->coords.y, + pen_data->coords.p, pen_data->coords.tilt_x, + pen_data->coords.tilt_x); } /* report pen button */ for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { @@ -869,45 +1107,40 @@ static void goodix_ts_report_pen(struct input_dev *dev, input_report_key(dev, pen_data->keys[i].code, 0); } input_sync(dev); + mutex_unlock(&dev->mutex); } static void goodix_ts_report_finger(struct input_dev *dev, struct goodix_touch_data *touch_data) { unsigned int touch_num = touch_data->touch_num; - static u32 pre_fin; int i; - /*first touch down and last touch up condition*/ - if (touch_num && !pre_fin) - input_report_key(dev, BTN_TOUCH, 1); - else if (!touch_num && pre_fin) - input_report_key(dev, BTN_TOUCH, 0); - - pre_fin = touch_num; + mutex_lock(&dev->mutex); for (i = 0; i < GOODIX_MAX_TOUCH; i++) { - if (!touch_data->coords[i].status) - continue; - if (touch_data->coords[i].status == TS_RELEASE) { + if (touch_data->coords[i].status == TS_TOUCH) { + ts_debug("report: id %d, x %d, y %d, w %d", i, + touch_data->coords[i].x, touch_data->coords[i].y, + touch_data->coords[i].w); + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, + touch_data->coords[i].x); + input_report_abs(dev, ABS_MT_POSITION_Y, + touch_data->coords[i].y); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, + touch_data->coords[i].w); + } else { input_mt_slot(dev, i); input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); - continue; } - ts_debug("report: id %d, x %d, y %d, w %d", i, - touch_data->coords[i].x, touch_data->coords[i].y, - touch_data->coords[i].p); - input_mt_slot(dev, i); - input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); - input_report_abs(dev, ABS_MT_POSITION_X, - touch_data->coords[i].x); - input_report_abs(dev, ABS_MT_POSITION_Y, - touch_data->coords[i].y); - input_report_abs(dev, ABS_MT_TOUCH_MAJOR, - touch_data->coords[i].w); } + input_report_key(dev, BTN_TOUCH, touch_num > 0 ? 1 : 0); input_sync(dev); + + mutex_unlock(&dev->mutex); } static int goodix_ts_request_handle(struct goodix_ts_core *cd, @@ -946,8 +1179,10 @@ static irqreturn_t goodix_ts_threadirq_func(int irq, void *data) struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; struct goodix_ext_module *ext_module, *next; struct goodix_ts_event *ts_event = &core_data->ts_event; + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; int ret; + ts_esd->irq_status = true; core_data->irq_trig_cnt++; /* inform external module */ mutex_lock(&goodix_modules.mutex); @@ -971,7 +1206,8 @@ static irqreturn_t goodix_ts_threadirq_func(int irq, void *data) goodix_ts_report_finger(core_data->input_dev, &ts_event->touch_data); } - if (ts_event->event_type == EVENT_PEN) { + if (core_data->board_data.pen_enable && + ts_event->event_type == EVENT_PEN) { goodix_ts_report_pen(core_data->pen_dev, &ts_event->pen_data); } @@ -980,7 +1216,10 @@ static irqreturn_t goodix_ts_threadirq_func(int irq, void *data) } } - hw_ops->after_event_handler(core_data); + if (!core_data->tools_ctrl_sync && !ts_event->retry) + hw_ops->after_event_handler(core_data); + ts_event->retry = 0; + return IRQ_HANDLED; } @@ -1037,6 +1276,11 @@ static int goodix_ts_power_init(struct goodix_ts_core *core_data) core_data->avdd = NULL; return ret; } + ret = regulator_set_voltage(core_data->avdd, 3000000, 3000000); + if (ret < 0) { + ts_err("set avdd voltage failed"); + return ret; + } } else { ts_info("Avdd name is NULL"); } @@ -1049,6 +1293,11 @@ static int goodix_ts_power_init(struct goodix_ts_core *core_data) ts_err("Failed to get regulator iovdd:%d", ret); core_data->iovdd = NULL; } + ret = regulator_set_voltage(core_data->iovdd, 1800000, 1800000); + if (ret < 0) { + ts_err("set iovdd voltage failed"); + return ret; + } } else { ts_info("iovdd name is NULL"); } @@ -1116,7 +1365,7 @@ static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data) * driver should use gpiod_ api. */ r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->reset_gpio, - GPIOF_OUT_INIT_HIGH, "ts_reset_gpio"); + GPIOF_OUT_INIT_LOW, "ts_reset_gpio"); if (r < 0) { ts_err("Failed to request reset gpio, r:%d", r); return r; @@ -1129,6 +1378,24 @@ static int goodix_ts_gpio_setup(struct goodix_ts_core *core_data) return r; } + if (ts_bdata->avdd_gpio > 0) { + r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->avdd_gpio, + GPIOF_OUT_INIT_LOW, "ts_avdd_gpio"); + if (r < 0) { + ts_err("Failed to request avdd-gpio, r:%d", r); + return r; + } + } + + if (ts_bdata->iovdd_gpio > 0) { + r = devm_gpio_request_one(&core_data->pdev->dev, ts_bdata->iovdd_gpio, + GPIOF_OUT_INIT_LOW, "ts_iovdd_gpio"); + if (r < 0) { + ts_err("Failed to request iovdd-gpio, r:%d", r); + return r; + } + } + return 0; } @@ -1208,6 +1475,7 @@ static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) ts_err("Failed to allocated pen device"); return -ENOMEM; } + core_data->pen_dev = pen_dev; input_set_drvdata(pen_dev, core_data); @@ -1219,6 +1487,8 @@ static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) pen_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); __set_bit(ABS_X, pen_dev->absbit); __set_bit(ABS_Y, pen_dev->absbit); + __set_bit(ABS_TILT_X, pen_dev->absbit); + __set_bit(ABS_TILT_Y, pen_dev->absbit); __set_bit(BTN_STYLUS, pen_dev->keybit); __set_bit(BTN_STYLUS2, pen_dev->keybit); __set_bit(BTN_TOUCH, pen_dev->keybit); @@ -1227,7 +1497,11 @@ static int goodix_ts_pen_dev_config(struct goodix_ts_core *core_data) input_set_abs_params(pen_dev, ABS_X, 0, ts_bdata->panel_max_x, 0, 0); input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y, 0, 0); input_set_abs_params(pen_dev, ABS_PRESSURE, 0, - ts_bdata->panel_max_w, 0, 0); + ts_bdata->panel_max_p, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_X, + -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_Y, + -GOODIX_PEN_MAX_TILT, GOODIX_PEN_MAX_TILT, 0, 0); r = input_register_device(pen_dev); if (r < 0) { @@ -1271,6 +1545,9 @@ static void goodix_ts_esd_work(struct work_struct *work) const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; int ret = 0; + if (ts_esd->irq_status) + goto exit; + if (!atomic_read(&ts_esd->esd_on)) return; @@ -1282,9 +1559,10 @@ static void goodix_ts_esd_work(struct work_struct *work) ts_err("esd check failed"); goodix_ts_power_off(cd); goodix_ts_power_on(cd); - if (hw_ops->reset) - hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); } + +exit: + ts_esd->irq_status = false; if (atomic_read(&ts_esd->esd_on)) schedule_delayed_work(&ts_esd->esd_work, 2 * HZ); } @@ -1300,6 +1578,9 @@ static void goodix_ts_esd_on(struct goodix_ts_core *cd) if (!misc->esd_addr) return; + if (atomic_read(&ts_esd->esd_on)) + return; + atomic_set(&ts_esd->esd_on, 1); if (!schedule_delayed_work(&ts_esd->esd_work, 2 * HZ)) { ts_info("esd work already in workqueue"); @@ -1315,6 +1596,9 @@ static void goodix_ts_esd_off(struct goodix_ts_core *cd) struct goodix_ts_esd *ts_esd = &cd->ts_esd; int ret; + if (!atomic_read(&ts_esd->esd_on)) + return; + atomic_set(&ts_esd->esd_on, 0); ret = cancel_delayed_work_sync(&ts_esd->esd_work); ts_info("Esd off, esd work state %d", ret); @@ -1380,24 +1664,21 @@ int goodix_ts_esd_init(struct goodix_ts_core *cd) static void goodix_ts_release_connects(struct goodix_ts_core *core_data) { struct input_dev *input_dev = core_data->input_dev; - struct input_mt *mt = input_dev->mt; int i; - memset(&core_data->ts_event, 0, sizeof(core_data->ts_event)); - goodix_ts_report_finger(core_data->input_dev, - &core_data->ts_event.touch_data); + mutex_lock(&input_dev->mutex); - if (mt) { - for (i = 0; i < mt->num_slots; i++) { - input_mt_slot(input_dev, i); - input_mt_report_slot_state(input_dev, - MT_TOOL_FINGER, - false); - } - input_report_key(input_dev, BTN_TOUCH, 0); - input_mt_sync_frame(input_dev); - input_sync(input_dev); + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, + false); } + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync_frame(input_dev); + input_sync(input_dev); + + mutex_unlock(&input_dev->mutex); } /** @@ -1410,7 +1691,14 @@ static int goodix_ts_suspend(struct goodix_ts_core *core_data) struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; int ret; + if (core_data->init_stage < CORE_INIT_STAGE2 || + atomic_read(&core_data->suspended)) + return 0; + ts_info("Suspend start"); + atomic_set(&core_data->suspended, 1); + /* disable irq */ + hw_ops->irq_enable(core_data, false); /* * notify suspend event, inform the esd protector @@ -1438,19 +1726,15 @@ static int goodix_ts_suspend(struct goodix_ts_core *core_data) } mutex_unlock(&goodix_modules.mutex); - /* disable irq */ - hw_ops->irq_enable(core_data, false); - - /* let touch ic work in sleep mode */ + /* enter sleep mode or power off */ if (hw_ops->suspend) hw_ops->suspend(core_data); - atomic_set(&core_data->suspended, 1); /* inform exteranl modules */ mutex_lock(&goodix_modules.mutex); if (!list_empty(&goodix_modules.head)) { list_for_each_entry_safe(ext_module, next, - &goodix_modules.head, list) { + &goodix_modules.head, list) { if (!ext_module->funcs->after_suspend) continue; @@ -1467,6 +1751,7 @@ static int goodix_ts_suspend(struct goodix_ts_core *core_data) mutex_unlock(&goodix_modules.mutex); out: + goodix_ts_release_connects(core_data); ts_info("Suspend end"); return 0; } @@ -1481,8 +1766,12 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; int ret; + if (core_data->init_stage < CORE_INIT_STAGE2 || + !atomic_read(&core_data->suspended)) + return 0; + ts_info("Resume start"); - goodix_ts_release_connects(core_data); + atomic_set(&core_data->suspended, 0); mutex_lock(&goodix_modules.mutex); if (!list_empty(&goodix_modules.head)) { @@ -1492,7 +1781,7 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) continue; ret = ext_module->funcs->before_resume(core_data, - ext_module); + ext_module); if (ret == EVT_CANCEL_RESUME) { mutex_unlock(&goodix_modules.mutex); ts_info("Canceled by module:%s", @@ -1503,8 +1792,7 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) } mutex_unlock(&goodix_modules.mutex); - atomic_set(&core_data->suspended, 0); - /* resume device */ + /* reset device or power on*/ if (hw_ops->resume) hw_ops->resume(core_data); @@ -1527,16 +1815,12 @@ static int goodix_ts_resume(struct goodix_ts_core *core_data) } mutex_unlock(&goodix_modules.mutex); - hw_ops->irq_enable(core_data, true); - - /* - * notify resume event, inform the esd protector - * and charger detector to turn on the work - */ - ts_info("try notify resume"); - goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); out: - ts_debug("Resume end"); + /* enable irq */ + hw_ops->irq_enable(core_data, true); + /* open esd */ + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); + ts_info("Resume end"); return 0; } @@ -1607,6 +1891,9 @@ static int goodix_generic_noti_callback(struct notifier_block *self, struct goodix_ts_core, ts_notifier); const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + if (cd->init_stage < CORE_INIT_STAGE2) + return 0; + ts_info("notify event type 0x%x", (unsigned int)action); switch (action) { case NOTIFY_FWUPDATE_START: @@ -1655,12 +1942,21 @@ int goodix_ts_stage2_init(struct goodix_ts_core *cd) if (fb_register_client(&cd->fb_notifier)) ts_err("Failed to register fb notifier client:%d", ret); #endif - /*create sysfs files*/ + /* create sysfs files */ goodix_ts_sysfs_init(cd); + /* create procfs files */ + goodix_ts_procfs_init(cd); + /* esd protector */ goodix_ts_esd_init(cd); - goodix_debugfs_init(cd); + + /* gesture init */ + gesture_module_init(); + + /* inspect init */ + inspect_module_init(); + return 0; exit: goodix_ts_pen_dev_remove(cd); @@ -1754,6 +2050,7 @@ static int goodix_later_init_thread(void *data) goto uninit_fw; } cd->init_stage = CORE_INIT_STAGE2; + return 0; uninit_fw: @@ -1854,7 +2151,6 @@ static int goodix_ts_probe(struct platform_device *pdev) goto err_out; } - core_data->hw_ops->reset(core_data, GOODIX_NORMAL_RESET_DELAY_MS); /* confirm it's goodix touch dev or not */ ret = core_data->hw_ops->dev_confirm(core_data); if (ret) { @@ -1873,11 +2169,13 @@ static int goodix_ts_probe(struct platform_device *pdev) goto err_out; } + /* debug node init */ + goodix_tools_init(); + core_data->init_stage = CORE_INIT_STAGE1; goodix_modules.core_data = core_data; core_module_prob_sate = CORE_MODULE_PROB_SUCCESS; - complete_all(&goodix_modules.core_comp); ts_info("goodix_ts_core probe success"); return 0; @@ -1885,8 +2183,6 @@ err_out: core_data->init_stage = CORE_INIT_FAIL; core_module_prob_sate = CORE_MODULE_PROB_FAILED; ts_err("goodix_ts_core failed, ret:%d", ret); - /* wakeup ext module register work */ - complete_all(&goodix_modules.core_comp); return ret; } @@ -1896,12 +2192,12 @@ static int goodix_ts_remove(struct platform_device *pdev) struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; struct goodix_ts_esd *ts_esd = &core_data->ts_esd; - if (goodix_unregister_all_module()) - return -EBUSY; - goodix_ts_unregister_notifier(&core_data->ts_notifier); + goodix_tools_exit(); if (core_data->init_stage >= CORE_INIT_STAGE2) { + gesture_module_exit(); + inspect_module_exit(); hw_ops->irq_enable(core_data, false); #ifdef CONFIG_FB fb_unregister_client(&core_data->fb_notifier); @@ -1911,11 +2207,11 @@ static int goodix_ts_remove(struct platform_device *pdev) goodix_ts_esd_off(core_data); goodix_ts_unregister_notifier(&ts_esd->esd_notifier); - goodix_debugfs_remove(core_data); goodix_fw_update_uninit(); goodix_ts_input_dev_remove(core_data); goodix_ts_pen_dev_remove(core_data); goodix_ts_sysfs_exit(core_data); + goodix_ts_procfs_exit(core_data); goodix_ts_power_off(core_data); } @@ -1955,7 +2251,11 @@ static int __init goodix_ts_core_init(void) int ret; ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION); - ret = goodix_bus_init(); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + ret = goodix_spi_bus_init(); +#else + ret = goodix_i2c_bus_init(); +#endif if (ret) { ts_err("failed add bus driver"); return ret; @@ -1967,10 +2267,14 @@ static void __exit goodix_ts_core_exit(void) { ts_info("Core layer exit"); platform_driver_unregister(&goodix_ts_driver); - goodix_bus_exit(); - return; +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + goodix_spi_bus_exit(); +#else + goodix_i2c_bus_exit(); +#endif } -module_init(goodix_ts_core_init); + +late_initcall(goodix_ts_core_init); module_exit(goodix_ts_core_exit); MODULE_DESCRIPTION("Goodix Touchscreen Core Module"); diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/include/goodix_ts_core.h b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.h old mode 100755 new mode 100644 similarity index 83% rename from drivers/input/touchscreen/goodix_berlin_mmi/include/goodix_ts_core.h rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.h index c1a73365e77f..fe150bb25fae --- a/drivers/input/touchscreen/goodix_berlin_mmi/include/goodix_ts_core.h +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.h @@ -14,7 +14,6 @@ #include #include #include -#include #include #ifdef CONFIG_OF #include @@ -25,18 +24,33 @@ #include #endif -#define GOODIX_CORE_DRIVER_NAME "goodix_ts" -#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" -#define GOODIX_DRIVER_VERSION "v1.0.1" -#define GOODIX_MAX_TOUCH 10 -#define GOODIX_PEN_MAX_PRESSURE 4096 -#define GOODIX_MAX_PEN_KEY 2 -#define GOODIX_CFG_MAX_SIZE 4096 -#define GOODIX_MAX_STR_LABLE_LEN 32 +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_DRIVER_VERSION "v1.1.21" +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_PEN_MAX_PRESSURE 4096 +#define GOODIX_MAX_PEN_KEY 2 +#define GOODIX_PEN_MAX_TILT 90 +#define GOODIX_CFG_MAX_SIZE 4096 +#define GOODIX_MAX_STR_LABLE_LEN 32 #define GOODIX_NORMAL_RESET_DELAY_MS 100 #define GOODIX_HOLD_CPU_RESET_DELAY_MS 5 +#define GOODIX_RETRY_3 3 +#define GOODIX_RETRY_5 5 +#define GOODIX_RETRY_10 10 + +#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" +#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" + +enum CORD_PROB_STA { + CORE_MODULE_UNPROBED = 0, + CORE_MODULE_PROB_SUCCESS = 1, + CORE_MODULE_PROB_FAILED = -1, + CORE_MODULE_REMOVED = -2, +}; + enum GOODIX_ERR_CODE { GOODIX_EBUS = (1<<0), GOODIX_ECHECKSUM = (1<<1), @@ -52,7 +66,9 @@ enum IC_TYPE_ID { IC_TYPE_NORMANDY, IC_TYPE_NANJING, IC_TYPE_YELLOWSTONE, - IC_TYPE_BERLIN, + IC_TYPE_BERLIN_A, + IC_TYPE_BERLIN_B, + IC_TYPE_BERLIN_D }; enum GOODIX_IC_CONFIG_TYPE { @@ -180,6 +196,18 @@ struct goodix_ic_info { }; #pragma pack() +/* + * struct ts_rawdata_info + * + */ +#define TS_RAWDATA_BUFF_MAX 7000 +#define TS_RAWDATA_RESULT_MAX 100 +struct ts_rawdata_info { + int used_size; //fill in rawdata size + s16 buff[TS_RAWDATA_BUFF_MAX]; + char result[TS_RAWDATA_RESULT_MAX]; +}; + /* * struct goodix_module - external modules container * @head: external modules list @@ -193,7 +221,6 @@ struct goodix_module { bool initilized; struct mutex mutex; struct workqueue_struct *wq; - struct completion core_comp; struct goodix_ts_core *core_data; }; @@ -214,6 +241,8 @@ struct goodix_ts_board_data { char iovdd_name[GOODIX_MAX_STR_LABLE_LEN]; int reset_gpio; int irq_gpio; + int avdd_gpio; + int iovdd_gpio; unsigned int irq_flags; unsigned int swap_axis; @@ -223,8 +252,8 @@ struct goodix_ts_board_data { unsigned int panel_max_p; /*pressure*/ bool pen_enable; - const char *fw_name; - const char *cfg_bin_name; + char fw_name[GOODIX_MAX_STR_LABLE_LEN]; + char cfg_bin_name[GOODIX_MAX_STR_LABLE_LEN]; }; enum goodix_fw_update_mode { @@ -324,6 +353,7 @@ struct goodix_pen_data { * @event_data: event data */ struct goodix_ts_event { + int retry; enum ts_event_type event_type; u8 request_code; /* represent the request type */ u8 gesture_type; @@ -339,6 +369,7 @@ enum goodix_ic_bus_type { struct goodix_bus_interface { int bus_type; + int ic_type; struct device *dev; int (*read)(struct device *dev, unsigned int addr, unsigned char *data, unsigned int len); @@ -366,6 +397,7 @@ struct goodix_ts_hw_ops { int (*esd_check)(struct goodix_ts_core *cd); int (*event_handler)(struct goodix_ts_core *cd, struct goodix_ts_event *ts_event); int (*after_event_handler)(struct goodix_ts_core *cd); /* clean sync flag */ + int (*get_capacitance_data)(struct goodix_ts_core *cd, struct ts_rawdata_info *info); }; /* @@ -375,6 +407,7 @@ struct goodix_ts_hw_ops { * off esd protection */ struct goodix_ts_esd { + bool irq_status; atomic_t esd_on; struct delayed_work esd_work; struct notifier_block esd_notifier; @@ -403,7 +436,7 @@ struct goodix_ts_core { struct goodix_ts_hw_ops *hw_ops; struct input_dev *input_dev; struct input_dev *pen_dev; - /* TODO counld we remove this from core data? */ + /* TODO counld we remove this from core data? */ struct goodix_ts_event ts_event; /* every pointer of this array represent a kind of config */ @@ -417,6 +450,8 @@ struct goodix_ts_core { atomic_t irq_enabled; atomic_t suspended; + /* when this flag is true, driver should not clean the sync flag */ + bool tools_ctrl_sync; struct notifier_block ts_notifier; struct goodix_ts_esd ts_esd; @@ -436,9 +471,9 @@ enum goodix_ext_priority { EXTMOD_PRIO_DEFAULT, }; -#define EVT_HANDLED 0 +#define EVT_HANDLED 0 #define EVT_CONTINUE 0 -#define EVT_CANCEL 1 +#define EVT_CANCEL 1 #define EVT_CANCEL_IRQEVT 1 #define EVT_CANCEL_SUSPEND 1 #define EVT_CANCEL_RESUME 1 @@ -511,16 +546,11 @@ struct goodix_ext_attribute { static struct goodix_ext_attribute ext_attr_##_name = \ __EXTMOD_ATTR(_name, _mode, _show, _store); - -//#define CONFIG_GOODIX_DEBUG /* log macro */ +extern bool debug_log_flag; #define ts_info(fmt, arg...) pr_info("[GTP-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) -#define ts_err(fmt, arg...) pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) -#ifdef CONFIG_GOODIX_DEBUG -#define ts_debug(fmt, arg...) pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) -#else -#define ts_debug(fmt, arg...) do {} while (0) -#endif +#define ts_err(fmt, arg...) pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#define ts_debug(fmt, arg...) {if (debug_log_flag) pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);} /* * get board data pointer @@ -554,24 +584,35 @@ int goodix_unregister_ext_module(struct goodix_ext_module *module); /* remove all registered ext module * return 0 on success, otherwise return < 0 */ -int goodix_unregister_all_module(void); int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v); -void goodix_debugfs_printf(const char *fmt, ...); struct kobj_type *goodix_get_default_ktype(void); +struct kobject *goodix_get_default_kobj(void); struct goodix_ts_hw_ops *goodix_get_hw_ops(void); int goodix_get_config_proc(struct goodix_ts_core *cd); -int goodix_bus_init(void); -void goodix_bus_exit(void); +int goodix_spi_bus_init(void); +void goodix_spi_bus_exit(void); +int goodix_i2c_bus_init(void); +void goodix_i2c_bus_exit(void); u32 goodix_append_checksum(u8 *data, int len, int mode); int checksum_cmp(const u8 *data, int size, int mode); int is_risk_data(const u8 *data, int size); u32 goodix_get_file_config_id(u8 *ic_config); +void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data); +int goodix_gesture_enable(int enable); int goodix_fw_update_init(struct goodix_ts_core *core_data); void goodix_fw_update_uninit(void); int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode); +int goodix_get_ic_type(struct device_node *node); +int gesture_module_init(void); +void gesture_module_exit(void); +int inspect_module_init(void); +void inspect_module_exit(void); +int goodix_tools_init(void); +void goodix_tools_exit(void); + #endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/goodix_ts_gesture.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture.c old mode 100755 new mode 100644 similarity index 73% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/goodix_ts_gesture.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture.c index e49445a301bc..a18676e1f441 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/goodix_ts_gesture.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture.c @@ -48,6 +48,30 @@ struct gesture_module { }; static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ +static bool module_initialized; + + +int goodix_gesture_enable(int enable) +{ + int ret = 0; + + if (!module_initialized) + return 0; + + if (enable) { + if (atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already registered"); + else + ret = goodix_register_ext_module_no_wait(&gsx_gesture->module); + } else { + if (!atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already unregistered"); + else + ret = goodix_unregister_ext_module(&gsx_gesture->module); + } + + return ret; +} /** * gsx_gesture_type_show - show valid gesture type @@ -125,10 +149,10 @@ static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, return ret; if (val) { - ret = goodix_register_ext_module_no_wait(&gsx_gesture->module); + ret = goodix_gesture_enable(1); return ret ? ret : count; } else { - ret = goodix_unregister_ext_module(&gsx_gesture->module); + ret = goodix_gesture_enable(0); return ret ? ret : count; } } @@ -157,46 +181,33 @@ const struct goodix_ext_attribute gesture_attrs[] = { static int gsx_gesture_init(struct goodix_ts_core *cd, struct goodix_ext_module *module) { - int i, ret = -EINVAL; - if (!cd || !cd->hw_ops->gesture) { ts_err("gesture unsupported"); return -EINVAL; } - if (atomic_read(&gsx_gesture->registered)) - return 0; - + ts_info("gesture switch: ON"); ts_debug("enable all gesture type"); /* set all bit to 1 to enable all gesture wakeup */ memset(gsx_gesture->gesture_type, 0xff, GSX_GESTURE_TYPE_LEN); - module->priv_data = cd; - - ret = kobject_init_and_add(&module->kobj, goodix_get_default_ktype(), - &cd->pdev->dev.kobj, "gesture"); - if (ret) { - ts_err("failed create gesture sysfs node!"); - return ret; - } - - for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) - ret = sysfs_create_file(&module->kobj, &gesture_attrs[i].attr); - if (ret) { - ts_err("failed create gst sysfs files"); - while (--i >= 0) - sysfs_remove_file(&module->kobj, &gesture_attrs[i].attr); - - kobject_put(&module->kobj); - return ret; - } - atomic_set(&gsx_gesture->registered, 1); + return 0; } static int gsx_gesture_exit(struct goodix_ts_core *cd, struct goodix_ext_module *module) { + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: OFF"); + ts_debug("disable all gesture type"); + memset(gsx_gesture->gesture_type, 0x00, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->registered, 0); + return 0; } @@ -248,9 +259,10 @@ static int gsx_gesture_ist(struct goodix_ts_core *cd, re_send_ges_cmd: if (hw_ops->gesture(cd, 0)) - ts_info("warning: failed re_send gesture cmd\n"); + ts_info("warning: failed re_send gesture cmd"); gesture_ist_exit: - hw_ops->after_event_handler(cd); + if (!cd->tools_ctrl_sync) + hw_ops->after_event_handler(cd); return EVT_CANCEL_IRQEVT; } @@ -268,29 +280,49 @@ static int gsx_gesture_before_suspend(struct goodix_ts_core *cd, int ret; const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; - atomic_set(&cd->suspended, 1); ret = hw_ops->gesture(cd, 0); if (ret) ts_err("failed enter gesture mode"); else ts_info("enter gesture mode"); + hw_ops->irq_enable(cd, true); + enable_irq_wake(cd->irq); + return EVT_CANCEL_SUSPEND; } +static int gsx_gesture_before_resume(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + hw_ops->irq_enable(cd, false); + disable_irq_wake(cd->irq); + hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + + return EVT_CANCEL_RESUME; +} + static struct goodix_ext_module_funcs gsx_gesture_funcs = { .irq_event = gsx_gesture_ist, .init = gsx_gesture_init, .exit = gsx_gesture_exit, - .before_suspend = gsx_gesture_before_suspend + .before_suspend = gsx_gesture_before_suspend, + .before_resume = gsx_gesture_before_resume, }; -static int __init goodix_gsx_gesture_init(void) +int gesture_module_init(void) { - ts_info("gesture module init"); + int ret; + int i; + struct kobject *def_kobj = goodix_get_default_kobj(); + struct kobj_type *def_kobj_type = goodix_get_default_ktype(); + gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); if (!gsx_gesture) return -ENOMEM; + gsx_gesture->module.funcs = &gsx_gesture_funcs; gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE; gsx_gesture->module.name = "Goodix_gsx_gesture"; @@ -298,32 +330,55 @@ static int __init goodix_gsx_gesture_init(void) atomic_set(&gsx_gesture->registered, 0); rwlock_init(&gsx_gesture->rwlock); - goodix_register_ext_module(&(gsx_gesture->module)); + + /* gesture sysfs init */ + ret = kobject_init_and_add(&gsx_gesture->module.kobj, + def_kobj_type, def_kobj, "gesture"); + if (ret) { + ts_err("failed create gesture sysfs node!"); + goto err_out; + } + + for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) + ret = sysfs_create_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + if (ret) { + ts_err("failed create gst sysfs files"); + while (--i >= 0) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + + kobject_put(&gsx_gesture->module.kobj); + goto err_out; + } + + module_initialized = true; + ts_info("gesture module init success"); + return 0; + +err_out: + ts_err("gesture module init failed!"); + kfree(gsx_gesture); + return ret; } -static void __exit goodix_gsx_gesture_exit(void) +void gesture_module_exit(void) { int i; ts_info("gesture module exit"); - if (atomic_read(&gsx_gesture->registered)) { - goodix_unregister_ext_module(&gsx_gesture->module); - atomic_set(&gsx_gesture->registered, 0); + if (!module_initialized) + return; - for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++) - sysfs_remove_file(&gsx_gesture->module.kobj, - &gesture_attrs[i].attr); + goodix_gesture_enable(0); - kobject_put(&gsx_gesture->module.kobj); - } + /* deinit sysfs */ + for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++) + sysfs_remove_file(&gsx_gesture->module.kobj, + &gesture_attrs[i].attr); + kobject_put(&gsx_gesture->module.kobj); kfree(gsx_gesture); -} - -late_initcall(goodix_gsx_gesture_init); -module_exit(goodix_gsx_gesture_exit); - -MODULE_DESCRIPTION("Goodix Touchscreen Gesture Module"); -MODULE_AUTHOR("Goodix, Inc."); -MODULE_LICENSE("GPL v2"); + module_initialized = false; +} \ No newline at end of file diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_inspect.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_inspect.c new file mode 100644 index 000000000000..90850add9e83 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_inspect.c @@ -0,0 +1,2736 @@ + /* + * 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" +#include +#include +#include +#include +#include + + +/* test config */ +#define TOTAL_FRAME_NUM 1 /* rawdata test frames */ +#define NOISEDATA_TEST_TIMES 1 /* noise test frames */ +#define SAVE_IN_CSV + +#define GOODIX_RESULT_SAVE_PATH "/vendor/etc/Test_Data.csv" +#define GOODIX_TEST_FILE_NAME "goodix" +#define MAX_DATA_BUFFER 28000 +#define MAX_SHORT_NUM 15 +#define MAX_LINE_LEN (1024 * 3 * 7) +#define MAX_DRV_NUM 52 +#define MAX_SEN_NUM 75 + +#define STATISTICS_DATA_LEN 32 +#define MAX_STR_LEN 32 +#define MAX_TEST_ITEMS 10 /* 0P-1P-2P-3P-5P total test items */ +#define GTP_CAP_TEST 1 +#define GTP_DELTA_TEST 2 +#define GTP_NOISE_TEST 3 +#define GTP_SHORT_TEST 5 +#define GTP_SELFCAP_TEST 6 +#define GTP_SELFNOISE_TEST 7 + +#define GTP_TEST_PASS 1 +#define GTP_PANEL_REASON 2 +#define SYS_SOFTWARE_REASON 3 + +#define CHN_VDD 0xFF +#define CHN_GND 0x7F +#define DRV_CHANNEL_FLAG 0x80 + +#define CSV_TP_SPECIAL_RAW_MIN "special_raw_min" +#define CSV_TP_SPECIAL_RAW_MAX "special_raw_max" +#define CSV_TP_SPECIAL_RAW_DELTA "special_raw_delta" +#define CSV_TP_SHORT_THRESHOLD "shortciurt_threshold" +#define CSV_TP_SPECIAL_SELFRAW_MAX "special_selfraw_max" +#define CSV_TP_SPECIAL_SELFRAW_MIN "special_selfraw_min" +#define CSV_TP_NOISE_LIMIT "noise_data_limit" +#define CSV_TP_SELFNOISE_LIMIT "noise_selfdata_limit" +#define CSV_TP_TEST_CONFIG "test_config" + +#define MAX_TEST_TIME_MS 15000 +#define DEFAULT_TEST_TIME_MS 7000 + +/* berlin A */ +#define MAX_DRV_NUM_BRA 21 +#define MAX_SEN_NUM_BRA 42 +#define SHORT_TEST_TIME_REG_BRA 0x11FF2 +#define DFT_ADC_DUMP_NUM_BRA 1396 +#define DFT_SHORT_THRESHOLD_BRA 16 +#define DFT_DIFFCODE_SHORT_THRESHOLD_BRA 16 +#define SHORT_TEST_STATUS_REG_BRA 0x10400 +#define SHORT_TEST_RESULT_REG_BRA 0x10410 +#define DRV_DRV_SELFCODE_REG_BRA 0x1045E +#define SEN_SEN_SELFCODE_REG_BRA 0x1084E +#define DRV_SEN_SELFCODE_REG_BRA 0x11712 +#define DIFF_CODE_DATA_REG_BRA 0x11F72 + +/* berlin B */ +#define MAX_DRV_NUM_BRB 52 +#define MAX_SEN_NUM_BRB 75 +#define SHORT_TEST_TIME_REG_BRB 0x26AE0 +#define DFT_ADC_DUMP_NUM_BRB 762 +#define DFT_SHORT_THRESHOLD_BRB 100 +#define DFT_DIFFCODE_SHORT_THRESHOLD_BRB 32 +#define SHORT_TEST_STATUS_REG_BRB 0x20400 +#define SHORT_TEST_RESULT_REG_BRB 0x20410 +#define DRV_DRV_SELFCODE_REG_BRB 0x2049A +#define SEN_SEN_SELFCODE_REG_BRB 0x21AF2 +#define DRV_SEN_SELFCODE_REG_BRB 0x248A6 +#define DIFF_CODE_DATA_REG_BRB 0x269E0 + + +#define ABS(val) ((val < 0)? -(val) : val) +#define MAX(a, b) ((a > b)? a : b) + +static bool module_initialized; + +/* berlin A drv-sen map */ +static u8 brl_a_drv_map[] = { + 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62 +}; +static u8 brl_a_sen_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41 +}; + +/* berlin B drv-sen map */ +static u8 brl_b_drv_map[] = { + 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, + 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, + 106, 107, 108, 109, 110, 111, 112, + 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126 +}; +static u8 brl_b_sen_map[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74 +}; + +typedef struct __attribute__((packed)) { + u8 result; + u8 drv_drv_num; + u8 sen_sen_num; + u8 drv_sen_num; + u8 drv_gnd_avdd_num; + u8 sen_gnd_avdd_num; + u16 checksum; +} test_result_t; + +struct params_info_t { + u32 max_drv_num; + u32 max_sen_num; + u8 *drv_map; + u8 *sen_map; + u32 short_test_time_reg; + u32 short_test_status_reg; + u32 short_test_result_reg; + u32 drv_drv_selfcode_reg; + u32 sen_sen_selfcode_reg; + u32 drv_sen_selfcode_reg; + u32 diffcode_data_reg; + u16 short_test_dump_num; + u16 dft_short_threshold; + u16 short_diffcode_threshold; +}; + +struct params_info_t params_bra = { + MAX_DRV_NUM_BRA, + MAX_SEN_NUM_BRA, + brl_a_drv_map, + brl_a_sen_map, + SHORT_TEST_TIME_REG_BRA, + SHORT_TEST_STATUS_REG_BRA, + SHORT_TEST_RESULT_REG_BRA, + DRV_DRV_SELFCODE_REG_BRA, + SEN_SEN_SELFCODE_REG_BRA, + DRV_SEN_SELFCODE_REG_BRA, + DIFF_CODE_DATA_REG_BRA, + DFT_ADC_DUMP_NUM_BRA, + DFT_SHORT_THRESHOLD_BRA, + DFT_DIFFCODE_SHORT_THRESHOLD_BRA, +}; + +struct params_info_t params_brb = { + MAX_DRV_NUM_BRB, + MAX_SEN_NUM_BRB, + brl_b_drv_map, + brl_b_sen_map, + SHORT_TEST_TIME_REG_BRB, + SHORT_TEST_STATUS_REG_BRB, + SHORT_TEST_RESULT_REG_BRB, + DRV_DRV_SELFCODE_REG_BRB, + SEN_SEN_SELFCODE_REG_BRB, + DRV_SEN_SELFCODE_REG_BRB, + DIFF_CODE_DATA_REG_BRB, + DFT_ADC_DUMP_NUM_BRB, + DFT_SHORT_THRESHOLD_BRB, + DFT_DIFFCODE_SHORT_THRESHOLD_BRB, +}; + +struct ts_test_params { + bool test_items[MAX_TEST_ITEMS]; + + u32 rawdata_addr; + u32 noisedata_addr; + u32 self_rawdata_addr; + u32 self_noisedata_addr; + + u32 drv_num; + u32 sen_num; + + struct params_info_t *params_info; + + s32 cfg_buf[GOODIX_CFG_MAX_SIZE]; + s32 max_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + s32 min_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + s32 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + s32 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM]; + s32 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM]; + s32 noise_threshold; + s32 self_noise_threshold; + + u32 short_threshold; + u32 r_drv_drv_threshold; + u32 r_drv_sen_threshold; + u32 r_sen_sen_threshold; + u32 r_drv_gnd_threshold; + u32 r_sen_gnd_threshold; + u32 avdd_value; +}; + +struct ts_test_rawdata { + s16 data[MAX_DRV_NUM * MAX_SEN_NUM]; + u32 size; +}; + +struct ts_test_self_rawdata { + s16 data[MAX_DRV_NUM + MAX_SEN_NUM]; + u32 size; +}; + +struct ts_short_res { + u8 short_num; + s16 short_msg[4 * MAX_SHORT_NUM]; +}; + +struct ts_open_res { + u8 beyond_max_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; + u8 beyond_min_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; + u8 beyond_accord_limit_cnt[MAX_DRV_NUM * MAX_SEN_NUM]; +}; + +struct goodix_ts_test { + struct goodix_ts_core *ts; + struct ts_test_params test_params; + struct ts_test_rawdata rawdata[TOTAL_FRAME_NUM]; + struct ts_test_rawdata accord_arr[TOTAL_FRAME_NUM]; + struct ts_test_rawdata noisedata[NOISEDATA_TEST_TIMES]; + struct goodix_ic_config test_config; + struct ts_test_self_rawdata self_rawdata; + struct ts_test_self_rawdata self_noisedata; + struct ts_short_res short_res; + struct ts_open_res open_res; + + /*[0][0][0][0][0].. 0 without test; 1 pass, 2 panel failed; 3 software failed */ + char test_result[MAX_TEST_ITEMS]; + char test_info[TS_RAWDATA_RESULT_MAX]; +}; + +static int cal_cha_to_cha_res(struct goodix_ts_test *ts_test, int v1, int v2) +{ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + return (v1 - v2) * 63 / v2; + + return (v1 - v2) * 74 / v2 + 20; +} + +static int cal_cha_to_avdd_res(struct goodix_ts_test *ts_test, int v1, int v2) +{ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + return 125 * 1024 * (100 * v2 - 125) * 40 / (1000 * v1) - 40; + + return 125 * 1024 * (100 * v2 - 125) * 99 / (1000 * v1) - 60; +} + +static int cal_cha_to_gnd_res(struct goodix_ts_test *ts_test, int v) +{ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + return 64148 / v - 40; + + return 150500 / v - 60; +} + +static int ts_test_reset(struct goodix_ts_test *ts_test, + u32 delay_ms) +{ + return ts_test->ts->hw_ops->reset(ts_test->ts, delay_ms); +} + +static int ts_test_read(struct goodix_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + return ts_test->ts->hw_ops->read(ts_test->ts, addr, data, len); +} + +static int ts_test_write(struct goodix_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + return ts_test->ts->hw_ops->write(ts_test->ts, addr, data, len); +} + +static int ts_test_send_cmd(struct goodix_ts_test *ts_test, + struct goodix_ts_cmd *cmd) +{ + return ts_test->ts->hw_ops->send_cmd(ts_test->ts, cmd); +} + +static int ts_test_irq_enable(struct goodix_ts_test *ts_test, + bool flag) +{ + return ts_test->ts->hw_ops->irq_enable(ts_test->ts, flag); +} + +static int ts_test_send_config(struct goodix_ts_test *ts_test, + int type) +{ + struct goodix_ic_config *cfg; + + if (type >= GOODIX_MAX_CONFIG_GROUP) { + ts_err("unsupproted config type %d", type); + return -EINVAL; + } + cfg = ts_test->ts->ic_configs[type]; + if (!cfg || cfg->len <= 0) { + ts_err("no valid normal config found"); + return -EINVAL; + } + + return ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); +} + +static int ts_test_read_version(struct goodix_ts_test *ts_test, + struct goodix_fw_version *version) +{ + return ts_test->ts->hw_ops->read_version(ts_test->ts, version); +} + +static void goto_next_line(char **ptr) +{ + do { + *ptr = *ptr + 1; + } while (**ptr != '\n' && **ptr != '\0'); + if (**ptr == '\0') { + return; + } + *ptr = *ptr + 1; +} + +static void copy_this_line(char *dest, char *src) +{ + char *copy_from; + char *copy_to; + + copy_from = src; + copy_to = dest; + do { + *copy_to = *copy_from; + copy_from++; + copy_to++; + } while((*copy_from != '\n') && (*copy_from != '\r') && (*copy_from != '\0')); + *copy_to = '\0'; +} + +static int getrid_space(s8* data, s32 len) +{ + u8* buf = NULL; + s32 i; + u32 count = 0; + + buf = (char*)kzalloc(len + 5, GFP_KERNEL); + if (buf == NULL){ + ts_err("get space kzalloc error"); + return -ESRCH; + } + + for (i = 0; i < len; i++) + { + if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n') + { + continue; + } + buf[count++] = data[i]; + } + + buf[count++] = '\0'; + + memcpy(data, buf, count); + kfree(buf); + + return count; +} + +static int parse_valid_data(char *buf_start, loff_t buf_size, + char *ptr, s32 *data, s32 rows) +{ + int i = 0; + int j = 0; + char *token = NULL; + char *tok_ptr = NULL; + char *row_data = NULL; + long temp_val; + + if (!ptr) { + ts_err("ptr is NULL"); + return -EINVAL; + } + if (!data) { + ts_err("data is NULL"); + return -EINVAL; + } + + row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL); + if (!row_data) { + ts_err("alloc bytes %d failed.", MAX_LINE_LEN); + return -ENOMEM; + } + + for (i = 0; i < rows; i++) { + memset(row_data, 0, MAX_LINE_LEN); + copy_this_line(row_data, ptr); + getrid_space(row_data, strlen(row_data)); + tok_ptr = row_data; + while ((token = strsep(&tok_ptr,","))) { + if (strlen(token) == 0) + continue; + if (kstrtol(token, 0, &temp_val)) { + kfree(row_data); + return -EINVAL; + } + data[j++] = (s32)temp_val; + } + if (i == rows - 1) + break; + goto_next_line(&ptr); //next row + if(!ptr || (0 == strlen(ptr)) || (ptr >= (buf_start + buf_size))) { + ts_info("invalid ptr, return"); + kfree(row_data); + row_data = NULL; + return -EPERM; + } + } + kfree(row_data); + return j; +} + +static int parse_csvfile(char *buf, size_t size, char *target_name, + s32 *data, s32 rows, s32 col) +{ + int ret = 0; + char *ptr = NULL; + int read_ret; + + read_ret = size; + if (read_ret > 0) { + ptr = buf; + ptr = strstr(ptr, target_name); + if (!ptr) { + ts_info("load %s failed 1, maybe not this item", target_name); + return -EINTR; + } + + goto_next_line(&ptr); + if (!ptr || (0 == strlen(ptr))) { + ts_err("load %s failed 2!", target_name); + return -EIO; + } + + if (data) { + ret = parse_valid_data(buf, size, ptr, data, rows); + } else { + ts_err("load %s failed 3!", target_name); + return -EINTR; + } + } else { + ts_err("ret=%d, read_ret=%d", ret, read_ret); + ret = -ENXIO; + } + + return ret; +} + + +static void goodix_init_params(struct goodix_ts_test *ts_test) +{ + struct goodix_ts_core *ts = ts_test->ts; + struct ts_test_params *test_params = &ts_test->test_params; + + test_params->rawdata_addr = ts->ic_info.misc.mutual_rawdata_addr; + test_params->noisedata_addr = ts->ic_info.misc.mutual_diffdata_addr; + test_params->self_rawdata_addr = ts->ic_info.misc.self_rawdata_addr; + test_params->self_noisedata_addr = ts->ic_info.misc.self_diffdata_addr; + + test_params->drv_num = ts->ic_info.parm.drv_num; + test_params->sen_num = ts->ic_info.parm.sen_num; + + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) + test_params->params_info = ¶ms_bra; + else + test_params->params_info = ¶ms_brb; +} + +static int goodix_init_testlimits(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + u32 data_buf[10] = {0}; + char *temp_buf = NULL; + struct ts_test_params *test_params = &ts_test->test_params; + struct goodix_ts_core *ts_core = ts_test->ts; + const struct firmware *firmware = NULL; + struct device *dev = &ts_core->pdev->dev; + char limit_file[100] = {0}; + u32 tx = test_params->drv_num; + u32 rx = test_params->sen_num; + + sprintf(limit_file, "%s_test_limits_%d.csv", GOODIX_TEST_FILE_NAME, + ts_core->fw_version.sensor_id); + ts_info("limit_file_name:%s", limit_file); + + ret = request_firmware(&firmware, limit_file, dev); + if (ret < 0) { + ts_err("limits file [%s] not available", limit_file); + return -EINVAL; + } + if (firmware->size <= 0) { + ts_err("request_firmware, limits param length error,len:%zu", + firmware->size); + ret = -EINVAL; + goto exit_free; + } + temp_buf = kzalloc(firmware->size + 1, GFP_KERNEL); + if (!temp_buf) { + ts_err("kzalloc bytes failed."); + ret = -ENOMEM; + goto exit_free; + } + memcpy(temp_buf, firmware->data, firmware->size); + + /* obtain config data */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_TEST_CONFIG, + test_params->cfg_buf, 1, GOODIX_CFG_MAX_SIZE); + if (ret < 0) { + ts_info("Can't find %s", CSV_TP_TEST_CONFIG); + } else { + ts_info("parse_csvfile %s OK, cfg_len:%d", CSV_TP_TEST_CONFIG, ret); + for (i = 0; i < ret; i++) + ts_test->test_config.data[i] = (u8)test_params->cfg_buf[i]; + ts_test->test_config.len = ret; + } + + /* obtain mutual_raw min */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MIN, + test_params->min_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get min_limits"); + goto exit_free; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MIN); + } + /* obtain mutual_raw max */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_MAX, + test_params->max_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get max_limits"); + goto exit_free; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_MAX); + } + /* obtain delta limit */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_RAW_DELTA, + test_params->deviation_limits, rx, tx); + if (ret < 0) { + ts_err("Failed get delta limit"); + goto exit_free; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_RAW_DELTA); + } + + /* obtain self_raw min */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MIN, + test_params->self_min_limits, 1, tx + rx); + /* obtain self_raw max */ + ret |= parse_csvfile(temp_buf, firmware->size, CSV_TP_SPECIAL_SELFRAW_MAX, + test_params->self_max_limits, 1, tx + rx); + if (ret < 0) { + ts_info("Can't find self_min_max_limits, ship this item"); + ret = 0; + test_params->test_items[GTP_SELFCAP_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MIN); + ts_info("parse_csvfile %s OK", CSV_TP_SPECIAL_SELFRAW_MAX); + test_params->test_items[GTP_SELFCAP_TEST] = true; + } + + /* obtain noise_threshold */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_NOISE_LIMIT, + &test_params->noise_threshold, 1, 1); + if (ret < 0) { + ts_info("Can't find noise_threshold, skip this item"); + ret = 0; + test_params->test_items[GTP_NOISE_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_NOISE_LIMIT); + test_params->test_items[GTP_NOISE_TEST] = true; + } + + /* obtain self_noise_threshold */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SELFNOISE_LIMIT, + &test_params->self_noise_threshold, 1, 1); + if (ret < 0) { + ts_info("Can't find self_noise_threshold, skip this item"); + ret = 0; + test_params->test_items[GTP_SELFNOISE_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SELFNOISE_LIMIT); + test_params->test_items[GTP_SELFNOISE_TEST] = true; + } + + /* obtain short_params */ + ret = parse_csvfile(temp_buf, firmware->size, CSV_TP_SHORT_THRESHOLD, + (s32 *)data_buf, 1, 7); + if (ret < 0) { + ts_info("Can't find short shortciurt_threshold, skip this item"); + ret = 0; + test_params->test_items[GTP_SHORT_TEST] = false; + } else { + ts_info("parse_csvfile %s OK", CSV_TP_SHORT_THRESHOLD); + test_params->test_items[GTP_SHORT_TEST] = true; + test_params->short_threshold = data_buf[0]; + test_params->r_drv_drv_threshold = data_buf[1]; + test_params->r_drv_sen_threshold = data_buf[2]; + test_params->r_sen_sen_threshold = data_buf[3]; + test_params->r_drv_gnd_threshold = data_buf[4]; + test_params->r_sen_gnd_threshold = data_buf[5]; + test_params->avdd_value = data_buf[6]; + } + +exit_free: + kfree(temp_buf); + if (firmware) + release_firmware(firmware); + return ret; +} + +static int goodix_tptest_prepare(struct goodix_ts_test *ts_test) +{ + int ret; + struct goodix_ic_config *cfg = &ts_test->test_config; + + ts_info("TP test prepare IN"); + + goodix_init_params(ts_test); + /* parse test limits from csv */ + ret = goodix_init_testlimits(ts_test); + if (ret < 0) { + ts_err("Failed to init testlimits from csv."); + return ret; + } + + /* disable irq */ + ts_test_irq_enable(ts_test, false); + /* close esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + + /* send test config if exist */ + if (cfg->len > 0) { + ts_info("Test config exists and send it"); + ret = ts_test->ts->hw_ops->send_config(ts_test->ts, cfg->data, cfg->len); + if (ret < 0) { + ts_err("Send test config failed, exit"); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + ts_test_irq_enable(ts_test, true); + return ret; + } + } + + return 0; +} + +static void goodix_tptest_finish(struct goodix_ts_test *ts_test) +{ + ts_info("TP test finish IN"); + /* reset chip */ + ts_test_reset(ts_test, 100); + /* send normal config */ + if (ts_test->test_config.len > 0) { + if (ts_test_send_config(ts_test, CONFIG_TYPE_NORMAL)) + ts_err("Send normal config failed"); + } + + /* open esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + /* enable irq */ + ts_test_irq_enable(ts_test, true); +} + +#define SHORT_TEST_RUN_REG 0x10400 +#define SHORT_TEST_RUN_FLAG 0xAA +#define INSPECT_FW_SWITCH_CMD 0x85 +#define TEST_FW_PID "OST" +static int goodix_short_test_prepare(struct goodix_ts_test *ts_test) +{ + struct goodix_ts_cmd tmp_cmd; + struct goodix_fw_version fw_ver; + int ret; + int retry = 3; + u8 status; + + ts_info("short test prepare IN"); + ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; + tmp_cmd.len = 4; + tmp_cmd.cmd = INSPECT_FW_SWITCH_CMD; + + ret = ts_test_send_cmd(ts_test, &tmp_cmd); + if (ret < 0) { + ts_err("send test mode failed"); + return ret; + } + + while (retry--) { + msleep(40); + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_A) { + ret = ts_test_read_version(ts_test, &fw_ver); + if (ret < 0) { + ts_err("read test version failed"); + return ret; + } + ret = memcmp(&(fw_ver.patch_pid[3]), TEST_FW_PID, strlen(TEST_FW_PID)); + if (ret == 0) + return 0; + else + ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID); + } else { + ret = ts_test_read(ts_test, SHORT_TEST_RUN_REG, &status, 1); + if (!ret && status == SHORT_TEST_RUN_FLAG) + return 0; + ts_info("short_mode_status=0x%02x ret=%d", status, ret); + } + } + + return -EINVAL; +} + +static u32 map_die2pin(struct ts_test_params *test_params, u32 chn_num) +{ + int i = 0; + u32 res = 255; + + if (chn_num & DRV_CHANNEL_FLAG) + chn_num = (chn_num & ~DRV_CHANNEL_FLAG) + test_params->params_info->max_sen_num; + + for (i = 0; i < test_params->params_info->max_sen_num; i++) { + if (test_params->params_info->sen_map[i] == chn_num) { + res = i; + break; + } + } + /* res != 255 mean found the corresponding channel num */ + if (res != 255) + return res; + /* if cannot find in SenMap try find in DrvMap */ + for (i = 0; i < test_params->params_info->max_drv_num; i++) { + if (test_params->params_info->drv_map[i] == chn_num) { + res = i; + break; + } + } + if (i >= test_params->params_info->max_drv_num) + ts_err("Faild found corrresponding channel num:%d", chn_num); + else + res |= DRV_CHANNEL_FLAG; + + return res; +} + +static void goodix_save_short_res(struct ts_test_params *params, + u16 chn1, u16 chn2, int r) +{ + int i; + u8 repeat_cnt = 0; + u8 repeat = 0; + struct goodix_ts_test *ts_test = container_of(params, + struct goodix_ts_test, test_params); + struct ts_short_res *short_res = &ts_test->short_res; + + if (chn1 == chn2 || short_res->short_num >= MAX_SHORT_NUM) + return; + + for (i = 0; i < short_res->short_num; i++) { + repeat_cnt = 0; + if (short_res->short_msg[4 * i] == chn1) + repeat_cnt++; + if (short_res->short_msg[4 * i] == chn2) + repeat_cnt++; + if (short_res->short_msg[4 * i + 1] == chn1) + repeat_cnt++; + if (short_res->short_msg[4 * i + 1] == chn2) + repeat_cnt++; + if (repeat_cnt >= 2){ + repeat = 1; + break; + } + } + if (repeat == 0) { + short_res->short_msg[4 * short_res->short_num + 0] = chn1; + short_res->short_msg[4 * short_res->short_num + 1] = chn2; + short_res->short_msg[4 * short_res->short_num + 2] = (r >> 8) & 0xFF; + short_res->short_msg[4 * short_res->short_num + 3] = r & 0xFF; + if (short_res->short_num < MAX_SHORT_NUM) + short_res->short_num++; + } +} + +static int gdix_check_tx_tx_shortcircut(struct goodix_ts_test *ts_test, + u8 short_ch_num) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u16 adc_signal = 0; + u8 master_pin_num, slave_pin_num; + u8 *data_buf; + u32 data_reg; + struct ts_test_params *test_params = &ts_test->test_params; + int max_drv_num = test_params->params_info->max_drv_num; + int max_sen_num = test_params->params_info->max_sen_num; + u16 self_capdata, short_die_num = 0; + + size = 4 + max_drv_num * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* drv&drv shortcircut check */ + data_reg = test_params->params_info->drv_drv_selfcode_reg; + for (i = 0; i < short_ch_num; i++) { + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret < 0) { + ts_err("Failed read Drv-to-Drv short rawdata"); + err = -EINVAL; + break; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("Drv-to-Drv adc data checksum error"); + err = -EINVAL; + break; + } + + r_threshold = test_params->r_drv_drv_threshold; + short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); + short_die_num -= max_sen_num; + if (short_die_num >= max_drv_num) { + ts_info("invalid short pad num:%d", + short_die_num + max_sen_num); + continue; + } + + /* TODO: j start position need recheck */ + self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); + if (self_capdata == 0xffff || self_capdata == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata); + continue; + } + + for (j = short_die_num + 1; j < max_drv_num; j++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); + + if (adc_signal < test_params->short_threshold) + continue; + + short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal); + if (short_r < r_threshold) { + master_pin_num = + map_die2pin(test_params, short_die_num + max_sen_num); + slave_pin_num = + map_die2pin(test_params, j + max_sen_num); + if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { + ts_info("WARNNING invalid pin"); + continue; + } + goodix_save_short_res(test_params, master_pin_num, + slave_pin_num, short_r); + ts_err("short circut:R=%dK,R_Threshold=%dK", + short_r, r_threshold); + ts_err("%s%d--%s%d shortcircut", + (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (master_pin_num & ~DRV_CHANNEL_FLAG), + (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (slave_pin_num & ~DRV_CHANNEL_FLAG)); + err = -EINVAL; + } + } + data_reg += size; + } + + kfree(data_buf); + return err; +} + +static int gdix_check_rx_rx_shortcircut(struct goodix_ts_test *ts_test, + u8 short_ch_num) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u16 adc_signal = 0; + u8 master_pin_num, slave_pin_num; + u8 *data_buf; + u32 data_reg; + struct ts_test_params *test_params = &ts_test->test_params; + int max_sen_num = test_params->params_info->max_sen_num; + u16 self_capdata, short_die_num = 0; + + size = 4 + max_sen_num * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* drv&drv shortcircut check */ + data_reg = test_params->params_info->sen_sen_selfcode_reg; + for (i = 0; i < short_ch_num; i++) { + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret) { + ts_err("Failed read Sen-to-Sen short rawdata"); + err = -EINVAL; + break; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("Sen-to-Sen adc data checksum error"); + err = -EINVAL; + break; + } + + r_threshold = test_params->r_sen_sen_threshold; + short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); + if (short_die_num >= max_sen_num) { + ts_info("invalid short pad num:%d", short_die_num); + continue; + } + + /* TODO: j start position need recheck */ + self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); + if (self_capdata == 0xffff || self_capdata == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata); + continue; + } + + for (j = short_die_num + 1; j < max_sen_num; j++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); + + if (adc_signal < test_params->short_threshold) + continue; + + short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal); + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, short_die_num); + slave_pin_num = map_die2pin(test_params, j); + if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { + ts_info("WARNNING invalid pin"); + continue; + } + goodix_save_short_res(test_params, master_pin_num, + slave_pin_num, short_r); + ts_err("short circut:R=%dK,R_Threshold=%dK", + short_r, r_threshold); + ts_err("%s%d--%s%d shortcircut", + (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (master_pin_num & ~DRV_CHANNEL_FLAG), + (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (slave_pin_num & ~DRV_CHANNEL_FLAG)); + err = -EINVAL; + } + } + data_reg += size; + } + + kfree(data_buf); + return err; +} + +static int gdix_check_tx_rx_shortcircut(struct goodix_ts_test *ts_test, + u8 short_ch_num) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u16 adc_signal = 0; + u8 master_pin_num, slave_pin_num; + u8 *data_buf = NULL; + u32 data_reg; + struct ts_test_params *test_params = &ts_test->test_params; + int max_drv_num = test_params->params_info->max_drv_num; + int max_sen_num = test_params->params_info->max_sen_num; + u16 self_capdata, short_die_num = 0; + + size = 4 + max_drv_num * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* drv&sen shortcircut check */ + data_reg = test_params->params_info->drv_sen_selfcode_reg; + for (i = 0; i < short_ch_num; i++) { + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret) { + ts_err("Failed read Drv-to-Sen short rawdata"); + err = -EINVAL; + break; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("Drv-to-Sen adc data checksum error"); + err = -EINVAL; + break; + } + + r_threshold = test_params->r_drv_sen_threshold; + short_die_num = le16_to_cpup((__le16 *)&data_buf[0]); + if (short_die_num >= max_sen_num) { + ts_info("invalid short pad num:%d", short_die_num); + continue; + } + + /* TODO: j start position need recheck */ + self_capdata = le16_to_cpup((__le16 *)&data_buf[2]); + if (self_capdata == 0xffff || self_capdata == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata); + continue; + } + + for (j = 0; j < max_drv_num; j++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[4 + j * 2]); + + if (adc_signal < test_params->short_threshold) + continue; + + short_r = (u32)cal_cha_to_cha_res(ts_test, self_capdata, adc_signal); + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, short_die_num); + slave_pin_num = map_die2pin(test_params, j + max_sen_num); + if (master_pin_num == 0xFF || slave_pin_num == 0xFF) { + ts_info("WARNNING invalid pin"); + continue; + } + goodix_save_short_res(test_params, master_pin_num, + slave_pin_num, short_r); + ts_err("short circut:R=%dK,R_Threshold=%dK", + short_r, r_threshold); + ts_err("%s%d--%s%d shortcircut", + (master_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (master_pin_num & ~DRV_CHANNEL_FLAG), + (slave_pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (slave_pin_num & ~DRV_CHANNEL_FLAG)); + err = -EINVAL; + } + } + data_reg += size; + } + + kfree(data_buf); + return err; +} + +#define SHORT_TYPE_FLAG ((uint16_t)1 << 15) +static int gdix_check_resistance_to_gnd(struct ts_test_params *test_params, + u16 adc_signal, u32 pos) +{ + long r = 0; + u16 r_th = 0, avdd_value = 0; + u16 chn_id_tmp = 0; + u8 pin_num = 0; + struct goodix_ts_test *ts_test = container_of(test_params, + struct goodix_ts_test, test_params); + int max_drv_num = test_params->params_info->max_drv_num; + int max_sen_num = test_params->params_info->max_sen_num; + + avdd_value = test_params->avdd_value; + if (adc_signal == 0 || adc_signal == 0x8000) + adc_signal |= 1; + + if ((adc_signal & 0x8000) == 0) { + /* short to GND */ + r = cal_cha_to_gnd_res(ts_test, adc_signal); + } else { + /* short to VDD */ + r = cal_cha_to_avdd_res(ts_test, adc_signal & ~0x8000, avdd_value); + } + + if (pos < max_drv_num) + r_th = test_params->r_drv_gnd_threshold; + else + r_th = test_params->r_sen_gnd_threshold; + + chn_id_tmp = pos; + if (chn_id_tmp < max_drv_num) + chn_id_tmp += max_sen_num; + else + chn_id_tmp -= max_drv_num; + + if (r < r_th) { + pin_num = map_die2pin(test_params, chn_id_tmp); + goodix_save_short_res(test_params, pin_num, + (adc_signal & 0x8000)? CHN_VDD : CHN_GND, r); + ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK", + (pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (pin_num & ~DRV_CHANNEL_FLAG), + (adc_signal & 0x8000) ? "VDD" : "GND", + r, r_th); + + return -EINVAL; + } + + return 0; +} + +static int gdix_check_gndvdd_shortcircut(struct goodix_ts_test *ts_test) +{ + int ret = 0, err = 0; + int size = 0, i = 0; + u16 adc_signal = 0; + u32 data_reg; + u8 *data_buf = NULL; + int max_drv_num = ts_test->test_params.params_info->max_drv_num; + int max_sen_num = ts_test->test_params.params_info->max_sen_num; + + size = (max_drv_num + max_sen_num) * 2 + 2; + data_buf = kzalloc(size, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + return -ENOMEM; + } + /* read diff code, diff code will be used to calculate + * resistance between channel and GND */ + data_reg = ts_test->test_params.params_info->diffcode_data_reg; + ret = ts_test_read(ts_test, data_reg, data_buf, size); + if (ret < 0) { + ts_err("Failed read to-gnd rawdata"); + err = -EINVAL; + goto err_out; + } + + if (checksum_cmp(data_buf, size, CHECKSUM_MODE_U8_LE)) { + ts_err("diff code checksum error"); + err = -EINVAL; + goto err_out; + } + + for (i = 0; i < max_drv_num + max_sen_num; i++) { + adc_signal = le16_to_cpup((__le16 *)&data_buf[i * 2]); + ret = gdix_check_resistance_to_gnd(&ts_test->test_params, + adc_signal, i); + if (ret != 0) { + ts_err("Resistance to-gnd/vdd short"); + err = ret; + } + } + +err_out: + kfree(data_buf); + return err; +} + +static int goodix_shortcircut_analysis(struct goodix_ts_test *ts_test) +{ + int ret; + int err = 0; + test_result_t test_result; + + ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_result_reg, + (u8 *)&test_result, sizeof(test_result)); + if (ret < 0) { + ts_err("Read TEST_RESULT_REG failed"); + return ret; + } + + if (checksum_cmp((u8 *)&test_result, sizeof(test_result), + CHECKSUM_MODE_U8_LE)) { + ts_err("shrot result checksum err"); + return -EINVAL; + } + + if (!(test_result.result & 0x0F)) { + ts_info(">>>>> No shortcircut"); + return 0; + } + ts_info("short flag 0x%02x, drv&drv:%d, sen&sen:%d, drv&sen:%d, drv/GNDVDD:%d, sen/GNDVDD:%d", + test_result.result, test_result.drv_drv_num, test_result.sen_sen_num, + test_result.drv_sen_num, test_result.drv_gnd_avdd_num, test_result.sen_gnd_avdd_num); + + if (test_result.drv_drv_num) + err |= gdix_check_tx_tx_shortcircut(ts_test, test_result.drv_drv_num); + if (test_result.sen_sen_num) + err |= gdix_check_rx_rx_shortcircut(ts_test, test_result.sen_sen_num); + if (test_result.drv_sen_num) + err |= gdix_check_tx_rx_shortcircut(ts_test, test_result.drv_sen_num); + if (test_result.drv_gnd_avdd_num || test_result.sen_gnd_avdd_num) + err |= gdix_check_gndvdd_shortcircut(ts_test); + + ts_info(">>>>> short check return 0x%x", err); + + return err; +} + +#define SHORT_FW_CMD_REG 0x10400 +static int send_test_cmd(struct goodix_ts_test *ts_test, + struct goodix_ts_cmd *cmd) +{ + int ret; + u32 reg = SHORT_FW_CMD_REG; + cmd->state = 0; + cmd->ack = 0; + goodix_append_checksum(&(cmd->buf[2]), cmd->len - 2, + CHECKSUM_MODE_U8_LE); + ret = ts_test_write(ts_test, reg, cmd->buf, cmd->len + 2); + if (ret < 0) + return ret; + usleep_range(10000, 11000); + return ret; +} + + +#define INSPECT_PARAM_CMD 0xAA +#define SHORT_TEST_FINISH_FLAG 0x88 +#define SHORT_TEST_THRESHOLD_REG 0x20402 +static void goodix_shortcircut_test(struct goodix_ts_test *ts_test) +{ + int ret = 0; + int retry; + u16 test_time; + u8 status; + int ic_type = ts_test->ts->bus->ic_type; + struct goodix_ts_cmd test_parm_cmd; + // u8 test_param[6]; + + ts_info("---------------------- short_test begin ----------------------"); + ret = goodix_short_test_prepare(ts_test); + if (ret < 0) { + ts_err("Failed enter short test mode"); + return; + } + + /* get short test time */ + ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_time_reg, (u8 *)&test_time, 2); + if (ret < 0) { + ts_err("Failed to get test_time, default %dms", DEFAULT_TEST_TIME_MS); + test_time = DEFAULT_TEST_TIME_MS; + } else { + if (ic_type == IC_TYPE_BERLIN_A) + test_time /= 10; + if (test_time > MAX_TEST_TIME_MS) { + ts_info("test time too long %d > %d", + test_time, MAX_TEST_TIME_MS); + test_time = MAX_TEST_TIME_MS; + } + ts_info("get test time %dms", test_time); + } + + /* start short test */ + if (ic_type == IC_TYPE_BERLIN_A) { + test_parm_cmd.len = 0x0A; + test_parm_cmd.cmd = INSPECT_PARAM_CMD; + test_parm_cmd.data[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; + test_parm_cmd.data[1] = (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; + test_parm_cmd.data[2] = ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; + test_parm_cmd.data[3] = (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; + test_parm_cmd.data[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; + test_parm_cmd.data[5] = (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; + ret = send_test_cmd(ts_test, &test_parm_cmd); + if (ret < 0) { + ts_err("send INSPECT_PARAM_CMD failed"); + return; + } + } else { + // test_param[0] = ts_test->test_params.params_info->dft_short_threshold & 0xFF; + // test_param[1] = (ts_test->test_params.params_info->dft_short_threshold >> 8) & 0xFF; + // test_param[2] = ts_test->test_params.params_info->short_diffcode_threshold & 0xFF; + // test_param[3] = (ts_test->test_params.params_info->short_diffcode_threshold >> 8) & 0xFF; + // test_param[4] = ts_test->test_params.params_info->short_test_dump_num & 0xFF; + // test_param[5] = (ts_test->test_params.params_info->short_test_dump_num >> 8) & 0xFF; + // ts_test_write(ts_test, SHORT_TEST_THRESHOLD_REG, test_param, sizeof(test_param)); + status = 0; + ts_test_write(ts_test, SHORT_TEST_RUN_REG, &status, 1); + } + + /* wait short test finish */ + msleep(test_time); + retry = 50; + while (retry--) { + ret = ts_test_read(ts_test, ts_test->test_params.params_info->short_test_status_reg, &status, 1); + if (!ret && status == SHORT_TEST_FINISH_FLAG) + break; + msleep(50); + } + if (retry < 0) { + ts_err("short test failed, status:0x%02x", status); + return; + } + + /* start analysis short result */ + ts_info("short_test finished, start analysis"); + ret = goodix_shortcircut_analysis(ts_test); + if (ret < 0) + ts_test->test_result[GTP_SHORT_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_PASS; +} + +#define GOODIX_CMD_RAWDATA 2 +#define GOODIX_TOUCH_EVENT 0x80 +static int goodix_cap_test_prepare(struct goodix_ts_test *ts_test) +{ + int ret; + struct goodix_ts_cmd temp_cmd; + + ts_info("cap test prepare IN"); + ts_test->test_result[GTP_CAP_TEST] = SYS_SOFTWARE_REASON; + ts_test->test_result[GTP_DELTA_TEST] = SYS_SOFTWARE_REASON; + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) + ts_test->test_result[GTP_SELFCAP_TEST] = SYS_SOFTWARE_REASON; + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) + ts_test->test_result[GTP_NOISE_TEST] = SYS_SOFTWARE_REASON; + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) + ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON; + + /* switch rawdata mode */ + temp_cmd.cmd = GOODIX_CMD_RAWDATA; + temp_cmd.len = 4; + ret = ts_test_send_cmd(ts_test, &temp_cmd); + if (ret < 0) + ts_err("Enter rawdata mode failed"); + + return ret; +} + +static int goodix_cap_test_finish(struct goodix_ts_test *ts_test) +{ + ts_info("cap_test finished"); + /* switch coor mode */ + ts_test_reset(ts_test, 100); + return 0; +} + +static int goodix_cache_rawdata(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + int retry; + u8 val; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num * drv_num; + u32 data_addr = ts_test->test_params.rawdata_addr; + u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + val = 0; + ret = ts_test_write(ts_test, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit"); + return -EAGAIN; + } + retry = 20; + while (retry--) { + usleep_range(5000, 5100); + ret = ts_test_read(ts_test, flag_addr, &val, 1); + if (!ret && (val & 0x80)) + break; + } + if (retry < 0) { + ts_err("rawdata is not ready val:0x%02x i:%d, exit", val, i); + return -EAGAIN; + } + + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->rawdata[i].data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + ts_test->rawdata[i].size = data_size; + goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->rawdata[i].data); + } + + return ret; +} + +static void goodix_cache_deltadata(struct goodix_ts_test *ts_test) +{ + u32 data_size; + int tx = ts_test->test_params.drv_num; + int i; + int j; + int max_val; + int raw; + int temp; + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + data_size = ts_test->rawdata[i].size; + if (data_size == 0) + continue; + for (j = 0; j < data_size; j++) { + raw = ts_test->rawdata[i].data[j]; + max_val = 0; + /* calcu delta with above node */ + if (j - tx >= 0) { + temp = ts_test->rawdata[i].data[j - tx]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + /* calcu delta with bellow node */ + if (j + tx < data_size) { + temp = ts_test->rawdata[i].data[j + tx]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + /* calcu delta with left node */ + if (j % tx) { + temp = ts_test->rawdata[i].data[j - 1]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + /* calcu delta with right node */ + if ((j + 1) % tx) { + temp = ts_test->rawdata[i].data[j + 1]; + temp = ABS(temp - raw); + max_val = MAX(max_val, temp); + } + ts_test->accord_arr[i].data[j] = max_val * 1000 / raw; + } + ts_test->accord_arr[i].size = data_size; + } +} + +static int goodix_cache_self_rawdata(struct goodix_ts_test *ts_test) +{ + int ret; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num + drv_num; + u32 data_addr = ts_test->test_params.self_rawdata_addr; + + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->self_rawdata.data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + ts_test->self_rawdata.size = data_size; + + return ret; +} + +static int goodix_cache_noisedata(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + int cnt; + int retry; + u8 val; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num * drv_num; + u32 data_addr = ts_test->test_params.noisedata_addr; + u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + + for (cnt = 0; cnt < NOISEDATA_TEST_TIMES; cnt++) { + val = 0; + ret = ts_test_write(ts_test, flag_addr, &val, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit"); + return -EAGAIN; + } + retry = 20; + while (retry--) { + usleep_range(5000, 5100); + ret = ts_test_read(ts_test, flag_addr, &val, 1); + if (!ret && (val & 0x80)) + break; + } + if (retry < 0) { + ts_err("noisedata is not ready val:0x%02x i:%d, exit", val, cnt); + return -EAGAIN; + } + + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->noisedata[cnt].data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + ts_test->noisedata[cnt].size = data_size; + goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->noisedata[cnt].data); + for (i = 0; i < data_size; i++) + ts_test->noisedata[cnt].data[i] = ABS(ts_test->noisedata[cnt].data[i]); + } + + return ret; +} + +static int goodix_cache_self_noisedata(struct goodix_ts_test *ts_test) +{ + int ret; + int i; + u32 sen_num = ts_test->test_params.sen_num; + u32 drv_num = ts_test->test_params.drv_num; + u32 data_size = sen_num + drv_num; + u32 data_addr = ts_test->test_params.self_noisedata_addr; + + ret = ts_test_read(ts_test, data_addr, + (u8 *)ts_test->self_noisedata.data, data_size * sizeof(s16)); + if (ret < 0) + return ret; + ts_test->self_noisedata.size = data_size; + for (i = 0; i < data_size; i++) { + ts_test->self_noisedata.data[i] = ABS(ts_test->self_noisedata.data[i]); + } + + return ret; +} + +static int goodix_analysis_rawdata(struct goodix_ts_test *ts_test) +{ + int i; + int j; + bool fail_flag = false; + int err_cnt = 0; + int times = TOTAL_FRAME_NUM; + s16 val; + u32 data_size = ts_test->rawdata[0].size; + + for (i = 0; i < times; i++) { + for (j = 0; j < data_size; j++) { + val = ts_test->rawdata[i].data[j]; + if (val < ts_test->test_params.min_limits[j]) { + fail_flag = true; + ts_test->open_res.beyond_min_limit_cnt[j]++; + } + if (val > ts_test->test_params.max_limits[j]) { + fail_flag = true; + ts_test->open_res.beyond_max_limit_cnt[j]++; + } + } + if (fail_flag) + err_cnt++; + fail_flag = false; + } + + if (err_cnt > 0) + ts_err("rawdata have %d frames out of range", err_cnt); + + err_cnt *= 100; + if (err_cnt > times * 100 * 9 / 10) + return -EINVAL; + + return 0; +} + +static int goodix_analysis_deltadata(struct goodix_ts_test *ts_test) +{ + int i; + int j; + int ret = 0; + s16 val; + u32 data_size = ts_test->accord_arr[0].size; + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + for (j = 0; j < data_size; j++) { + val = ts_test->accord_arr[i].data[j]; + if (val > ts_test->test_params.deviation_limits[j]) { + ts_test->open_res.beyond_accord_limit_cnt[j]++; + ret = -EINVAL; + } + } + } + + return ret; +} + +static int goodix_analysis_self_rawdata(struct goodix_ts_test *ts_test) +{ + int i; + s16 val; + u32 data_size = ts_test->self_rawdata.size; + + for (i = 0; i < data_size; i++) { + val = ts_test->self_rawdata.data[i]; + if (val < ts_test->test_params.self_min_limits[i] || + val > ts_test->test_params.self_max_limits[i]) { + ts_err("self_rawdata isn't in range, val:%d threshold:[%d,%d]", + val, ts_test->test_params.self_min_limits[i], + ts_test->test_params.self_max_limits[i]); + return -EINVAL; + } + } + + return 0; +} + +static int goodix_analysis_noisedata(struct goodix_ts_test *ts_test) +{ + int cnt; + int i; + bool fail_flag = false; + int err_cnt = 0; + int times = NOISEDATA_TEST_TIMES; + s16 val; + u32 data_size = ts_test->noisedata[0].size; + + for (cnt = 0; cnt < times; cnt++) { + for (i = 0; i < data_size; i++) { + val = ts_test->noisedata[cnt].data[i]; + if (val > ts_test->test_params.noise_threshold) + fail_flag = true; + } + if (fail_flag) + err_cnt++; + fail_flag = false; + } + + if (err_cnt > 0) + ts_err("noisedata have %d frames out of range", err_cnt); + + err_cnt *= 100; + if (err_cnt > times * 100 * 2 / 10) + return -EINVAL; + + return 0; +} + +static int goodix_analysis_self_noisedata(struct goodix_ts_test *ts_test) +{ + int i; + s16 val; + u32 data_size = ts_test->self_noisedata.size; + + for (i = 0; i < data_size; i++) { + val = ts_test->self_noisedata.data[i]; + if (val > ts_test->test_params.self_noise_threshold) { + ts_err("self noisedata isn't in range, val:%d threshold:[0,%d]", + val, ts_test->test_params.self_noise_threshold); + return -EINVAL; + } + } + + return 0; +} + +static void goodix_capacitance_test(struct goodix_ts_test *ts_test) +{ + int ret; + + ts_info("---------------------- cap_test begin ----------------------"); + ret = goodix_cap_test_prepare(ts_test); + if (ret < 0) { + ts_err("cap_test prepare failed, exit"); + goto exit; + } + ts_info("cap rawdata prepare OK"); + + /* obtain rawdata */ + ret = goodix_cache_rawdata(ts_test); + if (ret < 0) { + if (ret == -EAGAIN) { + ts_err("Capacitance exit"); + goto exit; + } else { + ts_err("Failed to read capdata"); + } + } else { + ts_info("get rawdata finish, start analysis"); + ret = goodix_analysis_rawdata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PASS; + } + + /* obtain delta_data */ + goodix_cache_deltadata(ts_test); + ts_info("get deltadata finish, start analysis"); + ret = goodix_analysis_deltadata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_PASS; + + /* obtain noisedata */ + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + ret = goodix_cache_noisedata(ts_test); + if (ret < 0) { + ts_err("Failed to read noisedata"); + } else { + ts_info("get noisedata finish, start analysis"); + ret = goodix_analysis_noisedata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_NOISE_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_NOISE_TEST] = GTP_TEST_PASS; + } + } + + /* obtain self_rawdata */ + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + ret = goodix_cache_self_rawdata(ts_test); + if (ret < 0) { + ts_err("Failed to read self_capdata"); + } else { + ts_info("get self_rawdata finish, start analysis"); + ret = goodix_analysis_self_rawdata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_TEST_PASS; + } + } + + + /* obtain self_noisedata */ + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + ret = goodix_cache_self_noisedata(ts_test); + if (ret < 0) { + ts_err("Failed to read self_noisedata"); + } else { + ts_info("get self_noisedata finish, start analysis"); + ret = goodix_analysis_self_noisedata(ts_test); + if (ret < 0) + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_PANEL_REASON; + else + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_TEST_PASS; + } + } + +exit: + goodix_cap_test_finish(ts_test); +} + +char *goodix_strncat(char *dest, char *src, size_t dest_size) +{ + size_t dest_len = 0; + + dest_len = strnlen(dest, dest_size); + return strncat(&dest[dest_len], src, dest_size - dest_len - 1); +} + +char *goodix_strncatint(char *dest, int src, char *format, size_t dest_size) +{ + char src_str[MAX_STR_LEN] = {0}; + + snprintf(src_str, MAX_STR_LEN, format, src); + return goodix_strncat(dest, src_str, dest_size); +} + +static void goodix_data_cal(s16 *data, size_t data_size, s16 *stat_result) +{ + int i = 0; + s16 avg = 0; + s16 min = 0; + s16 max = 0; + long long sum = 0; + + min = data[0]; + max = data[0]; + for (i = 0; i < data_size; i++) { + sum += data[i]; + if (max < data[i]) + max = data[i]; + if (min > data[i]) + min = data[i]; + } + avg = div_s64(sum, data_size); + stat_result[0] = avg; + stat_result[1] = max; + stat_result[2] = min; +} + +static void goodix_data_statistics(s16 *data, size_t data_size, + char *result, size_t res_size) +{ + s16 stat_value[3]; + + if (!data || !result) { + ts_err("parameters error please check *data and *result value"); + return; + } + + if (data_size <= 0 || res_size <= 0) { + ts_err("input parameter is illegva:data_size=%ld, res_size=%ld", + data_size, res_size); + return; + } + goodix_data_cal(data, data_size, stat_value); + + memset(result, 0, res_size); + snprintf(result, res_size, "[%d,%d,%d]", + stat_value[0], stat_value[1], stat_value[2]); + return; +} + +#ifdef SAVE_IN_CSV +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +static ssize_t fs_write(const void* buf, size_t size, struct file* fp) +{ + loff_t pos; + ssize_t len; + + pos = fp->f_pos; + len = kernel_write(fp, buf, size, &pos); + fp->f_pos = pos; + + return len; +} +#else +static ssize_t fs_write(const void* buf, size_t size, struct file* fp) +{ + mm_segment_t old_fs; + loff_t pos; + ssize_t len; + + pos = fp->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + len = vfs_write(fp, buf, size, &pos); + set_fs(old_fs); + fp->f_pos = pos; + + return len; +} +#endif + +static int goodix_save_test_config(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret = 0; + int i; + int bytes = 0; + char *data; + struct goodix_ic_config *cfg = &ts_test->test_config; + + if (cfg->len <= 0) { + ts_info("Can't find vaild test config"); + return 0; + } + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed"); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < cfg->len; i++) { + bytes += sprintf(&data[bytes], "0x%02x,", cfg->data[i]); + } + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("test config write failed"); + goto save_end; + } + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_header(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret; + int i; + int bytes = 0; + bool result = false; + char *data = NULL; + struct goodix_ts_core *ts = ts_test->ts; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed"); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "
\n"); + /* sava test result */ + for (i = 0; i < MAX_TEST_ITEMS; i++) { + if ((ts_test->test_result[i] > 0) && + (ts_test->test_result[i] != GTP_TEST_PASS)) { + result = true; + break; + } + } + if (result) + bytes += sprintf(&data[bytes], "NG\n"); + else + bytes += sprintf(&data[bytes], "OK\n"); + bytes += sprintf(&data[bytes], "GT%s\n", + ts->fw_version.patch_pid); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->ts->fw_version.sensor_id); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("header write failed"); + goto save_end; + } + bytes = 0; + /* save test config */ + ret = goodix_save_test_config(ts_test, fp); + if (ret < 0) { + ts_err("save test config failed"); + goto save_end; + } + + bytes += sprintf(&data[bytes], "
\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("header write failed"); + goto save_end; + } + bytes = 0; + + /* item list */ + bytes += sprintf(&data[bytes], "\n"); + if (ts_test->test_result[GTP_CAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_DELTA_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_DELTA_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_NOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SHORT_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("item list write failed"); + goto save_end; + } + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_limits(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret; + int i; + int bytes = 0; + char *data = NULL; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int chn1; + int chn2; + int r; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + + /* save short result */ + if (ts_test->test_result[GTP_SHORT_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->short_res.short_num); + for (i = 0; i < ts_test->short_res.short_num; i++) { + chn1 = ts_test->short_res.short_msg[4 * i]; + chn2 = ts_test->short_res.short_msg[4 * i + 1]; + r = (ts_test->short_res.short_msg[4 * i + 2] << 8) + + ts_test->short_res.short_msg[4 * i + 3]; + if (chn1 == CHN_VDD) + bytes += sprintf(&data[bytes], "\n", r); + else if (chn2 == CHN_GND) + bytes += sprintf(&data[bytes], + "Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n", r); + else if (chn2 & DRV_CHANNEL_FLAG) + bytes += sprintf(&data[bytes], + "Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + else + bytes += sprintf(&data[bytes], + "Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("short res write fail."); + goto save_end; + } + bytes = 0; + } + + /* rawdata max limit */ + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + TOTAL_FRAME_NUM); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.max_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondRawdataUpperLimit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_max_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata limit write failed"); + goto save_end; + } + bytes = 0; + + /* rawdata min limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.min_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondRawdataLower limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_min_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata limit write failed"); + goto save_end; + } + bytes = 0; + + /* Max Accord limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.deviation_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondAccordLimitCnt */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_accord_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata limit write failed"); + goto save_end; + } + bytes = 0; + + /* save noise limit */ + if (ts_test->test_result[GTP_NOISE_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + NOISEDATA_TEST_TIMES); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->test_params.noise_threshold); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("noise limit write failed"); + goto save_end; + } + bytes = 0; + } + + /* save self rawdata limit */ + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "1\n"); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx + rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.self_max_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if ((tx + rx) % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx + rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.self_min_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if ((tx + rx) % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("self rawdata limit write failed"); + goto save_end; + } + bytes = 0; + } + + /* save selfnoise limit */ + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "1\n"); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->test_params.self_noise_threshold); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("raw limit write failed"); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("limit write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_rawdata(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int i; + int j; + int ret; + int bytes = 0; + s16 stat_result[3]; + char *data = NULL; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int len = tx * rx; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + goodix_data_cal(ts_test->rawdata[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->rawdata[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + goodix_data_cal(ts_test->accord_arr[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->accord_arr[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata write fail."); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("rawdata write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_noise_data(struct goodix_ts_test *ts_test, struct file *fp) +{ + int i; + int j; + int ret = 0; + int bytes = 0; + s16 stat_result[3]; + char *data = NULL; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int len = tx * rx; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < NOISEDATA_TEST_TIMES; i++) { + goodix_data_cal(ts_test->noisedata[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->noisedata[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("noisedata write fail."); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("noisedata write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_self_data(struct goodix_ts_test *ts_test, + struct file *fp, s16 *src_data, u8 *title, int len) +{ + int i; + int ret = 0; + s32 bytes = 0; + char *data; + s16 stat_result[3]; + int tx = ts_test->test_params.drv_num; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "<%s>\n",title); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata write fail."); + goto save_end; + } + bytes = 0; + + goodix_data_cal(src_data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + len, stat_result[1], stat_result[2], stat_result[0]); + for (i = 0; i < len; i++) { + bytes += sprintf(&data[bytes], "%d,", src_data[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if (len % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n",title); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("rawdata write fail."); + +save_end: + kfree(data); + return ret; +} + +static int goodix_save_data(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret; + int bytes = 0; + char *data = NULL; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata record lable failed"); + goto save_end; + } + bytes = 0; + + ret = goodix_save_rawdata(ts_test, fp); + if (ret < 0) + goto save_end; + + if (ts_test->test_result[GTP_NOISE_TEST]) { + ret = goodix_save_noise_data(ts_test, fp); + if (ret < 0) + goto save_end; + } + + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + ret = goodix_save_self_data(ts_test, fp, ts_test->self_rawdata.data, + "selfDataRecord", ts_test->self_rawdata.size); + if (ret < 0) + goto save_end; + } + + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + ret = goodix_save_self_data(ts_test, fp, ts_test->self_noisedata.data, + "selfDiffDataRecord", ts_test->self_noisedata.size); + if (ret < 0) + goto save_end; + } + + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("rawdata data record lable fail."); + +save_end: + kfree(data); + return ret; +} + +/* save end tag in csv file */ +static int goodix_save_tail(struct goodix_ts_test *ts_test, + struct file *fp) +{ + int ret = 0; + int bytes = 0; + char *data = NULL; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (!data) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "
\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("tail write failed"); + + kfree(data); + return ret; +} + +static void goodix_save_result_data(struct goodix_ts_test *ts_test) +{ + int ret = 0; + char save_path[100]; + struct file *fp = NULL; + + /* format result file */ + sprintf(save_path, GOODIX_RESULT_SAVE_PATH); + ts_info("save result IN, file_name:%s", save_path); + + fp = filp_open(save_path, O_CREAT | O_WRONLY | O_TRUNC, 0666); + if (IS_ERR(fp)) { + ts_err("create file:%s failed, fp:%ld", save_path, PTR_ERR(fp)); + return; + } + + /* save header */ + ret = goodix_save_header(ts_test, fp); + if (ret < 0) + goto save_end; + + /* save limits */ + ret = goodix_save_limits(ts_test, fp); + if (ret < 0) + goto save_end; + + /* save data */ + ret = goodix_save_data(ts_test, fp); + if (ret < 0) + goto save_end; + + /* save tail */ + ret = goodix_save_tail(ts_test, fp); + if (ret < 0) + goto save_end; + + ts_info("the test result save in %s", save_path); +save_end: + filp_close(fp, NULL); +} +#endif // SAVE_IN_CSV + +static void goodix_put_test_result(struct goodix_ts_test *ts_test, + struct ts_rawdata_info *info) +{ + int i; + bool have_bus_error = false; + bool have_panel_error = false; + char statistics_data[STATISTICS_DATA_LEN] = {0}; + struct goodix_ts_core *ts = ts_test->ts; + + ts_info("put test result IN"); + + info->buff[0] = ts_test->test_params.sen_num; + info->buff[1] = ts_test->test_params.drv_num; + info->used_size = 2; + /* save rawdata to info->buff, only one frame */ + if (ts_test->rawdata[0].size) { + for (i = 0; i < ts_test->rawdata[0].size; i++) + info->buff[info->used_size + i] = ts_test->rawdata[0].data[i]; + info->used_size += ts_test->rawdata[0].size; + } + + /* save noisedata to info->buff */ + if (ts_test->noisedata[0].size) { + for (i = 0; i < ts_test->noisedata[0].size; i++) + info->buff[info->used_size + i] = ts_test->noisedata[0].data[i]; + info->used_size += ts_test->noisedata[0].size; + } + + /* save self_noisedata to info->buff */ + if (ts_test->self_noisedata.size) { + for (i = 0; i < ts_test->self_noisedata.size; i++) + info->buff[info->used_size + i] = ts_test->self_noisedata.data[i]; + info->used_size += ts_test->self_noisedata.size; + } + + /* save self_rawdata to info->buff */ + if (ts_test->self_rawdata.size) { + for (i = 0; i < ts_test->self_rawdata.size; i++) + info->buff[info->used_size + i] = ts_test->self_rawdata.data[i]; + info->used_size += ts_test->self_rawdata.size; + } + + /* check if there have bus error */ + for (i = 0; i < MAX_TEST_ITEMS; i++) { + if (ts_test->test_result[i] == SYS_SOFTWARE_REASON) + have_bus_error = true; + else if (ts_test->test_result[i] == GTP_PANEL_REASON) + have_panel_error = true; + } + ts_info("Have bus error:%d", have_bus_error); + if (have_bus_error || have_panel_error) + goodix_strncat(ts_test->test_info, "[FAIL]-", TS_RAWDATA_RESULT_MAX); + else + goodix_strncat(ts_test->test_info, "[PASS]-", TS_RAWDATA_RESULT_MAX); + + if (have_bus_error) + goodix_strncat(ts_test->test_info, "0F-", TS_RAWDATA_RESULT_MAX); + else + goodix_strncat(ts_test->test_info, "0P-", TS_RAWDATA_RESULT_MAX); + + for (i = 0; i < MAX_TEST_ITEMS; i++) { + /* if have tested, show result */ + if (ts_test->test_result[i]) { + if (GTP_TEST_PASS == ts_test->test_result[i]) + goodix_strncatint(ts_test->test_info, i, "%dP-", + TS_RAWDATA_RESULT_MAX); + else + goodix_strncatint(ts_test->test_info, i, "%dF-", + TS_RAWDATA_RESULT_MAX); + } + } + + /* calculate rawdata min avg max value*/ + if (ts_test->rawdata[0].size) { + goodix_data_statistics( + ts_test->rawdata[0].data, + ts_test->rawdata[0].size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable rawdata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + + /* calculate noisedata min avg max value*/ + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + if (ts_test->noisedata[0].size) { + goodix_data_statistics( + ts_test->noisedata[0].data, + ts_test->noisedata[0].size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable noisedata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + } + + /* calculate self_rawdata min avg max value*/ + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + if (ts_test->self_rawdata.size) { + goodix_data_statistics( + ts_test->self_rawdata.data, + ts_test->self_rawdata.size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable self_rawdata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + } + + /* calculate self_noisedata min avg max value*/ + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + if (ts_test->self_noisedata.size) { + goodix_data_statistics( + ts_test->self_noisedata.data, + ts_test->self_noisedata.size, + statistics_data, + STATISTICS_DATA_LEN); + goodix_strncat(ts_test->test_info, statistics_data, + TS_RAWDATA_RESULT_MAX); + } else { + ts_err("NO valiable self_noisedata"); + goodix_strncat(ts_test->test_info, "[0,0,0]", + TS_RAWDATA_RESULT_MAX); + } + } + + goodix_strncat(ts_test->test_info, "-GT", + TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, ts->fw_version.patch_pid, + TS_RAWDATA_RESULT_MAX); + goodix_strncat(ts_test->test_info, "\n", + TS_RAWDATA_RESULT_MAX); + strncpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1); + +#ifdef SAVE_IN_CSV + /* save result to file */ + goodix_save_result_data(ts_test); +#endif +} + +static int goodix_do_inspect(struct goodix_ts_core *cd, struct ts_rawdata_info *info) +{ + int ret; + struct goodix_ts_test *ts_test = NULL; + + if (!cd || !info) { + ts_err("core_data or info is NULL"); + return -ENODEV; + } + + ts_test = kzalloc(sizeof(*ts_test), GFP_KERNEL); + if (!ts_test) { + ts_err("Failed to alloc mem"); + return -ENOMEM; + } + + ts_test->ts = cd; + ret = goodix_tptest_prepare(ts_test); + if (ret < 0) { + ts_err("Failed to prepare TP test, exit"); + strncpy(info->result, "[FAIL]-0F-software reason\n", + TS_RAWDATA_RESULT_MAX - 1); + goto exit_finish; + } + ts_info("TP test prepare OK"); + + goodix_capacitance_test(ts_test); /* 1F 3F 6F 7F test */ + if (ts_test->test_params.test_items[GTP_SHORT_TEST]) + goodix_shortcircut_test(ts_test); /* 5F test */ + goodix_put_test_result(ts_test, info); + goodix_tptest_finish(ts_test); + +exit_finish: + kfree(ts_test); + return ret; +} + +/* show rawdata */ +static ssize_t goodix_ts_get_rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct ts_rawdata_info *info = NULL; + struct goodix_ts_core *cd = dev_get_drvdata(dev); + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("Failed to alloc rawdata info memory"); + return -ENOMEM; + } + + goodix_do_inspect(cd, info); + + ret = snprintf(buf, PAGE_SIZE, "resultInfo: %s", info->result); + + kfree(info); + return ret; +} + +static DEVICE_ATTR(get_rawdata, S_IRUGO, goodix_ts_get_rawdata_show, NULL); + +int inspect_module_init(void) +{ + int ret; + struct kobject *def_kobj = goodix_get_default_kobj(); + + /* create sysfs */ + ret = sysfs_create_file(def_kobj, &dev_attr_get_rawdata.attr); + if (ret < 0) { + ts_err("create sysfs of get_rawdata failed"); + goto err_out; + } + + module_initialized = true; + ts_info("inspect module init success"); + return 0; + +err_out: + ts_err("inspect module init failed!"); + return ret; +} + +void inspect_module_exit(void) +{ + struct kobject *def_kobj = goodix_get_default_kobj(); + + ts_info("inspect module exit"); + if (!module_initialized) + return; + + sysfs_remove_file(def_kobj, &dev_attr_get_rawdata.attr); + module_initialized = false; +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/goodix_ts_tools.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_tools.c old mode 100755 new mode 100644 similarity index 75% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/goodix_ts_tools.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_tools.c index 8021e260dfb9..75570c8836d6 --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_ext/goodix_ts_tools.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_tools.c @@ -45,30 +45,13 @@ static const u16 goodix_tools_ver = ((GOODIX_TOOLS_VER_MAJOR << 8) + #define GTP_READ_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK) #define GTP_ESD_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 8) #define GTP_TOOLS_VER (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK) +#define GTP_TOOLS_CTRL_SYNC (_IOW(GOODIX_TS_IOC_MAGIC, 10, u8) & NEGLECT_SIZE_MASK) -#define GOODIX_TS_IOC_MAXNR 10 #define MAX_BUF_LENGTH (16*1024) #define IRQ_FALG (0x01 << 2) #define I2C_MSG_HEAD_LEN 20 -/* - * struct goodix_tools_data - goodix tools data message used in sync read - * @data: The buffer into which data is written - * @reg_addr: Slave device register start address to start read data - * @length: Number of data bytes in @data being read from slave device - * @filled: When buffer @data be filled will set this flag with 1, outhrwise 0 - * @list_head:Eonnet every goodix_tools_data struct into a list - */ - -struct goodix_tools_data { - u32 reg_addr; - u32 length; - u8 *data; - bool filled; - struct list_head list; -}; - /* * struct goodix_tools_dev - goodix tools device struct * @ts_core: The core data struct of ts driver @@ -188,64 +171,6 @@ err_out: return ret; } -/* read data from i2c synchronous, - * return data length when success, otherwise return < 0 - */ -static int sync_read(struct goodix_tools_dev *dev, void __user *arg) -{ - int ret = 0; - u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; - struct goodix_tools_data tools_data; - - ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); - if (ret) { - ts_err("Copy data from user failed"); - return -EFAULT; - } - tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) - + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); - tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) - + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); - tools_data.filled = 0; - if (tools_data.length > MAX_BUF_LENGTH) { - ts_err("buffer too long:%d > %d", - tools_data.length, MAX_BUF_LENGTH); - return -EINVAL; - } - tools_data.data = kzalloc(tools_data.length, GFP_KERNEL); - if (!tools_data.data) { - ts_err("Alloc memory failed"); - return -ENOMEM; - } - - mutex_lock(&dev->mutex); - list_add_tail(&tools_data.list, &dev->head); - mutex_unlock(&dev->mutex); - /* wait queue will timeout after 1 seconds */ - wait_event_interruptible_timeout(dev->wq, tools_data.filled == 1, HZ * 3); - - mutex_lock(&dev->mutex); - list_del(&tools_data.list); - mutex_unlock(&dev->mutex); - if (!tools_data.filled) { - ret = -EAGAIN; - ts_err("Wait queue timeout"); - goto out; - } - - ret = copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, - tools_data.data, tools_data.length); - if (ret) { - ret = -EFAULT; - ts_err("Copy_to_user failed"); - goto out; - } - ret = tools_data.length; -out: - kfree(tools_data.data); - return ret; -} - /* write data to i2c asynchronous, * success return bytes write, else return <= 0 */ @@ -351,11 +276,6 @@ static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, ts_err("Bad magic num:%c", _IOC_TYPE(cmd)); return -ENOTTY; } - if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) { - ts_err("Bad cmd num:%d > %d", - _IOC_NR(cmd), GOODIX_TS_IOC_MAXNR); - return -ENOTTY; - } switch (cmd & NEGLECT_SIZE_MASK) { case GTP_IRQ_ENABLE: @@ -425,14 +345,7 @@ static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, ts_err("Async data read failed"); break; case GTP_SYNC_READ: - if (filp->f_flags & O_NONBLOCK) { - ts_err("Goodix tools now worked in sync_bus mode"); - ret = -EAGAIN; - goto err_out; - } - ret = sync_read(dev, (void __user *)arg); - if (ret < 0) - ts_err("Sync data read failed"); + ts_info("unsupport sync read"); break; case GTP_ASYNC_WRITE: ret = async_write(dev, (void __user *)arg); @@ -445,6 +358,10 @@ static long goodix_tools_ioctl(struct file *filp, unsigned int cmd, if (ret) ts_err("failed copy driver version info to user"); break; + case GTP_TOOLS_CTRL_SYNC: + ts_core->tools_ctrl_sync = !!arg; + ts_info("set tools ctrl sync %d", ts_core->tools_ctrl_sync); + break; default: ts_info("Invalid cmd"); ret = -ENOTTY; @@ -489,41 +406,13 @@ static int goodix_tools_release(struct inode *inode, struct file *filp) { int ret = 0; /* when the last close this dev node unregister the module */ + goodix_tools_dev->ts_core->tools_ctrl_sync = false; atomic_set(&goodix_tools_dev->in_use, 0); goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); ret = goodix_unregister_ext_module(&goodix_tools_dev->module); return ret; } -/** - * goodix_tools_module_irq - goodix tools Irq handle - * This functions is excuted when interrupt happended - * - * @core_data: pointer to touch core data - * @module: pointer to goodix_ext_module struct - * return: EVT_CONTINUE let other module handle this irq - */ -static int goodix_tools_module_irq(struct goodix_ts_core *core_data, - struct goodix_ext_module *module) -{ - struct goodix_tools_dev *dev = module->priv_data; - const struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; - struct goodix_tools_data *tools_data, *next; - - mutex_lock(&dev->mutex); - if (!list_empty(&dev->head)) { - list_for_each_entry_safe(tools_data, next, &dev->head, list) { - if (!hw_ops->read(core_data, tools_data->reg_addr, - tools_data->data, tools_data->length)) { - tools_data->filled = 1; - } - } - wake_up(&dev->wq); - } - mutex_unlock(&dev->mutex); - return EVT_CONTINUE; -} - static int goodix_tools_module_init(struct goodix_ts_core *core_data, struct goodix_ext_module *module) { @@ -566,7 +455,6 @@ static struct miscdevice goodix_tools_miscdev = { }; static struct goodix_ext_module_funcs goodix_tools_module_funcs = { - .irq_event = goodix_tools_module_irq, .init = goodix_tools_module_init, .exit = goodix_tools_module_exit, }; @@ -576,7 +464,7 @@ static struct goodix_ext_module_funcs goodix_tools_module_funcs = { * * return: 0 success, else failed */ -static int __init goodix_tools_init(void) +int goodix_tools_init(void) { int ret; @@ -601,20 +489,15 @@ static int __init goodix_tools_init(void) ret = misc_register(&goodix_tools_miscdev); if (ret) ts_err("Debug tools miscdev register failed"); + else + ts_info("Debug tools miscdev register success"); return ret; } -static void __exit goodix_tools_exit(void) +void goodix_tools_exit(void) { misc_deregister(&goodix_tools_miscdev); kfree(goodix_tools_dev); - ts_info("Goodix tools miscdev exit"); + ts_info("Debug tools miscdev exit"); } - -late_initcall(goodix_tools_init); -module_exit(goodix_tools_exit); - -MODULE_DESCRIPTION("Goodix tools Module"); -MODULE_AUTHOR("Goodix, Inc."); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_ts_utils.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_utils.c old mode 100755 new mode 100644 similarity index 70% rename from drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_ts_utils.c rename to drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_utils.c index 92c18024c89b..7080aa3f713a --- a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_core/goodix_ts_utils.c +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_utils.c @@ -16,6 +16,8 @@ */ #include "goodix_ts_core.h" +bool debug_log_flag = false; + /***************************************************************************** * goodix_append_checksum * @summary @@ -119,3 +121,59 @@ u32 goodix_get_file_config_id(u8 *ic_config) return 0; return le32_to_cpup((__le32 *)&ic_config[CONFIG_ID_OFFSET]); } + +/* matrix transpose */ +void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data) +{ + s16 *temp_buf = NULL; + int size = tx * rx; + int i; + int j; + int col; + + temp_buf = kcalloc(size, sizeof(s16), GFP_KERNEL); + if (!temp_buf) { + ts_err("malloc failed"); + return; + } + + for (i = 0, j = 0, col = 0; i < size; i++) { + temp_buf[i] = data[j++ * rx + col]; + if (j == tx) { + j = 0; + col++; + } + } + + memcpy(data, temp_buf, size * sizeof(s16)); + kfree(temp_buf); +} + +/* get ic type */ +int goodix_get_ic_type(struct device_node *node) +{ + const char *name_tmp; + int ret; + + ret = of_property_read_string(node, "compatible", &name_tmp); + if (ret < 0) { + ts_err("get compatible failed"); + return ret; + } + + if (strstr(name_tmp, "9897")) { + ts_info("ic type is BerlinA"); + ret = IC_TYPE_BERLIN_A; + } else if (strstr(name_tmp, "9966") || strstr(name_tmp, "7986")) { + ts_info("ic type is BerlinB"); + ret = IC_TYPE_BERLIN_B; + } else if (strstr(name_tmp, "9916")) { + ts_info("ic type is BerlinD"); + ret = IC_TYPE_BERLIN_D; + } else { + ts_info("can't find valid ic_type"); + ret = -EINVAL; + } + + return ret; +}