diff --git a/drivers/input/touchscreen/synaptics_mmi_class/Android.mk b/drivers/input/touchscreen/synaptics_mmi_class/Android.mk new file mode 100644 index 000000000000..4088d97f685f --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/Android.mk @@ -0,0 +1,31 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) +KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_dsx_fw_update.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_dsx_rmi_dev.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_dsx_test_reporting.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/Kbuild b/drivers/input/touchscreen/synaptics_mmi_class/Kbuild new file mode 100644 index 000000000000..78b150a05f80 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_mmi_class + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) +EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI -DCONFIG_PANEL_NOTIFICATIONS + +synaptics_mmi-objs = synaptics_dsx_i2c.o synaptics_mmi_class.o synaptics_dsx_instance.o + +obj-m += synaptics_mmi.o +obj-m += synaptics_dsx_fw_update.o +obj-m += synaptics_dsx_rmi_dev.o +obj-m += synaptics_dsx_test_reporting.o + +endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_fw_update.c new file mode 100644 index 000000000000..dab482b0cc19 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_fw_update.c @@ -0,0 +1,4398 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_i2c.h" + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define IMAGE_HEADER_VERSION_05 0x05 +#define IMAGE_HEADER_VERSION_06 0x06 +#define IMAGE_HEADER_VERSION_10 0x10 + +#define IMAGE_AREA_OFFSET 0x100 +#define LOCKDOWN_SIZE 0x50 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 +#define V5V6_CONFIG_ID_SIZE 4 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_NUMBER_OFFSET 0 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_PROPERTIES_2_OFFSET 4 +#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5 +#define V6_BLOCK_NUMBER_OFFSET 0 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define V7_CONFIG_ID_SIZE 32 + +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 + +#define V7_PARTITION_SUPPORT_BYTES 4 + +#define F35_ERROR_CODE_OFFSET 0 +#define F35_CHUNK_NUM_LSB_OFFSET 0 +#define F35_CHUNK_NUM_MSB_OFFSET 1 +#define F35_CHUNK_DATA_OFFSET 2 +#define F35_CHUNK_COMMAND_OFFSET 18 + +#define F35_CHUNK_SIZE 16 +#define F35_ERASE_ALL_WAIT_MS 3000 +#define F35_RESET_WAIT_MS 250 + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define INT_DISABLE_WAIT_MS 20 +#define ENTER_FLASH_PROG_WAIT_MS 20 + +#define LOGDEV (fwu->dev) +#define SYSFS_KOBJ (&rmi4_data->i2c_client->dev.kobj) + +static int fwu_do_reflash(struct synaptics_rmi4_fwu_handle *fwu); + +static int fwu_recovery_check_status(struct synaptics_rmi4_fwu_handle *fwu); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t fwu_sysfs_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum flash_area { + NONE = 0, + UI_FIRMWARE, + UI_CONFIG, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +enum config_area { + UI_CONFIG_AREA = 0, + PM_CONFIG_AREA, + BL_CONFIG_AREA, + DP_CONFIG_AREA, + FLASH_CONFIG_AREA, +}; + +enum v7_status { + SUCCESS = 0x00, + DEVICE_NOT_IN_BOOTLOADER_MODE, + INVALID_PARTITION, + INVALID_COMMAND, + INVALID_BLOCK_OFFSET, + INVALID_TRANSFER, + NOT_ERASED, + FLASH_PROGRAMMING_KEY_INCORRECT, + BAD_PARTITION_TABLE, + CHECKSUM_FAILED, + FLASH_HARDWARE_FAILURE = 0x1f, +}; + +enum v7_partition_id { + BOOTLOADER_PARTITION = 0x01, + DEVICE_CONFIG_PARTITION, + FLASH_CONFIG_PARTITION, + MANUFACTURING_BLOCK_PARTITION, + GUEST_SERIALIZATION_PARTITION, + GLOBAL_PARAMETERS_PARTITION, + CORE_CODE_PARTITION, + CORE_CONFIG_PARTITION, + GUEST_CODE_PARTITION, + DISPLAY_CONFIG_PARTITION, +}; + +enum v7_flash_command { + CMD_V7_IDLE = 0x00, + CMD_V7_ENTER_BL, + CMD_V7_READ, + CMD_V7_WRITE, + CMD_V7_ERASE, + CMD_V7_ERASE_AP, + CMD_V7_SENSOR_ID, +}; + +enum v5v6_flash_command { + CMD_V5V6_IDLE = 0x0, + CMD_V5V6_WRITE_FW = 0x2, + CMD_V5V6_ERASE_ALL = 0x3, + CMD_V5V6_WRITE_LOCKDOWN = 0x4, + CMD_V5V6_READ_CONFIG = 0x5, + CMD_V5V6_WRITE_CONFIG = 0x6, + CMD_V5V6_ERASE_UI_CONFIG = 0x7, + CMD_V5V6_ERASE_BL_CONFIG = 0x9, + CMD_V5V6_ERASE_DISP_CONFIG = 0xa, + CMD_V5V6_ERASE_GUEST_CODE = 0xb, + CMD_V5V6_WRITE_GUEST_CODE = 0xc, + CMD_V5V6_ENABLE_FLASH_PROG = 0xf, +}; + +enum flash_command { + CMD_IDLE = 0, + CMD_WRITE_FW, + CMD_WRITE_CONFIG, + CMD_WRITE_LOCKDOWN, + CMD_WRITE_GUEST_CODE, + CMD_READ_CONFIG, + CMD_ERASE_ALL, + CMD_ERASE_UI_FIRMWARE, + CMD_ERASE_UI_CONFIG, + CMD_ERASE_BL_CONFIG, + CMD_ERASE_DISP_CONFIG, + CMD_ERASE_FLASH_CONFIG, + CMD_ERASE_GUEST_CODE, + CMD_ENABLE_FLASH_PROG, +}; + +enum f35_flash_command { + CMD_F35_IDLE = 0x0, + CMD_F35_RESERVED = 0x1, + CMD_F35_WRITE_CHUNK = 0x2, + CMD_F35_ERASE_ALL = 0x3, + CMD_F35_RESET = 0x10, +}; + +enum container_id { + TOP_LEVEL_CONTAINER = 0, + UI_CONTAINER, + UI_CONFIG_CONTAINER, + BL_CONTAINER, + BL_IMAGE_CONTAINER, + BL_CONFIG_CONTAINER, + BL_LOCKDOWN_INFO_CONTAINER, + PERMANENT_CONFIG_CONTAINER, + GUEST_CODE_CONTAINER, + BL_PROTOCOL_DESCRIPTOR_CONTAINER, + UI_PROTOCOL_DESCRIPTOR_CONTAINER, + RMI_SELF_DISCOVERY_CONTAINER, + RMI_PAGE_CONTENT_CONTAINER, + GENERAL_INFORMATION_CONTAINER, + DEVICE_CONFIG_CONTAINER, + FLASH_CONFIG_CONTAINER, + GUEST_SERIALIZATION_CONTAINER, + GLOBAL_PARAMETERS_CONTAINER, + CORE_CODE_CONTAINER, + CORE_CONFIG_CONTAINER, + DISPLAY_CONFIG_CONTAINER, +}; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; +#endif + +static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + __ATTR(dorecovery, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_do_recovery_store), + __ATTR(writeconfig, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_write_config_store), + __ATTR(readconfig, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_read_config_store), + __ATTR(configarea, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_config_area_store), + __ATTR(imagename, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_image_name_store), + __ATTR(imagesize, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_image_size_store), + __ATTR(blocksize, S_IRUGO, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, S_IRUGO, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, S_IRUGO, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, S_IRUGO, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, S_IRUGO, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, S_IRUGO, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(guestcodeblockcount, S_IRUGO, + fwu_sysfs_guest_code_block_count_show, + synaptics_rmi4_store_error), + __ATTR(writeguestcode, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_write_guest_code_store), +#endif + __ATTR(doreflash, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_do_reflash_store), + __ATTR(forcereflash, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_force_reflash_store), +}; + +#include + +static struct device_attribute erase_attr[] = { + __ATTR(erase_all, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_erase_store), +}; + +static inline void sema_clear(struct semaphore *sem) +{ + do { + } while (!down_trylock(sem)); +} + +static unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +static int fwu_allocate_read_config_buf( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int count) +{ + if (count > fwu->read_config_buf_size) { + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(count, GFP_KERNEL); + if (!fwu->read_config_buf) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = count; + } + return 0; +} + +static void fwu_compare_partition_tables(struct synaptics_rmi4_fwu_handle *fwu) +{ + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) { + fwu->new_partition_table = true; + return; + } + + if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) { + fwu->new_partition_table = true; + return; + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) { + fwu->new_partition_table = true; + return; + } + } + + dev_dbg(LOGDEV, "%s: same partition table\n", __func__); + fwu->new_partition_table = false; + return; +} + +static void fwu_parse_partition_table( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *partition_table, + struct block_count *blkcount, + struct physical_address *phyaddr) +{ + unsigned char ii; + unsigned char index; + unsigned char offset; + unsigned short partition_length; + unsigned short physical_address; + struct partition_table *ptable; + + for (ii = 0; ii < fwu->partitions; ii++) { + index = ii * 8 + 2; + ptable = (struct partition_table *)&partition_table[index]; + partition_length = ptable->partition_length_15_8 << 8 | + ptable->partition_length_7_0; + physical_address = ptable->start_physical_address_15_8 << 8 | + ptable->start_physical_address_7_0; + dev_dbg(LOGDEV, + "%s: Partition entry %d:\n", + __func__, ii); + for (offset = 0; offset < 8; offset++) { + dev_dbg(LOGDEV, + "%s: 0x%02x\n", + __func__, + partition_table[index + offset]); + } + switch (ptable->partition_id) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + dev_dbg(LOGDEV, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + dev_dbg(LOGDEV, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + dev_dbg(LOGDEV, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + dev_dbg(LOGDEV, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + dev_dbg(LOGDEV, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + dev_dbg(LOGDEV, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + dev_dbg(LOGDEV, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + dev_dbg(LOGDEV, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10_bl_container( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = (fwu->img.bootloader.size - 4) / 4; + + for (ii = 1; ii <= num_of_containers; ii++) { + addr = le_to_uint(fwu->img.bootloader.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + fwu->img.bl_config.data = content; + fwu->img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + fwu->img.lockdown.data = content; + fwu->img.lockdown.size = length; + break; + default: + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10(struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const unsigned char *image; + const unsigned char *content; + struct container_descriptor *descriptor; + struct image_header_10 *header; + + image = fwu->image; + header = (struct image_header_10 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + /* address of top level container */ + offset = le_to_uint(header->top_level_container_start_addr); + descriptor = (struct container_descriptor *)(image + offset); + + /* address of top level container content */ + offset = le_to_uint(descriptor->content_address); + num_of_containers = le_to_uint(descriptor->content_length) / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + addr = le_to_uint(image + offset); + offset += 4; + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + fwu->img.ui_firmware.data = content; + fwu->img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + fwu->img.ui_config.data = content; + fwu->img.ui_config.size = length; + break; + case BL_CONTAINER: + fwu->img.bl_version = *content; + fwu->img.bootloader.data = content; + fwu->img.bootloader.size = length; + fwu_parse_image_header_10_bl_container(fwu, image); + break; + case GUEST_CODE_CONTAINER: + fwu->img.contains_guest_code = true; + fwu->img.guest_code.data = content; + fwu->img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + fwu->img.contains_disp_config = true; + fwu->img.dp_config.data = content; + fwu->img.dp_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + fwu->img.contains_flash_config = true; + fwu->img.fl_config.data = content; + fwu->img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + fwu->img.contains_firmware_id = true; + fwu->img.firmware_id = le_to_uint(content + 4); + break; + default: + break; + } + } + + return; +} + +static void fwu_parse_image_header_05_06(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + const unsigned char *image; + struct image_header_05_06 *header; + + image = fwu->image; + header = (struct image_header_05_06 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + fwu->img.bl_version = header->header_version; + + fwu->img.contains_bootloader = header->options_bootloader; + if (fwu->img.contains_bootloader) + fwu->img.bootloader_size = le_to_uint(header->bootloader_size); + + fwu->img.ui_firmware.size = le_to_uint(header->firmware_size); + if (fwu->img.ui_firmware.size) { + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + if (fwu->img.contains_bootloader) + fwu->img.ui_firmware.data += fwu->img.bootloader_size; + } + + if ((fwu->img.bl_version == BL_V6) && header->options_tddi) + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + + fwu->img.ui_config.size = le_to_uint(header->config_size); + if (fwu->img.ui_config.size) { + fwu->img.ui_config.data = fwu->img.ui_firmware.data + + fwu->img.ui_firmware.size; + } + + if (fwu->img.contains_bootloader || header->options_tddi) + fwu->img.contains_disp_config = true; + else + fwu->img.contains_disp_config = false; + + if (fwu->img.contains_disp_config) { + fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr); + fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size); + fwu->img.dp_config.data = image + fwu->img.disp_config_offset; + } else { + retval = secure_memcpy(fwu->img.cstmr_product_id, + sizeof(fwu->img.cstmr_product_id), + header->cstmr_product_id, + sizeof(header->cstmr_product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy custom product ID string\n", + __func__); + } + fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0; + } + + fwu->img.contains_firmware_id = header->options_firmware_id; + if (fwu->img.contains_firmware_id) + fwu->img.firmware_id = le_to_uint(header->firmware_id); + + retval = secure_memcpy(fwu->img.product_id, + sizeof(fwu->img.product_id), + header->product_id, + sizeof(header->product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy product ID string\n", + __func__); + } + fwu->img.product_id[PRODUCT_ID_SIZE] = 0; + + fwu->img.lockdown.size = LOCKDOWN_SIZE; + fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE; + + return; +} + +static int fwu_parse_image_info(struct synaptics_rmi4_fwu_handle *fwu) +{ + struct image_header_10 *header; + + header = (struct image_header_10 *)fwu->image; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + fwu_parse_image_header_10(fwu); + break; + case IMAGE_HEADER_VERSION_05: + case IMAGE_HEADER_VERSION_06: + fwu_parse_image_header_05_06(fwu); + break; + default: + dev_err(LOGDEV, + "%s: Unsupported image file format (0x%02x)\n", + __func__, header->major_header_version); + return -EINVAL; + } + + if (fwu->bl_version >= BL_V7) { + if (!fwu->img.contains_flash_config) { + dev_err(LOGDEV, + "%s: No flash config found in firmware image\n", + __func__); + return -EINVAL; + } + + fwu_parse_partition_table(fwu, fwu->img.fl_config.data, + &fwu->img.blkcount, &fwu->img.phyaddr); + + fwu_compare_partition_tables(fwu); + } else { + fwu->new_partition_table = false; + } + + return 0; +} + +static int fwu_read_flash_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval, partition = -1; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + dev_dbg(LOGDEV, "%s: Flash status = 0x%02x\n", + __func__, (unsigned int)status); + fwu->in_bl_mode = status >> 7; + + if (fwu->bl_version == BL_V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == BL_V6) + fwu->flash_status = status & MASK_3BIT; + else + fwu->flash_status = status & MASK_5BIT; + + if (fwu->bl_version <= BL_V6) { + unsigned char command; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash command\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) + fwu->command = command & MASK_4BIT; + else if (fwu->bl_version == BL_V6) + fwu->command = command & MASK_6BIT; + else + fwu->command = command; + + } else { + struct f34_v7_data_1_5 data15; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.partition_id, + (unsigned char *)&data15, + sizeof(data15)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read data15\n", + __func__); + return retval; + } + + if (fwu->flash_status == BAD_PARTITION_TABLE) + fwu->flash_status = 0x00; + + partition = data15.partition_id; + fwu->command = data15.command; + } + + if (fwu->flash_status != 0x00) { + dev_err(LOGDEV, + "%s: Flash status = %d, part_id = %d, command = 0x%02x\n", + __func__, fwu->flash_status, partition, fwu->command); + } + + return 0; +} + +static int fwu_read_interrupt_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char interrupt_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &interrupt_status, + sizeof(interrupt_status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read intr status\n", + __func__); + return retval; + } + dev_dbg(LOGDEV, + "%s: F01 interrupt status = 0x%02x\n", + __func__, interrupt_status); + return interrupt_status; +} + +static irqreturn_t fwu_irq(int irq, void *data) +{ + struct synaptics_rmi4_fwu_handle *fwu_ptr = data; + up(&fwu_ptr->irq_sema); + return IRQ_HANDLED; +} + +static void fwu_irq_enable( + struct synaptics_rmi4_fwu_handle *fwu, bool enable) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + int retval; + + if (enable) { + if (fwu->irq_enabled) { + dev_warn(LOGDEV, + "%s: irq already enabled\n", __func__); + return; + } + + fwu_read_interrupt_status(fwu); + retval = request_irq(rmi4_data->irq, fwu_irq, + IRQF_TRIGGER_FALLING, "fwu", fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to request irq: %d\n", + __func__, retval); + } + + dev_dbg(LOGDEV, + "enabling F34 IRQ handler\n"); + fwu->irq_enabled = true; + } else { + if (!fwu->irq_enabled) { + dev_warn(LOGDEV, + "%s: irq already disabled\n", __func__); + return; + } + + dev_dbg(LOGDEV, + "disabling F34 IRQ handler\n"); + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, fwu); + fwu->irq_enabled = false; + } + sema_clear(&fwu->irq_sema); +} + +static int fwu_wait_for_idle( + struct synaptics_rmi4_fwu_handle *fwu, int timeout_ms) +{ + int retval; + + retval = down_timeout(&fwu->irq_sema, msecs_to_jiffies(timeout_ms)); + if (retval) { + retval = -ETIMEDOUT; + dev_err(LOGDEV, + "%s: timed out waiting for cmd to complete\n", + __func__); + } + + retval = fwu_read_interrupt_status(fwu); + fwu_read_flash_status(fwu); + if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00)) + return 0; + + return retval; +} + +static void fwu_reset_device(struct synaptics_rmi4_fwu_handle *fwu) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + rmi4_data->reset_device(rmi4_data); + if (fwu->irq_enabled) { + /* F34 irq handler will override default reset handler */ + /* and since ISR is triggered on falling edge, need to */ + /* wait for idle twice */ + fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + } +} + +static int fwu_write_f34_v7_command_single_transaction( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + struct f34_v7_data_1_5 data_1_5; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + + switch (cmd) { + case CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + }; + + data_1_5.payload_0 = fwu->bootloader_id[0]; + data_1_5.payload_1 = fwu->bootloader_id[1]; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.partition_id, + data_1_5.data, + sizeof(data_1_5.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write single transaction command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v7_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_GUEST_CODE: + command = CMD_V7_WRITE; + break; + case CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + fwu->command = command; + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = fwu_write_f34_v7_command_single_transaction(fwu, cmd); + if (retval < 0) + return retval; + else + return 0; + default: + break; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v5v6_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_IDLE: + command = CMD_V5V6_IDLE; + break; + case CMD_WRITE_FW: + command = CMD_V5V6_WRITE_FW; + break; + case CMD_WRITE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_WRITE_LOCKDOWN: + command = CMD_V5V6_WRITE_LOCKDOWN; + break; + case CMD_WRITE_GUEST_CODE: + command = CMD_V5V6_WRITE_GUEST_CODE; + break; + case CMD_READ_CONFIG: + command = CMD_V5V6_READ_CONFIG; + break; + case CMD_ERASE_ALL: + command = CMD_V5V6_ERASE_ALL; + break; + case CMD_ERASE_UI_CONFIG: + command = CMD_V5V6_ERASE_UI_CONFIG; + break; + case CMD_ERASE_DISP_CONFIG: + command = CMD_V5V6_ERASE_DISP_CONFIG; + break; + case CMD_ERASE_GUEST_CODE: + command = CMD_V5V6_ERASE_GUEST_CODE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V5V6_ENABLE_FLASH_PROG; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + break; + default: + break; + }; + + fwu->command = command; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_command(fwu, cmd); + else + retval = fwu_write_f34_v5v6_command(fwu, cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char partition; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case CMD_WRITE_CONFIG: + case CMD_READ_CONFIG: + if (fwu->config_area == UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (fwu->config_area == DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (fwu->config_area == PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (fwu->config_area == BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (fwu->config_area == FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.partition_id, + &partition, + sizeof(partition)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write partition ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_partition_id( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_partition_id(fwu, cmd); + else + retval = 0; + + return retval; +} + +static int fwu_read_f34_v7_partition_table( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *partition_table) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + fwu->config_area = FLASH_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(fwu, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT); + length[1] = (unsigned char)(fwu->flash_config_length >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(fwu, CMD_READ_CONFIG); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + partition_table, + fwu->partition_table_bytes); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_v7_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char ii; + unsigned char base; + unsigned char index; + unsigned char offset; + unsigned char *ptable; + struct f34_v7_query_0 query_0; + struct f34_v7_query_1_7 query_1_7; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read query 0\n", + __func__); + return retval; + } + + offset = query_0.subpacket_1_size + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset, + query_1_7.data, + sizeof(query_1_7.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + fwu->bootloader_id[0] = query_1_7.bl_minor_revision; + fwu->bootloader_id[1] = query_1_7.bl_major_revision; + dev_dbg(LOGDEV, "%s: BL ver %d.%d\n", __func__, + fwu->bootloader_id[1], fwu->bootloader_id[0]); + + if (fwu->bootloader_id[1] == BL_V8) + fwu->bl_version = BL_V8; + else { + int i; + unsigned char *s = NULL; + struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info); + + dev_info(LOGDEV, + "%s: BL ver.7 detected; need to update product ID\n", __func__); + /* need to add 2 extra characters, thus make sure they fit */ + for (i = 0; i < SYNAPTICS_RMI4_PRODUCT_ID_SIZE-2; i++) + if (rmi->product_id_string[i] == 0) { + if (!(rmi->product_id_string[i-1] == '7' && + rmi->product_id_string[i-2] == 'v')) + s = &rmi->product_id_string[i]; + break; + } + + if (s) { + /* adding suffix 'v7' to the original product id string */ + *s++ = 'v'; *s++ = '7'; *s = 0; + dev_info(LOGDEV, "%s: NOTE new product ID: '%s;\n", + __func__, rmi->product_id_string); + } + } + + fwu->block_size = query_1_7.block_size_15_8 << 8 | + query_1_7.block_size_7_0; + + fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 | + query_1_7.flash_config_length_7_0; + + fwu->payload_length = query_1_7.payload_length_15_8 << 8 | + query_1_7.payload_length_7_0; + + fwu->off.flash_status = V7_FLASH_STATUS_OFFSET; + fwu->off.partition_id = V7_PARTITION_ID_OFFSET; + fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET; + fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + fwu->off.flash_cmd = V7_COMMAND_OFFSET; + fwu->off.payload = V7_PAYLOAD_OFFSET; + + fwu->flash_properties.has_disp_config = query_1_7.has_display_config; + fwu->flash_properties.has_pm_config = query_1_7.has_guest_serialization; + fwu->flash_properties.has_bl_config = query_1_7.has_global_parameters; + + fwu->has_guest_code = query_1_7.has_guest_code; + + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES; + + fwu->partitions = 0; + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) { + for (ii = 0; ii < 8; ii++) { + if (query_1_7.data[index + offset] & (1 << ii)) + fwu->partitions++; + } + + dev_dbg(LOGDEV, + "%s: Supported partitions: 0x%02x\n", + __func__, query_1_7.data[index + offset]); + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!ptable) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for partition table\n", + __func__); + return -ENOMEM; + } + + retval = fwu_read_f34_v7_partition_table(fwu, ptable); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read partition table\n", + __func__); + kfree(ptable); + return retval; + } + + fwu_parse_partition_table(fwu, ptable, &fwu->blkcount, &fwu->phyaddr); + + kfree(ptable); + + return 0; +} + +static int fwu_read_f34_v5v6_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char count; + unsigned char base; + unsigned char buf[10]; + struct f34_v5v6_flash_properties_2 properties_2; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + V5V6_BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) { + fwu->off.properties = V5_PROPERTIES_OFFSET; + fwu->off.block_size = V5_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V5_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == BL_V6) { + fwu->off.properties = V6_PROPERTIES_OFFSET; + fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET; + fwu->off.block_size = V6_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V6_BLOCK_COUNT_OFFSET; + fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_size, + buf, + 2); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == BL_V5) { + fwu->off.flash_cmd = fwu->off.payload + fwu->block_size; + fwu->off.flash_status = fwu->off.flash_cmd; + } else if (fwu->bl_version == BL_V6) { + fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET; + fwu->off.flash_status = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties.has_pm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_disp_config) + count += 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_count, + buf, + count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.ui_firmware, &(buf[0])); + batohs(&fwu->blkcount.ui_config, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_pm_config) { + batohs(&fwu->blkcount.pm_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->blkcount.bl_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_disp_config) + batohs(&fwu->blkcount.dp_config, &(buf[count])); + + fwu->has_guest_code = false; + + if (fwu->flash_properties.has_query4) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties_2, + properties_2.data, + sizeof(properties_2.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties 2\n", + __func__); + return retval; + } + + if (properties_2.has_guest_code) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.gc_block_count, + buf, + 2); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read guest code block count\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.guest_code, &(buf[0])); + fwu->has_guest_code = true; + } + } + + return 0; +} + +static int fwu_read_f34_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + + /* Strict BL version comparison is correct in this case, */ + /* since PDT scan assigns bl_version to BL_V7 for F34 v.2*/ + if (fwu->bl_version == BL_V7) + retval = fwu_read_f34_v7_queries(fwu); + else + retval = fwu_read_f34_v5v6_queries(fwu); + + dev_info(LOGDEV, + "%s: BL version = %d\n", + __func__, fwu->bl_version); + + return retval; +} + +static int fwu_write_f34_v7_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(fwu, command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + dev_dbg(LOGDEV, "%s: update %s (%3d / %3d)\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + block_cnt - remaining, block_cnt); + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + block_ptr, + transfer * fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + block_ptr += (transfer * fwu->block_size); + remaining -= transfer; + dev_dbg(LOGDEV, "%s: update %s (%3d / %3d)\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + block_cnt - remaining, block_cnt); + } while (remaining); + + return 0; +} + +static int fwu_write_f34_v5v6_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + unsigned int progress = (command == CMD_WRITE_CONFIG) ? + 10 : 100; + + base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + if (blk % progress == 0) + dev_dbg(LOGDEV, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + blk, + block_cnt); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block data (block %d)\n", + __func__, blk); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command for block %d\n", + __func__, blk); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (block %d)\n", + __func__, blk); + return retval; + } + + block_ptr += fwu->block_size; + } + + return 0; +} + +static int fwu_write_f34_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_blocks(fwu, block_ptr, block_cnt, cmd); + else + retval = fwu_write_f34_v5v6_blocks(fwu, block_ptr, block_cnt, cmd); + + return retval; +} + +static int fwu_read_f34_v7_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(fwu, command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + &fwu->read_config_buf[index], + transfer * fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_v5v6_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write read config command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data (block %d)\n", + __func__, blk); + return retval; + } + + index += fwu->block_size; + } + + return 0; +} + +static int fwu_read_f34_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_read_f34_v7_blocks(fwu, block_cnt, cmd); + else + retval = fwu_read_f34_v5v6_blocks(fwu, block_cnt, cmd); + + return retval; +} + +static void fwu_get_device_firmware_id( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int *dev_id) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + struct synaptics_rmi4_device_info *rmi = + &rmi4_data->rmi4_mod_info; + unsigned char *buf = (unsigned char *)dev_id; + buf[0] = 0; + buf[1] = rmi->build_id[0]; + buf[2] = rmi->build_id[1]; + buf[3] = rmi->build_id[2]; +} + +static int fwu_get_image_firmware_id( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int *fw_id) +{ + if (fwu->img.contains_firmware_id) { + *fw_id = fwu->img.firmware_id; + return 0; + } + + dev_err(LOGDEV, "%s: no build ID in image\n", __func__); + return -EINVAL; +} + +static int fwu_get_device_config_id(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char config_id_size; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + if (fwu->bl_version >= BL_V7) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + fwu->config_id, + config_id_size); + if (retval < 0) + return retval; + + return 0; +} + +static enum flash_area fwu_go_nogo(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char ii; + unsigned char config_id_size; + unsigned int device_fw_id; + unsigned int image_fw_id; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->in_bl_mode) { + flash_area = UI_FIRMWARE; + dev_dbg(LOGDEV, "%s: BL mode shortcut\n", __func__); + goto exit; + } + + /* Get device firmware ID */ + fwu_get_device_firmware_id(fwu, &device_fw_id); + dev_info(LOGDEV, + "%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + retval = fwu_get_image_firmware_id(fwu, &image_fw_id); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + dev_info(LOGDEV, + "%s: Image firmware ID = %d\n", + __func__, image_fw_id); + + if (image_fw_id != device_fw_id) { + dev_info(LOGDEV, + "%s: Image firmware ID differs from device firmware ID 0x%x 0x%x\n", + __func__, image_fw_id, device_fw_id); + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device config ID */ + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + if (fwu->bl_version >= BL_V7) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + for (ii = 0; ii < config_id_size; ii++) { + if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) { + flash_area = UI_CONFIG; + goto exit; + } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) { + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + dev_info(LOGDEV, + "%s: No need to do reflash\n", + __func__); + } else { + dev_info(LOGDEV, + "%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware and config" : + "UI config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + bool f35found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + fwu->in_ub_mode = false; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(LOGDEV, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + switch (rmi_fd.fn_version) { + case F34_V0: + fwu->bl_version = BL_V5; + break; + case F34_V1: + fwu->bl_version = BL_V6; + break; + case F34_V2: + fwu->bl_version = BL_V7; + break; + default: + dev_err(LOGDEV, + "%s: Unrecognized F34 version\n", + __func__); + return -EINVAL; + } + + dev_dbg(LOGDEV, + "%s: F34 version %d\n", + __func__, rmi_fd.fn_version); + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + case SYNAPTICS_RMI4_F35: + f35found = true; + fwu->f35_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f35_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f35_fd.data_base_addr = + rmi_fd.data_base_addr; + break; + } + } else { + break; + } + + intr_count += rmi_fd.intr_src_count; + } + + if (!f01found || !f34found) { + dev_err(LOGDEV, + "%s: Failed to find both F01 and F34\n", + __func__); + if (!f35found) { + dev_err(LOGDEV, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + fwu->in_ub_mode = true; + dev_dbg(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + fwu_recovery_check_status(fwu); + return 0; + } + } + + return 0; +} + +int fwu_check_intr_en(struct synaptics_rmi4_data *rmi4_data, + unsigned char *drv_intr_en, size_t size) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int i; + unsigned char intr_en[MAX_INTR_REGISTERS]; + int retval = 0; + unsigned char addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + intr_en, + size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read interrupt enable register\n", + __func__); + return retval; + } + /* ensure interrupt enable mask expected by driver is subset of */ + /* touch IC interrupt enable register */ + for (i = 0; i < size; ++i) + if (drv_intr_en[i] != (drv_intr_en[i] & intr_en[i])) { + retval = 1; + dev_err(LOGDEV, + "%s: Interrupt enable mismatch:" + " drv[%d] = 0x%02x, fw[%d] = 0x%02x\n", + __func__, i, drv_intr_en[i], i, intr_en[i]); + } + + return retval; +} + +static int fwu_enter_flash_prog(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_read_flash_status(fwu); + if (retval < 0) + return retval; + + if (fwu->in_bl_mode) { + dev_dbg(LOGDEV, "%s: BL mode shortcut\n", __func__); + return 0; + } + + retval = fwu_write_f34_command(fwu, CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto out; + + retval = fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + if (retval < 0) + goto out; + + if (!fwu->in_bl_mode) { + dev_err(LOGDEV, + "%s: BL mode not entered\n", + __func__); + retval = -EINVAL; + goto out; + } + + retval = fwu_scan_pdt(fwu); + if (retval < 0) + goto out; + + fwu_check_intr_en(rmi4_data, &fwu->intr_mask, sizeof(fwu->intr_mask)); + + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + goto out; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read F01 device control\n", + __func__); + goto out; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write F01 device control\n", + __func__); + goto out; + } + + msleep(ENTER_FLASH_PROG_WAIT_MS); +out: + return retval; +} + +static int fwu_check_ui_firmware_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.ui_firmware.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_firmware) { + dev_err(LOGDEV, + "%s: UI firmware size mismatch: %d (expected %d)\n", + __func__, block_count, fwu->blkcount.ui_firmware); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_ui_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.ui_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_config) { + dev_err(LOGDEV, + "%s: UI config size mismatch: %d (expected %d)\n", + __func__, block_count, fwu->blkcount.ui_config); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_dp_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.dp_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.dp_config) { + dev_err(LOGDEV, + "%s: Display configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_bl_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.bl_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.bl_config) { + dev_err(LOGDEV, + "%s: Bootloader configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_guest_code_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.guest_code.size / fwu->block_size; + if (block_count != fwu->blkcount.guest_code) { + dev_err(LOGDEV, + "%s: Guest code size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_write_firmware( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short firmware_block_count; + + firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size; + + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.ui_firmware.data, + firmware_block_count, CMD_WRITE_FW); +} + +static int fwu_erase_configuration( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_UI_CONFIG); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_DISP_CONFIG); + if (retval < 0) + return retval; + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_BL_CONFIG); + if (retval < 0) + return retval; + break; + } + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + return retval; +} + +static int fwu_erase_guest_code( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + retval = fwu_write_f34_command(fwu, CMD_ERASE_GUEST_CODE); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +static int fwu_erase_all(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + if (fwu->bl_version == BL_V7) { + retval = fwu_write_f34_command(fwu, CMD_ERASE_UI_FIRMWARE); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + } else { + retval = fwu_write_f34_command(fwu, CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (!(fwu->bl_version == BL_V8 && + fwu->flash_status == BAD_PARTITION_TABLE)) { + if (retval < 0) + return retval; + } + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + if (fwu->bl_version == BL_V8) + return 0; + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + } + + if (fwu->new_partition_table && fwu->has_guest_code) { + retval = fwu_erase_guest_code(fwu); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_write_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG); +} + +static int fwu_write_ui_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->img.ui_config.data; + fwu->config_size = fwu->img.ui_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(fwu); +} + +static int fwu_write_dp_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + fwu->config_area = DP_CONFIG_AREA; + fwu->config_data = fwu->img.dp_config.data; + fwu->config_size = fwu->img.dp_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(fwu); +} + +static int fwu_write_flash_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(LOGDEV, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_f34_command(fwu, CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase flash configuration command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + fwu_reset_device(fwu); + + return 0; +} + +static int fwu_write_guest_code(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short guest_code_block_count; + + guest_code_block_count = fwu->img.guest_code.size / fwu->block_size; + + retval = fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.guest_code.data, + guest_code_block_count, CMD_WRITE_GUEST_CODE); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_lockdown(struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short lockdown_block_count; + + lockdown_block_count = fwu->img.lockdown.size / fwu->block_size; + + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.lockdown.data, + lockdown_block_count, CMD_WRITE_LOCKDOWN); +} + +static int fwu_write_partition_table_v8(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(LOGDEV, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + fwu_reset_device(fwu); + + return 0; +} + +static int fwu_write_partition_table_v7(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short block_count; + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu, fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(fwu, block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + + retval = fwu_write_flash_configuration(fwu); + if (retval < 0) + return retval; + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_do_reflash(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + if (!fwu->new_partition_table) { + retval = fwu_check_ui_firmware_size(fwu); + if (retval < 0) + return retval; + + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + return retval; + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_check_dp_configuration_size(fwu); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_check_guest_code_size(fwu); + if (retval < 0) + return retval; + } + } else if (fwu->bl_version == BL_V7) { + retval = fwu_check_bl_configuration_size(fwu); + if (retval < 0) + return retval; + } + + retval = fwu_erase_all(fwu); + if (retval < 0) + return retval; + + if (fwu->bl_version == BL_V7 && fwu->new_partition_table) { + retval = fwu_write_partition_table_v7(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } else if (fwu->bl_version == BL_V8) { + retval = fwu_write_partition_table_v8(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } + + retval = fwu_write_firmware(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_write_ui_configuration(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_write_dp_configuration(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Display configuration programmed\n", __func__); + } + + if (fwu->new_partition_table) { + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_write_guest_code(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Guest code programmed\n", __func__); + } + } + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_do_read_config( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short block_count; + unsigned short config_area; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->blkcount.ui_config; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(LOGDEV, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.dp_config; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(LOGDEV, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.pm_config; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) { + dev_err(LOGDEV, + "%s: Bootloader configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.bl_config; + break; + default: + dev_err(LOGDEV, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + if (block_count == 0) { + dev_err(LOGDEV, + "%s: Invalid block count\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + + retval = fwu_read_f34_blocks(fwu, block_count, CMD_READ_CONFIG); + +exit: + fwu_reset_device(fwu); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} +#endif + +static int fwu_do_lockdown(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if (fwu->flash_properties.unlocked == 0) { + dev_info(LOGDEV, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(fwu); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_start_write_guest_code(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_parse_image_info(fwu); + if (retval < 0) + return -EINVAL; + + if (!fwu->has_guest_code) { + dev_err(LOGDEV, + "%s: Guest code not supported\n", + __func__); + return -EINVAL; + } + + if (!fwu->img.contains_guest_code) { + dev_err(LOGDEV, + "%s: No guest code in firmware image\n", + __func__); + return -EINVAL; + } + + rmi4_data->set_state(rmi4_data, STATE_INIT); + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write guest code process\n", __func__); + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + retval = fwu_check_guest_code_size(fwu); + if (retval < 0) + goto exit; + + retval = fwu_erase_guest_code(fwu + if (retval < 0) + goto exit; + + retval = fwu_write_guest_code(fwu); + if (retval < 0) + goto exit; + + pr_notice("%s: Guest code programmed\n", __func__); + +exit: + fwu_reset_device(fwu); + + pr_notice("%s: End of write guest code process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} + +static int fwu_start_write_config(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short config_area; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_parse_image_info(fwu); + if (retval < 0) + return -EINVAL; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + fwu_get_device_firmware_id(fwu, &device_fw_id); + retval = fwu_get_image_firmware_id(fwu, &image_fw_id); + if (retval < 0) + return retval; + if (device_fw_id != image_fw_id) { + dev_err(LOGDEV, + "%s: Device and image firmware IDs don't match\n", + __func__); + return -EINVAL; + } + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(LOGDEV, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_disp_config) { + dev_err(LOGDEV, + "%s: No display configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_dp_configuration_size(fwu); + if (retval < 0) + return retval; + break; + default: + dev_err(LOGDEV, + "%s: Configuration not supported\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write config process\n", __func__); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + retval = fwu_erase_configuration(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to erase config\n", + __func__); + goto exit; + } + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_ui_configuration(fwu); + if (retval < 0) + goto exit; + break; + case DP_CONFIG_AREA: + retval = fwu_write_dp_configuration(fwu); + if (retval < 0) + goto exit; + break; + } + + pr_notice("%s: Config written\n", __func__); + +exit: + switch (fwu->config_area) { + case UI_CONFIG_AREA: + fwu_reset_device(fwu); + break; + case DP_CONFIG_AREA: + fwu_reset_device(fwu); + break; + } + + pr_notice("%s: End of write config process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} +#endif + +static bool fwu_tdat_image_format(const unsigned char *fw_image) +{ + return fw_image[0] == 0x31; +} + +static void fwu_tdat_config_set(const unsigned char *data, unsigned int size, + const unsigned char **image, unsigned int *image_size) +{ + unsigned short id; + unsigned int length, offset; + + for (offset = 0; offset < size; offset += length+5) { + id = (data[offset+1] << 8) | data[offset]; + length = (data[offset+4] << 8) | data[offset+3]; + if (id == 0x0001) { + *image = &data[offset+5]; + *image_size = length; + } + } +} + +static void fwu_tdat_section_offset( + const unsigned char **image, unsigned int *image_size) +{ + unsigned int offset; + offset = (*image)[0] + 1; + *image_size -= offset; + *image = &(*image)[offset]; +} + +static int fwu_parse_tdat_image(struct synaptics_rmi4_fwu_handle *fwu) +{ + int ii; + unsigned int id; + unsigned int length, offset; + struct image_metadata *img = &fwu->img; + const unsigned char *section, *data = fwu->image; + unsigned int fw_size = fwu->image_size; + img->contains_firmware_id = false; + img->contains_bootloader = false; + img->contains_disp_config = false; + img->contains_guest_code = false; + img->contains_flash_config = false; + img->lockdown.data = NULL; + + + pr_notice("%s: Start TDAT image processing\n", __func__); + + for (ii = 0, offset = 1; offset < fw_size; offset += length+4) { + length = (data[offset+3] << 16) | + (data[offset+2] << 8) | data[offset+1]; + + dev_dbg(LOGDEV, + "Record[%d]: length %u, offset %u\n", + ii++, length, offset); + + if ((offset+length+4) > fw_size) { + dev_err(LOGDEV, + "Data overflow at offset %u (%u)\n", + offset, data[offset]); + return -EINVAL; + } + } + + if (offset != fw_size) { + dev_err(LOGDEV, + "Data is misaligned\n"); + return -EINVAL; + } + + for (offset = 1; offset < fw_size; offset += length+4) { + id = data[offset]; + length = (data[offset+3] << 16) | + (data[offset+2] << 8) | data[offset+1]; + section = &data[offset+4]; + + switch (id) { + case 1: /* config */ + dev_dbg(LOGDEV, + "%s: Config record %d, size %u\n", + __func__, id, length); + + fwu_tdat_config_set(section, length, + &img->ui_config.data, + &img->ui_config.size); + fwu_tdat_section_offset(&img->ui_config.data, + &img->ui_config.size); + break; + + case 2: /* firmware */ + dev_dbg(LOGDEV, + "%s: Firmware record %d, size %u\n", + __func__, + id, + length); + + img->contains_firmware_id = true; + batohui(&fwu->img.firmware_id, + (unsigned char *)§ion[1], + SYNAPTICS_RMI4_BUILD_ID_SIZE); + + dev_dbg(LOGDEV, + "%s: Firmware build ID %x\n", + __func__, + fwu->img.firmware_id); + + img->ui_firmware.data = section; + img->ui_firmware.size = length; + fwu_tdat_section_offset(&img->ui_firmware.data, + &img->ui_firmware.size); + break; + default: + dev_dbg(LOGDEV, + "%s: Don't care section id %d\n", + __func__, + id); + break; + } + } + + dev_dbg(LOGDEV, + "%s: Firwmare size %u, config size %u\n", + __func__, + img->ui_firmware.size, + img->ui_config.size); + return 0; +} + +static int fwu_start_reflash(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval = 0; + enum flash_area flash_area; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + __pm_stay_awake(&fwu->flash_wake_src); + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + pr_notice("%s: Start of reflash process\n", __func__); + + rmi4_data->set_state(rmi4_data, STATE_INIT); + fwu_irq_enable(fwu, true); + + if (fwu->image == NULL) { + dev_dbg(LOGDEV, + "%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + LOGDEV); + if (retval != 0) { + dev_err(LOGDEV, + "%s: Firmware image %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(LOGDEV, + "%s: Firmware image size = %zd\n", + __func__, fw_entry->size); + + fwu->image = fw_entry->data; + fwu->image_size = fw_entry->size; + } + + if (fwu_tdat_image_format(fwu->image)) + fwu_parse_tdat_image(fwu); + else { + retval = fwu_parse_image_info(fwu); + if (retval < 0) + goto exit; + + if (fwu->bl_version != fwu->img.bl_version) { + dev_err(LOGDEV, + "%s: BL versions mismatch: ic=%d, image=%d\n", + __func__, fwu->bl_version, fwu->img.bl_version); + retval = -EINVAL; + goto exit; + } + + if (!fwu->force_update && fwu->new_partition_table) { + dev_err(LOGDEV, + "%s: Partition table mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + } + + retval = fwu_read_flash_status(fwu); + if (retval < 0) + goto exit; + + if (fwu->in_bl_mode) { + sema_clear(&fwu->irq_sema); + dev_info(LOGDEV, + "%s: Device in bootloader mode\n", + __func__); + } + + if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) { + switch (fwu->bl_version) { + case BL_V5: + case BL_V6: + retval = fwu_do_lockdown(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do lockdown\n", + __func__); + } + fwu_reset_device(fwu); + break; + default: + break; + } + } + + flash_area = fwu_go_nogo(fwu); + dev_dbg(LOGDEV, "%s: flash_area = %d\n", __func__, flash_area); + + if (flash_area != NONE) { + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) { + fwu_reset_device(fwu); + goto exit; + } + + } + + rmi4_data->set_state(rmi4_data, STATE_FLASH); + + switch (flash_area) { + case UI_FIRMWARE: + retval = fwu_do_reflash(fwu); + break; + case UI_CONFIG: + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + break; + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + break; + retval = fwu_write_ui_configuration(fwu); + break; + case NONE: + default: + break; + } + + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do reflash\n", + __func__); + } + +exit: + if (fw_entry) + release_firmware(fw_entry); + + fwu_irq_enable(fwu, false); + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + fwu_reset_device(fwu); + pr_notice("%s: End of reflash process\n", __func__); + + /* Rescan PDT after flashing and before register access */ + retval = fwu_scan_pdt(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to scan PDT\n", __func__); + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to query F34\n", __func__); + } + + fwu_check_intr_en(rmi4_data, &rmi4_data->intr_mask[0], + rmi4_data->num_of_intr_regs); + + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + } + + memcpy(rmi4_data->rmi4_mod_info.config_id, fwu->config_id, + V5V6_CONFIG_ID_SIZE); + + rmi4_data->ready_state(rmi4_data, false); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + __pm_relax(&fwu->flash_wake_src); + + return retval; +} + +static int fwu_recovery_check_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.data_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + F35_ERROR_CODE_OFFSET, + &status, + 1); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read status\n", + __func__); + return retval; + } + + status = status & MASK_7BIT; + + if (status != 0x00) { + dev_err(LOGDEV, + "%s: Recovery mode status = %d\n", + __func__, status); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_recovery_erase_all( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char command = CMD_F35_ERASE_ALL; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to issue erase all command\n", + __func__); + return retval; + } + + msleep(F35_ERASE_ALL_WAIT_MS); + + retval = fwu_recovery_check_status(fwu); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_recovery_write_chunk( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char chunk_number[] = {0, 0}; + unsigned char chunk_spare; + unsigned char chunk_size; + unsigned char buf[F35_CHUNK_SIZE + 1]; + unsigned short chunk; + unsigned short chunk_total; + unsigned short bytes_written = 0; + unsigned char *chunk_ptr = (unsigned char *)fwu->image; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_NUM_LSB_OFFSET, + chunk_number, + sizeof(chunk_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk number\n", + __func__); + return retval; + } + + buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK; + + chunk_total = fwu->image_size / F35_CHUNK_SIZE; + chunk_spare = fwu->image_size % F35_CHUNK_SIZE; + if (chunk_spare) + chunk_total++; + + for (chunk = 0; chunk < chunk_total; chunk++) { + if (chunk_spare && chunk == chunk_total - 1) + chunk_size = chunk_spare; + else + chunk_size = F35_CHUNK_SIZE; + + memset(buf, 0x00, F35_CHUNK_SIZE); + secure_memcpy(buf, sizeof(buf), chunk_ptr, + fwu->image_size - bytes_written, + chunk_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_DATA_OFFSET, + buf, + sizeof(buf)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data (chunk %d)\n", + __func__, chunk); + return retval; + } + chunk_ptr += chunk_size; + bytes_written += chunk_size; + } + + retval = fwu_recovery_check_status(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_recovery_reset( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char command = CMD_F35_RESET; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to issue reset command\n", + __func__); + return retval; + } + + msleep(F35_RESET_WAIT_MS); + + return 0; +} + +static int fwu_start_recovery( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of recovery process\n", __func__); + + __pm_stay_awake(&fwu->flash_wake_src); + + retval = rmi4_data->irq_enable(rmi4_data, false); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to disable interrupt\n", + __func__); + goto exit; + } + + msleep(INT_DISABLE_WAIT_MS); + + fwu_irq_enable(fwu, true); + + retval = fwu_recovery_erase_all(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do erase all in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: External flash erased\n", __func__); + + retval = fwu_recovery_write_chunk(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Chunk data programmed\n", __func__); + + retval = fwu_recovery_reset(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to reset device in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Recovery mode reset issued\n", __func__); + + + retval = 0; + +exit: + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + fwu_reset_device(fwu); + fwu_irq_enable(fwu, false); + __pm_relax(&fwu->flash_wake_src); + + pr_notice("%s: End of recovery process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->ready_state(rmi4_data, false); + + return retval; +} +#endif + +static int synaptics_fw_updater( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + if (fwu->in_ub_mode) + return -ENODEV; + + fwu->image = fw_data; + + retval = fwu_start_reflash(fwu); + + fwu->image = NULL; + + return retval; +} +//EXPORT_SYMBOL(synaptics_fw_updater); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + + if (count < fwu->config_size) { + dev_err(LOGDEV, + "%s: Not enough space (%zd bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + retval = secure_memcpy(buf, count, fwu->read_config_buf, + fwu->read_config_buf_size, fwu->config_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy config data\n", + __func__); + return retval; + } + + return fwu->config_size; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + + retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos], + fwu->image_size - fwu->data_pos, buf, count, count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy image data\n", + __func__); + return retval; + } + + fwu->data_pos += count; + + return count; +} + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (!fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: Not in microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_recovery(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do recovery\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} +#endif + +static int synaptics_dsx_firmware_update(struct device *dev, + const char *fwname) +{ + int retval; + char prefix[SYNAPTICS_RMI4_FILENAME_SIZE] = "synaptics"; + char template[SYNAPTICS_RMI4_FILENAME_SIZE]; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (!fwu->force_update) { + if (strncmp(fwname, prefix, + strnlen(prefix, sizeof(prefix)))) { + dev_err(dev, "%s: FW does not belong to Synaptics\n", + __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + rmi->product_id_string); + if (!strnstr(fwname + strnlen(prefix, sizeof(prefix)), + template, strlen(fwname))) { + dev_err(dev, "%s: FW does not belong to %s\n", + __func__, rmi->product_id_string); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(fwu->image_name, fwname, strlen(fwname)); + dev_dbg(dev, "%s: FW filename: %s\n", __func__, fwu->image_name); + + retval = synaptics_fw_updater(fwu, fwu->ext_data_source); + if (retval < 0) { + dev_err(dev, "%s: Failed to do reflash\n", __func__); + goto exit; + } + + retval = 0; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + fwu->image_name[0] = 0; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + return retval; +} + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + + if (count > MAX_IMAGE_NAME_LEN) + return -EINVAL; + + retval = synaptics_dsx_firmware_update(dev, buf); + + return count; +} + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + fwu->force_update = true; + + return count; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_write_config(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + return -EINVAL; + } + + retval = fwu_do_read_config(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read config\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + fwu->config_area = config_area; + + return count; +} + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + buf, count, count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy image file name\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned long size; + + retval = sstrtoul(buf, 10, &size); + if (retval) + return retval; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for image data\n", + __func__); + return -ENOMEM; + } + + return count; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware); +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config); +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config); +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config); +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config); +} + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code); +} + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_write_guest_code(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write guest code\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} +#endif + +static int synaptics_dsx_firmware_erase(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + + __pm_stay_awake(&fwu->flash_wake_src); + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + if (!fwu->in_bl_mode) { + fwu_irq_enable(fwu, true); + + retval = fwu_write_f34_command(fwu, CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto reset_and_exit; + + retval = fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + fwu_irq_enable(fwu, false); + if (retval < 0 || !fwu->in_bl_mode) + goto reset_and_exit; + } + + rmi4_data->set_state(rmi4_data, STATE_INIT); + fwu_irq_enable(fwu, true); + + retval = fwu_erase_all(fwu); + if (retval < 0) + pr_err("%s: ERASE_ALL failed\n", __func__); + + fwu_irq_enable(fwu, false); + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + +reset_and_exit: + fwu_reset_device(fwu); + pr_notice("%s: End of reflash process\n", __func__); + + /* Rescan PDT after flashing and before register access */ + retval = fwu_scan_pdt(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to scan PDT\n", __func__); + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to query F34\n", __func__); + } + + rmi4_data->ready_state(rmi4_data, false); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + __pm_relax(&fwu->flash_wake_src); + + return 0; +} + +static ssize_t fwu_sysfs_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + int retval; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + retval = synaptics_dsx_firmware_erase(dev); + + return count; +} + +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + + if (!fwu) + return; + + if (fwu->intr_mask & intr_mask) + fwu_read_flash_status(fwu); + + return; +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval, attr_count; + struct pdt_properties pdt_props; + + fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL); + if (!fwu->image_name) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for image name\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + sema_init(&fwu->irq_sema, 0); + mutex_init(&rmi4_data->rmi4_exp_init_mutex); + + retval = synaptics_rmi4_reg_read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(LOGDEV, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(LOGDEV, + "%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_mem; + } + + retval = fwu_scan_pdt(fwu); + if (retval < 0) + goto exit_free_mem; + + if (!fwu->in_ub_mode) { + fwu_irq_enable(fwu, true); + retval = fwu_read_f34_queries(fwu); + fwu_irq_enable(fwu, false); + if (retval < 0) + goto exit_free_mem; + + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + goto exit_free_mem; + } + } + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + + wakeup_source_init(&fwu->flash_wake_src, "synaptics_fw_flash"); + + fwu->initialized = true; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + retval = sysfs_create_bin_file(SYSFS_KOBJ, &dev_attr_data); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_mem; + } +#endif + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(SYSFS_KOBJ, &attrs[attr_count].attr); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } +/* + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + retval = sysfs_create_file(SYSFS_KOBJ, &erase_attr[0].attr); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create erase sysfs attributes\n", + __func__); + } else + fwu->has_erase_all = true; + } +*/ + rmi4_data->fwu_data = (void *)fwu; + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(SYSFS_KOBJ, &attrs[attr_count].attr); +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + sysfs_remove_bin_file(SYSFS_KOBJ, &dev_attr_data); +#endif +exit_free_mem: + kfree(fwu->image_name); + +exit_free_fwu: + kfree(fwu); + rmi4_data->fwu_data = NULL; + + return retval; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int attr_count; + + if (!fwu) + goto exit; + + wakeup_source_trash(&fwu->flash_wake_src); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(SYSFS_KOBJ, &attrs[attr_count].attr); + } + + if (fwu->has_erase_all) + sysfs_remove_file(SYSFS_KOBJ, &erase_attr[0].attr); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + sysfs_remove_bin_file(SYSFS_KOBJ, &dev_attr_data); +#endif + +exit: + complete(&fwu->remove_complete); + + return; +} + +static int synaptics_rmi4_fwu_flash_status( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int istatus = 0, retval; + unsigned char status; + + if (fwu == NULL) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash status\n", __func__); + return retval; + } + istatus = status >> 7; + + return istatus; +} + +static int __init rmi4_fw_update_module_init(void) +{ + struct synaptics_rmi4_fwu_handle *fwu; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + fwu->firmware_update = synaptics_dsx_firmware_update; + fwu->firmware_erase = synaptics_dsx_firmware_erase; + fwu->dev = &next->i2c_client->dev; + /* rmi4_data pointer set as drvdata to i2c device */ + next->fwu_data = (void *)fwu; + synaptics_rmi4_new_function(next, RMI_FW_UPDATER, true, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn, + synaptics_rmi4_fwu_flash_status, + IC_MODE_ANY); + } + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + struct synaptics_rmi4_fwu_handle *fwu; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + fwu = (struct synaptics_rmi4_fwu_handle *)next->fwu_data; + init_completion(&fwu->remove_complete); + synaptics_rmi4_new_function(next, RMI_FW_UPDATER, false, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn, + synaptics_rmi4_fwu_flash_status, + IC_MODE_ANY); + wait_for_completion(&fwu->remove_complete); + if (fwu->read_config_buf_size) + kfree(fwu->read_config_buf); + kfree(fwu->image_name); + kfree(fwu); + next->fwu_data = NULL; + } + return; +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX FW Update Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.c new file mode 100644 index 000000000000..e56d05c04eb7 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.c @@ -0,0 +1,7098 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#define DEBUG 511 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#include "synaptics_dsx_i2c.h" + +static struct workqueue_struct *det_workqueue; + +static char *INSTANCE_RMI(char *string, + struct synaptics_rmi4_data *rmi4_data) +{ + char instance[64]; + snprintf(instance, sizeof(instance), "%s.%d", + string, rmi4_data->instance); + return kstrdup(instance, GFP_KERNEL); +} + +static char *INSTANCE_DBG(char *string, + struct synaptics_rmi4_data *rmi4_data) +{ + static char name_with_instance[64]; + snprintf(name_with_instance, sizeof(name_with_instance), "%s.%d", + string, rmi4_data->instance); + return name_with_instance; +} + +static int synaptics_rmi4_hw_init(struct synaptics_rmi4_data *rmi4_data); + +static void synaptics_dsx_free_patch( + struct synaptics_dsx_patch *patch); +static struct synaptics_dsx_patch * + synaptics_dsx_init_patch(const char *name); +static int synaptics_rmi4_set_page( + struct synaptics_rmi4_data *rmi4_data, + unsigned int address); +static int synaptics_rmi4_query_device( + struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_remove(struct i2c_client *client); + +/* F12 packet register description */ +struct f12_c8_s0_type { + unsigned char max_x_lsb; + unsigned char max_x_msb; + unsigned char max_y_lsb; + unsigned char max_y_msb; +}; + +struct f12_c10_s0_type { + unsigned char noise_floor; + unsigned char min_peak_amplitude; + unsigned char peak_merge_threshold_lsb; + unsigned char peak_merge_threshold_msb; +}; + +struct f12_c15_s0_type { + unsigned char finger_threshold; + unsigned char small_finger_threshold; + unsigned char small_finger_border; + unsigned char negative_finger_threshold; +}; + +struct f12_c18_s0_type { + unsigned char zone0_x_lsb; + unsigned char zone0_x_msb; + unsigned char zone0_y_lsb; + unsigned char zone0_y_msb; + unsigned char zone1_x_lsb; + unsigned char zone1_x_msb; + unsigned char zone1_y_lsb; + unsigned char zone1_y_msb; + unsigned char max_timer; + unsigned char max_distance; +}; + +struct f12_c20_s0_type { + unsigned char x_suppression; + unsigned char y_suppression; +}; + +struct f12_c20_s1_type { + union { + struct { + unsigned char flags:1; + unsigned char wakeup_gesture_only:1; + unsigned char dribble:1; + unsigned char proximity_only:1; + unsigned char hover_swipe:1; + unsigned char proximity:1; + unsigned char hover_pinch:1; + unsigned char lp_proximity:1; + } __packed; + unsigned char report; + }; +}; + +struct f12_c23_s0_type { + union { + struct { + unsigned char finger:1; + unsigned char stylus:1; + unsigned char palm:1; + unsigned char unclassified:1; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f12_c23_s1_type { + unsigned char max_num_reported_objects; +}; + +struct f12_c27_s0_type { + union { + struct { + unsigned char double_tap:1; + unsigned char swipe:1; + unsigned char tap_n_hold:1; + unsigned char circle:1; + unsigned char triangle:1; + unsigned char vee:1; + unsigned char unicode:1; + unsigned char reserved:1; + } __packed; + unsigned char wakeup_gesture; + }; +}; + +struct f12_c28_s0_type { + unsigned char reported_bytes_per_object; +}; + +struct f12_d1_s0_type { + unsigned char type_and_stylus; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char z; + unsigned char wx; + unsigned char wy; +}; + +/* RMI4 has the following wakeup gesture defined: + • 0x00 = No gesture + • 0x01 = One-Finger single tap + • 0x02 = One-Finger tap-and-hold + • 0x03 = One-Finger double tap + • 0x04 = One-Finger early tap + • 0x05 = One-Finger flick + • 0x06 = One-Finger press + • 0x07 = One-Finger swipe + • 0x08 = One-Finger Circle + • 0x09 = One-Finger Triangle + • 0x0A = One-Finger Vee + • 0x0B = Reserved + • 0x0C = Triple Tap + • 0x0D = Click + • 0x0E = Reserved + • 0x30 = Edge gesture + • 0x7F = Reserved + • 0x80 = Pinch + • 0x81 = Rotate + • 0x82 through 0xFF = Reserved +*/ + +#define DOUBLE_TAP_GESTURE 0x03 + +struct f12_d4_s0_type { + unsigned char gesture; +}; + +struct f12_d15_s0_type { + unsigned char attn[2]; +}; + +/* F01 packet register description */ +struct f01_c0_s0_type { + union { + struct { + unsigned char sleep_mode:2; + unsigned char no_sleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_c2_s0_type { + unsigned char doze_interval; +}; + +struct f01_c3_s0_type { + unsigned char wakeup_threshold; +}; + +struct f01_c5_s0_type { + unsigned char doze_holdoff; +}; + +struct f01_c9_s0_type { + unsigned char recalibration_interval; +}; + +/* F51 packet register description */ +struct f51_d0_s0_type { + union { + struct { + unsigned char noise_state:1; + unsigned char gear_change:1; + unsigned char guard_state:1; + unsigned char unused:5; + unsigned char md_present:1; + unsigned char reserved:7; + } __packed; + unsigned char data[2]; + }; +}; + +struct f51_c0_s0_type { + union { + struct { + unsigned char cid_check_enable:1; + unsigned char fsim_clear_enable:1; + unsigned char nsm_intr_enable:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f51_c1_s0_type { + struct { + unsigned char dyn_jitter_strength; + unsigned char dyn_jitter_gain; + unsigned char dyn_jitter_step_size; + } __packed; +}; + +struct f51_c4_s0_type { + union { + struct { + unsigned char md_feature_enable:1; + unsigned char md_no_relax:1; + unsigned char md_uncond_shutoff:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; + unsigned char md_min_threshold; + unsigned char md_max_threshold; + unsigned char md_2d_area; + unsigned char md_frame_count_in; + unsigned char md_frame_count_out; +}; + +/* F54 packet register description */ +struct f54_q12_s0_type { + union { + struct { + unsigned char num_of_scan_freq:4; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_c0_s0_type { + union { + struct { + unsigned char no_relax:1; + unsigned char no_scan:1; + unsigned char force_fast_relax:1; + unsigned char startup_fast_relax:1; + unsigned char gest_cancels_fast_relax:1; + unsigned char energy_ratio_relax:1; + unsigned char exces_noise_reporting:1; + unsigned char enable_cap_correction:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_c2_s0_type { + struct { + unsigned short saturation_cap; + } __packed; +}; + +struct f54_d6_s0_type { + union { + struct { + unsigned char interference_metric_lsb; + unsigned char interference_metric_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d10_s0_type { + unsigned char noise_state; +}; + +struct f54_d14_s0_type { + union { + struct { + unsigned char cid_im_lsb; + unsigned char cid_im_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d16_s0_type { + union { + struct { + unsigned char freq_scan_im_lsb; + unsigned char freq_scan_im_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d17_s0_type { + union { + struct { + unsigned char freq:7; + unsigned char inhibit_freq_shift:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_m0_s0_type { + unsigned char command; +}; + +#define CTRL_TYPE (0 << 8) +#define DATA_TYPE (1 << 8) +#define QUERY_TYPE (2 << 8) +#define COMMAND_TYPE (4 << 8) + +/*subpkt data type*/ +#define DYNA_RMI4_SUBPKT(a)\ +do {\ + s->present = 0;\ + s->expected = 1;\ + s->size = sizeof(struct a);\ + s->data = kzalloc(sizeof(struct a), GFP_KERNEL);\ + if (!s->data)\ + goto alloc_error_subpkt;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %lu bytes of subptk data\n", sizeof(struct a));\ +} while(0) + +/*register number, number of subpkts, register size*/ +#define DYNA_RMI4_REG(n, s, rsz)\ +do {\ + r->r_number = n;\ + r->offset = -1;\ + r->updated = 0;\ + r->modified = 0;\ + r->expected = 1;\ + r->size = rsz;\ + r->nr_subpkts = s;\ + r->subpkt = kzalloc((s)*sizeof(struct synaptics_rmi4_subpkt), GFP_KERNEL);\ + if (!r->subpkt)\ + goto alloc_error_reg;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %d subptks for reg %d\n", s, n);\ +} while(0) + +/*func number, base address, offset, number of registers*/ +#define DYNA_RMI4_FUNC(n, a, o, r)\ +do {\ + dev_dbg(&rmi4_data->i2c_client->dev, "config_regs for func[%02x:%d] = %p\n", (n & 0xff), (n >> 8), f);\ + f->f_number = n;\ + f->base_addr = a;\ + f->query_offset = o;\ + f->nr_regs = r;\ + f->regs = kzalloc((r)*sizeof(struct synaptics_rmi4_packet_reg), GFP_KERNEL);\ + if (!f->regs)\ + goto alloc_error_func;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %d regs for func[%02x:%d]\n", r, (n & 0xff), (n >> 8));\ +} while(0) + +#define RMI4_GET_FUNC(n) (&rmi4_data->config_regs[n]) +#define RMI4_GET_PKT_REG(n) (&f->regs[n]) +#define RMI4_GET_SUBPKT(n) (&r->subpkt[n]) + +#define RMI4_DECLARE_FUNC(nf, addr, off, nr) \ +do {\ + f = RMI4_GET_FUNC(fnum++); snum = 0; rnum = 0;\ + DYNA_RMI4_FUNC(nf, addr, off, nr);\ +} while(0) + +#define RMI4_DECLARE_REG_NO_ALLOC(rn, ns) \ +do {\ + r = RMI4_GET_PKT_REG(rnum++); snum = 0;\ + DYNA_RMI4_REG(rn, ns, 0);\ +} while(0) + +#define RMI4_DECLARE_REG_ONE_SUBPKT(rn, prefix) \ +do {\ + r = RMI4_GET_PKT_REG(rnum++); snum = 0;\ + DYNA_RMI4_REG(rn, 1, sizeof(struct prefix##_s0_type));\ + s = RMI4_GET_SUBPKT(0);\ + DYNA_RMI4_SUBPKT(prefix##_s0_type);\ +} while(0) + +/*subpkt data type*/ +#define RMI4_DECLARE_SUBPKT(subpkt_data_type) \ +do {\ + s = RMI4_GET_SUBPKT(snum++);\ + DYNA_RMI4_SUBPKT(subpkt_data_type);\ +} while(0) + +#define NO_MATTER 0 +#define SLOT_IN_USE (rmi4_data->config_regs[i].f_number) + +static int synaptics_dsx_config_regs_alloc(struct synaptics_rmi4_data *rmi4_data) +{ + int i, snum, rnum, fnum = 0; + struct synaptics_rmi4_func_packet_regs *f; + struct synaptics_rmi4_packet_reg *r; + struct synaptics_rmi4_subpkt *s; + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F01 | CTRL_TYPE), 0, NO_MATTER, 5); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f01_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(2, f01_c2); + RMI4_DECLARE_REG_ONE_SUBPKT(3, f01_c3); + RMI4_DECLARE_REG_ONE_SUBPKT(5, f01_c5); + RMI4_DECLARE_REG_ONE_SUBPKT(9, f01_c9); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F12 | CTRL_TYPE), 0, 4, 8); + RMI4_DECLARE_REG_ONE_SUBPKT( 8, f12_c8); + RMI4_DECLARE_REG_ONE_SUBPKT(10, f12_c10); + RMI4_DECLARE_REG_ONE_SUBPKT(15, f12_c15); + RMI4_DECLARE_REG_ONE_SUBPKT(18, f12_c18); + RMI4_DECLARE_REG_NO_ALLOC(20, 2); + RMI4_DECLARE_SUBPKT(f12_c20_s0_type); + RMI4_DECLARE_SUBPKT(f12_c20_s1_type); + RMI4_DECLARE_REG_NO_ALLOC(23, 2); + RMI4_DECLARE_SUBPKT(f12_c23_s0_type); + RMI4_DECLARE_SUBPKT(f12_c23_s1_type); + RMI4_DECLARE_REG_ONE_SUBPKT(27, f12_c27); + RMI4_DECLARE_REG_ONE_SUBPKT(28, f12_c28); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F12 | DATA_TYPE), 0, 7, 3); + RMI4_DECLARE_REG_NO_ALLOC(1, 10); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_REG_ONE_SUBPKT( 4, f12_d4); + RMI4_DECLARE_REG_ONE_SUBPKT(15, f12_d15); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | DATA_TYPE), 0, NO_MATTER, 5); + RMI4_DECLARE_REG_ONE_SUBPKT( 6, f54_d6); + RMI4_DECLARE_REG_ONE_SUBPKT(10, f54_d10); + RMI4_DECLARE_REG_ONE_SUBPKT(14, f54_d14); + RMI4_DECLARE_REG_ONE_SUBPKT(16, f54_d16); + RMI4_DECLARE_REG_ONE_SUBPKT(17, f54_d17); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | CTRL_TYPE), 0, NO_MATTER, 3); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f54_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(2, f54_c2); + RMI4_DECLARE_REG_NO_ALLOC(95, 8); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | COMMAND_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f54_m0); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | QUERY_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(12, f54_q12); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F51 | CTRL_TYPE), 0, NO_MATTER, 3); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f51_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(1, f51_c1); + RMI4_DECLARE_REG_ONE_SUBPKT(4, f51_c4); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F51 | DATA_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f51_d0); + + return 0; + +alloc_error_subpkt: +alloc_error_reg: +alloc_error_func: + + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + int j, k; + + f = &rmi4_data->config_regs[i]; + if (!f->nr_regs) + continue; + for (j = 0; j < f->nr_regs; j++) { + if (!f->regs[j].r_number) + break; + r = &f->regs[j]; + for (k = 0 ; k < r->nr_subpkts; k++) { + if (!r->subpkt[k].size) + break; + kfree(r->subpkt[k].data); + } + kfree(r->subpkt); + kfree(r->data); + } + kfree(f->regs); + memset(f, 0, sizeof(*f)); + } + + return -ENOMEM; +} + +static unsigned char power_sleep_value = 1; +static struct synaptics_dsx_func_patch power_sleep_func_patch = { + .func = SYNAPTICS_RMI4_F01, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 7, + .data = &power_sleep_value, +}; + +static unsigned char force_update_value = 4; +static struct synaptics_dsx_func_patch force_update_func_patch = { + .func = SYNAPTICS_RMI4_F54 | COMMAND_TYPE, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 0, + .data = &force_update_value, +}; + +static struct synaptics_dsx_patch *force_update_patch; +static struct synaptics_dsx_patch *power_sleep_patch; + +static inline int register_ascii_to_type(unsigned char *symbol) +{ + int reg_type = CTRL_TYPE; + switch (*symbol) { + case 'Q': + reg_type = QUERY_TYPE; + break; + case 'D': + reg_type = DATA_TYPE; + break; + case 'M': + reg_type = COMMAND_TYPE; + break; + case 'C': + break; + default: return reg_type; + } + *symbol = '\0'; + return reg_type; +} + +static inline unsigned char register_type_to_ascii(int type) +{ + unsigned char ascii; + switch (type) { + case QUERY_TYPE: + ascii = 'Q'; + break; + case DATA_TYPE: + ascii = 'D'; + break; + case COMMAND_TYPE: + ascii = 'M'; + break; + case CTRL_TYPE: + default: + ascii = 'C'; + } + return ascii; +} + +#define F12_D1_IDX 0 +#define F12_D4_IDX 1 +#define F12_D15_IDX 2 + +static char *synaptics_dsx_find_patch(char *head, char *delimiters, char **next) +{ + char *patch = head; + for (; patch; patch = *next) { + *next = strpbrk(patch, delimiters); + if (*next) + *(*next)++ = '\0'; + patch = skip_spaces(patch); + if (!patch || !*patch || *patch == '#') + continue; + else + break; + } + return patch; +} + +static int parse_patch_data(char *value_p, u8 data[], long *bitmask_v) +{ + int i, error, num_of_bytes = 1; + long value_v; + char *bitmask_p; + + bitmask_p = strpbrk(value_p, "&"); + if (bitmask_p) { + *bitmask_p++ = '\0'; + error = kstrtol(bitmask_p, 16, bitmask_v); + if (error) + pr_err("bitmask conv error\n"); + } + + /* detect multiple bytes data */ + if ((strlen(value_p)%2) == 0) + num_of_bytes = strlen(value_p)/2; + + for (i = 0; i < num_of_bytes; i++) { + char hex_val[3] = {0}; + hex_val[0] = *value_p; + hex_val[1] = *(value_p + 1); + error = kstrtol(hex_val, 16, &value_v); + if (error) + pr_err("value conv error\n"); + data[i] = (u8)value_v; + value_p += 2; + } + + return num_of_bytes; +} + +static int synaptics_dsx_parse_patch(int func, char *query, + struct synaptics_dsx_patch *patch_ptr, bool expect_data) +{ + int i, error, rt_mod, function, num_of_bytes; + u8 data[64]; + char *next, *subpkt_p, *value_p, *pair = query; + long regstr_v, bitmask_v, subpkt_v; + struct synaptics_dsx_func_patch *patch; + + rt_mod = func & 0xf00; + function = func & 0xff; + for (i = 0; pair; pair = next, i++) { + num_of_bytes = 0; + bitmask_v = subpkt_v = 0L; + + pair = synaptics_dsx_find_patch(pair, ",", &next); + + value_p = strpbrk(pair, "="); + if (!value_p) { + pr_err("F%x[%d]: invalid syntax '%s'\n", + function, i, pair); + continue; + } + + /* make sure string is null terminated */ + *value_p = '\0'; + + subpkt_p = strpbrk(pair, ":"); + if (subpkt_p) { + *subpkt_p++ = '\0'; + error = kstrtol(subpkt_p, 10, &subpkt_v); + if (error) { + pr_err("F%x[%d]: subpacket conv error\n", + function, i); + continue; + } + } + + error = kstrtol(pair, 10, ®str_v); + if (error) { + pr_err("F%x[%d]: register conv error\n", + function, i); + continue; + } + + if (expect_data) + num_of_bytes = parse_patch_data( + ++value_p, data, &bitmask_v); + /* primitive data validation */ + if (function <= 0 || function > 255 || + regstr_v < 0 || regstr_v > 255) { + pr_err("F%x[%d]: invalid values\n", function, i); + continue; + } + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + + if (expect_data) { + patch->data = kzalloc(num_of_bytes, GFP_KERNEL); + if (!patch->data) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + memcpy(patch->data, data, num_of_bytes); + } + + patch->func = (u16)func; + patch->regstr = (u8)regstr_v; + patch->subpkt = (u8)subpkt_v; + patch->size = (u8)num_of_bytes; + patch->bitmask = (u8)bitmask_v; + + pr_debug("F%x[%d], register %d, subpkt %d, size %d, mask %x\n", + function, i, patch->regstr, patch->subpkt, + patch->size, patch->bitmask); + patch_ptr->cfg_num++; + list_add_tail(&patch->link, &patch_ptr->cfg_head); + } + + return 0; +} + +/* + * Special settings passed to the driver via device tree + * Example: "F12@11:2=1fa0;F1@1=1;" + * Where: + * F12 - decimal object number + * @ - delimits function number and following patch sets + * 11:2 - decimal register and subpacket numbers (0 offset) + * 26=a - register number and hex value to assign + * 2=1fa0 - 2 bytes of data to write to the register + */ +static void synaptics_dsx_parse_string(struct synaptics_rmi4_data *data, + const char *patch_ptr, + struct synaptics_dsx_patch *patch, + bool expect_data) +{ + struct device *dev = &data->i2c_client->dev; + long number_v; + char *patch_string; + char *config_p, *next, *patch_set; + int i, error, rt_mod = 0; + + patch_string = kstrdup(patch_ptr, GFP_KERNEL); + for (i = 0, patch_set = patch_string; patch_set; patch_set = next) { + patch_set = synaptics_dsx_find_patch(patch_set, ";\n", &next); + if (!patch_set) + break; + + dev_dbg(dev, "patch set %d: \"%s\"\n", i, patch_set); + config_p = strpbrk(patch_set, "@"); + + /* consider force update and power seetings here */ + if (!config_p) { + switch (*patch_set) { + case 'S': + patch->flags |= FLAG_POWER_SLEEP; + break; + case 'U': + patch->flags |= FLAG_FORCE_UPDATE; + break; + case 'W': + patch->flags |= FLAG_WAKEABLE; + break; + } + continue; + } + + if (*patch_set != 'F' && *patch_set != 'f') { + dev_err(dev, "invalid syntax '%s'\n", patch_set); + continue; + } + + rt_mod = register_ascii_to_type(config_p-1); + dev_dbg(dev, "register type modifier %d\n", rt_mod); + + /* strip non digits */ + *config_p++ = '\0'; + + error = kstrtol(++patch_set, 16, &number_v); + if (error) { + dev_err(dev, "kstrtol error %d\n", error); + continue; + } + + error = synaptics_dsx_parse_patch((int)number_v + rt_mod, + config_p, patch, expect_data); + if (error < 0) { + dev_err(dev, "invalid patch; parse error %d\n", error); + continue; + } + + i++; + } + kfree(patch_string); + if (patch->cfg_num) + dev_info(dev, "processed %d patch sets for %d functions\n", + patch->cfg_num, i); + else + dev_info(dev, "no valid patch sets found\n"); +} + +#define LAST_SUBPACKET_ROW_IND_MASK 0x80 +#define NR_SUBPKT_PRESENCE_BITS 7 + +static struct synaptics_rmi4_func_packet_regs *find_function( + struct synaptics_rmi4_data *rmi4_data, int f_number) +{ + struct synaptics_rmi4_func_packet_regs *regs = NULL; + int i; + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + if (rmi4_data->config_regs[i].f_number == f_number) { + regs = &rmi4_data->config_regs[i]; + break; + } + } + return regs; +} + +static struct synaptics_rmi4_packet_reg *find_packet_reg( + struct synaptics_rmi4_func_packet_regs *regs, int number) +{ + struct synaptics_rmi4_packet_reg *reg = NULL; + int r; + + for (r = 0; r < regs->nr_regs; r++) + if (number == regs->regs[r].r_number) { + reg = ®s->regs[r]; + break; + } + return reg; +} + +static struct synaptics_rmi4_subpkt *find_subpkt( + struct synaptics_rmi4_func_packet_regs *regs, int nr, int ns, + struct synaptics_rmi4_packet_reg **reg, void **data) +{ + struct synaptics_rmi4_packet_reg *lreg = find_packet_reg(regs, nr); + struct synaptics_rmi4_subpkt *subpkt = NULL; + + if (reg) + *reg = lreg; + + if (lreg && ns < lreg->nr_subpkts) { + subpkt = &lreg->subpkt[ns]; + if (subpkt->present && data) + *data = lreg->subpkt[ns].data; + } + return subpkt; +} + +int synaptics_rmi4_scan_f12_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs) +{ + unsigned char sz, mask; + int ii, jj, r, s, retval, max_reg_number; + unsigned short r_offset; + unsigned short addr = query_addr; + unsigned char data[255]; + bool r_exist[128] = {false}; + + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + regs->regs[r].size = 0; + /* reg data can be re-allocated */ + kfree(regs->regs[r].data); + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + /* subpkt data cannot be re-allocated */ + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = regs_base_addr; + retval = rmi4_data->i2c_read(rmi4_data, addr, &sz, 1); + dev_dbg(&rmi4_data->i2c_client->dev, + "size of reg presence = %d, addr 0x%x\n", sz, addr); + if (retval < 0) + return retval; + if (!sz) + return -EIO; + /* Scan register presence */ + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + if (!data[0]) { + dev_err(&rmi4_data->i2c_client->dev, + "packet register size greater 255 bytes not supported\n"); + return -ENOSYS; + } + ii = 1; + for (r = 0, r_offset = 0; ii < sz; ++ii) { + dev_dbg(&rmi4_data->i2c_client->dev, + "reg presence [%d] = 0x%02x\n", ii, data[ii]); + for (jj = 0, mask = 1; jj < 8; ++jj, ++r, mask <<= 1) { + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + bool present = (data[ii] & mask) != 0; + bool allocated = reg ? true : false; + if (present != allocated) + dev_dbg(&rmi4_data->i2c_client->dev, + " reg: r%d is%s present, but was%s expected\n", r, + present ? "" : " NOT", + allocated ? "" : " NOT"); + if (!present) { + r_exist[r] = false; + continue; + } + r_exist[r] = true; + if (allocated) + reg->offset = r_offset; + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d offset = %d\n", r, r_offset); + r_offset++; + } + } + + max_reg_number = r; + dev_dbg(&rmi4_data->i2c_client->dev, + "max register number = %d\n", max_reg_number); + + /* Scan register size and subpacket presence*/ + sz = data[0]; + dev_dbg(&rmi4_data->i2c_client->dev, + "subpacket presence sz = %d, addr 0x%x\n", sz, addr+1); + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + for (r = 0, ii = 0; r < max_reg_number && ii < sz; ++r) { + unsigned int reg_size, expected_reg_size; + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + if (!r_exist[r]) { + dev_dbg(&rmi4_data->i2c_client->dev, + "r%d does not exist; go to the next...\n", r); + continue; + } + reg_size = data[ii++]; + if (!reg_size) { + dev_err(&rmi4_data->i2c_client->dev, + "packet register size greater 255 bytes not supported\n"); + return -ENOSYS; + } + if (reg && reg->offset != -1) { + reg->data = kzalloc(reg_size, GFP_KERNEL); + if (reg->data) { + reg->size = reg_size; + dev_dbg(&rmi4_data->i2c_client->dev, + "r%d allocated %d bytes buffer @%p\n", + reg->r_number, reg->size, reg->data); + } else + dev_err(&rmi4_data->i2c_client->dev, + "cannot alloc register data buffer\n"); + } + dev_dbg(&rmi4_data->i2c_client->dev, "r%d sz = %d\n", r, reg_size); + expected_reg_size = 0; + for (s = 0; ii < sz;) { + dev_dbg(&rmi4_data->i2c_client->dev, + " subpkt presence [%d] = 0x%02x\n", + ii, data[ii]); + for (jj = 0, mask = 1; jj < NR_SUBPKT_PRESENCE_BITS; + ++jj, ++s, mask <<= 1) { + struct synaptics_rmi4_subpkt *subpkt = NULL; + bool present = (data[ii] & mask) != 0; + bool expected = false; + if (reg && s < reg->nr_subpkts) { + subpkt = reg->subpkt + s; + expected = subpkt->expected; + } + if (!present || !expected) { + if (present != expected) + dev_dbg(&rmi4_data->i2c_client->dev, + " subpacket:" + " r%d s%d is%s present," + " but was%s expected\n", + r, s, + present ? "" : " NOT", + expected ? "" : " NOT"); + continue; + } + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d.s%d is present\n", r, s); + if (subpkt) { + subpkt->present = true; + expected_reg_size += subpkt->size; + } + } + if ((data[ii++] & LAST_SUBPACKET_ROW_IND_MASK) == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, "no more subpackets\n"); + break; + } + } + if (reg && reg->expected && reg->size != expected_reg_size) { + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d size error: expected %d actual is %d\n", + r, expected_reg_size, reg->size); + } + } + return 0; +} + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + int s, retval, offset; + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + + if (!reg || !reg->size) { + dev_err(&rmi4_data->i2c_client->dev, + "register %d not found or zero size\n", r_number); + return -EINVAL; + } + + if (reg->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "touch register error: can't read r%d - not present\n", + r_number); + return -ENOENT; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "F%02x%c reading r%d{%X}, size %d to @%p\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + reg->r_number, regs->base_addr + reg->offset, + reg->size, reg->data); + + retval = rmi4_data->i2c_read(rmi4_data, + regs->base_addr + reg->offset, + reg->data, reg->size); + if (retval < 0) + return retval; + + for (s = 0, offset = 0; s < reg->nr_subpkts; ++s) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + s; + if (!subpkt->present) + continue; + + if ((reg->size - offset) < subpkt->size) { + dev_err(&rmi4_data->i2c_client->dev, + "subpkt size error: expected %d bytes," + " only %d present\n", subpkt->size, + (reg->size - offset)); + break; + } + + memcpy(subpkt->data, reg->data+offset, subpkt->size); + offset += subpkt->size; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int kk; + dev_dbg(&rmi4_data->i2c_client->dev, + "read r%d.s%d =\n", reg->r_number, s); + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } +#endif + } + return retval; +} + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs) +{ + int r; + int retval = 0; + + for (r = 0; r < regs->nr_regs; ++r) { + dev_dbg(&rmi4_data->i2c_client->dev, + "about to read F%02x%c register %d [%d/%d]\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + regs->regs[r].r_number, + regs->regs[r].expected, + regs->regs[r].offset >= 0); + if (regs->regs[r].expected && regs->regs[r].offset >= 0) { + retval = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, regs->regs[r].r_number); + if (retval < 0) + break; + } + } + return retval; +} + +static int synaptics_rmi4_write_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + int sz, ii, offset, retval; + + if (!reg || !reg->size) { + dev_err(&rmi4_data->i2c_client->dev, + "r%d does not exist or zero size\n", r_number); + return -EINVAL; + } + if (reg->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "touch register error: writing to absent register r%d\n", + r_number); + return -ENOENT; + } + + for (ii = 0, sz = 0, offset = 0; ii < reg->nr_subpkts; ++ii) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + ii; + if (!subpkt->present) + continue; + + if (subpkt->data && subpkt->size && + (offset + subpkt->size) <= reg->size) { + if ((reg->size - offset) >= subpkt->size) { + memcpy(reg->data + sz, subpkt->data, + subpkt->size); + sz += subpkt->size; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "expected %d bytes, only %d present\n", + offset + subpkt->size, reg->size); + break; + } + } else { + retval = -EINVAL; + dev_err(&rmi4_data->i2c_client->dev, + "cannot update subpacket %d: "\ + "sz=%d, offset=%d, data=%p, reg_sz=%d\n", + ii, subpkt->size, offset, + subpkt->data, reg->size); + goto out; + } + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "writing r%d{%X}, size %d to @%p\n", reg->r_number, + regs->base_addr + reg->offset, reg->size, reg->data); + + retval = rmi4_data->i2c_write(rmi4_data, + regs->base_addr + reg->offset, + reg->data, sz); +out: + return retval; +} + +#ifdef CONFIG_OF +static int synaptics_dsx_gpio_config( + struct synaptics_dsx_platform_data *pdata, bool enable) +{ + int retval = 0; + struct synaptics_rmi4_data *data = + container_of(pdata, struct synaptics_rmi4_data, board); + + if (enable) { + if (!gpio_is_valid(pdata->irq_gpio)) { + pr_err("invalid %s\n", INSTANCE_DBG(IRQ_GPIO_NAME, data)); + retval = -EINVAL; + } + retval = gpio_request(pdata->irq_gpio, + INSTANCE_RMI(IRQ_GPIO_NAME, data)); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + INSTANCE_DBG(IRQ_GPIO_NAME, data), pdata->irq_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_input(pdata->irq_gpio); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + INSTANCE_DBG(IRQ_GPIO_NAME, data), pdata->irq_gpio, retval); + goto err_gpio; + } + + if (!gpio_is_valid(pdata->reset_gpio)) + return retval; + + retval = gpio_request(pdata->reset_gpio, + INSTANCE_RMI(RESET_GPIO_NAME, data)); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + INSTANCE_DBG(RESET_GPIO_NAME, data), pdata->reset_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_output(pdata->reset_gpio, 1); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + INSTANCE_DBG(RESET_GPIO_NAME, data), pdata->reset_gpio, retval); + goto err_gpio; + } + } else { + gpio_free(pdata->irq_gpio); + if (!gpio_is_valid(pdata->reset_gpio)) + return retval; + gpio_free(pdata->reset_gpio); + } + +err_gpio: + return retval; +} + +static void synaptics_dsx_dt_parse_modifier(struct synaptics_rmi4_data *data, + struct device_node *parent, struct config_modifier *config, + const char *modifier_name, bool active) +{ + struct device *dev = &data->i2c_client->dev; + const char *patch_string; + char node_name[64]; + struct synaptics_clip_area clipa; + struct device_node *np_config; + int err; + + scnprintf(node_name, 63, "%s-%s", modifier_name, + active ? "active" : "suspended"); + np_config = of_find_node_by_name(parent, node_name); + if (!np_config) { + dev_dbg(dev, "%s: node does not exist\n", node_name); + return; + } + + err = of_property_read_string(np_config, "patch-data", + (const char **)&patch_string); + if (!err) { + struct synaptics_dsx_patch *p_data = + synaptics_dsx_init_patch(modifier_name); + + if (!p_data) { + dev_err(dev, "%s: alloc error\n", node_name); + goto clip_area; + } + synaptics_dsx_parse_string(data, patch_string, p_data, true); + if (active) + config->active = p_data; + else + config->suspended = p_data; + } + +clip_area: + + err = of_property_read_u32_array(np_config, "touch-clip-area", + (unsigned int *)&clipa, sizeof(clipa)/sizeof(unsigned int)); + if (!err) { + config->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!config->clipa) { + dev_err(dev, "clip area allocation failure\n"); + return; + } + memcpy(config->clipa, &clipa, sizeof(clipa)); + pr_notice("using touch clip area in %s\n", node_name); + } + + of_node_put(np_config); +} + +/* ASCII names order MUST match enum */ +static const char const *ascii_names[] = { "aod", "stats", "folio", + "charger", "wakeup", "fps", "query", "runtime", "na" +}; + +static int modifier_name2id(const char *name) +{ + int i, len2cmp, chosen = -1; + + for (i = 0; i < SYNA_MOD_MAX; i++) { + len2cmp = min_t(int, strlen(name), strlen(ascii_names[i])); + if (!strncmp(name, ascii_names[i], len2cmp)) { + chosen = i; + break; + } + } + return chosen; +} + +static inline const char *modifier_id2name(int id) +{ + return (id >= 0 && id < SYNA_MOD_MAX) ? + ascii_names[id] : ascii_names[SYNA_MOD_MAX]; +} + +static struct config_modifier *modifier_by_id( + struct synaptics_rmi4_data *data, int id) +{ + struct config_modifier *cm, *found = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + pr_debug("walk-thru: ptr=%p modifier[%s] id=%d\n", + cm, cm->name, cm->id); + if (cm->id == id) { + found = cm; + break; + } + } + up(&data->modifiers.list_sema); + pr_debug("returning modifier id=%d[%d]\n", found ? found->id : -1, id); + return found; +} + +static int synaptics_dsx_dt_parse_modifiers(struct synaptics_rmi4_data *data) +{ + struct device *dev = &data->i2c_client->dev; + struct device_node *np = dev->of_node; + struct device_node *np_mod; + int i, num_names, ret = 0; + char node_name[64]; + static const char **modifiers_names; + + sema_init(&data->modifiers.list_sema, 1); + INIT_LIST_HEAD(&data->modifiers.mod_head); + data->modifiers.mods_num = 0; + + num_names = of_property_count_strings(np, "config_modifier-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse config_modifier-names: %d\n", + num_names); + return -ENODEV; + } + + modifiers_names = devm_kzalloc(dev, + sizeof(*modifiers_names) * num_names, GFP_KERNEL); + if (!modifiers_names) + return -ENOMEM; + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "config_modifier-names", + i, &modifiers_names[i]); + if (ret < 0) { + dev_err(dev, "Cannot parse modifier-names: %d\n", ret); + return ret; + } + } + + data->modifiers.mods_num = num_names; + + for (i = 0; i < num_names; i++) { + int id; + struct config_modifier *cm, *config; + + scnprintf(node_name, 63, "config_modifier-%s", + modifiers_names[i]); + np_mod = of_find_node_by_name(np, node_name); + if (!np_mod) { + dev_warn(dev, "cannot find modifier node %s\n", + node_name); + continue; + } + + /* check for duplicate nodes in devtree */ + id = modifier_name2id(modifiers_names[i]); + pr_err("processing modifier %s[%d]\n", node_name, id); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == id) { + dev_err(dev, "duplicate modifier node %s\n", + node_name); + return -EFAULT; + } + } + /* allocate modifier's structure */ + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + list_add_tail(&config->link, &data->modifiers.mod_head); + config->name = modifiers_names[i]; + config->id = id; + + if (of_property_read_bool(np_mod, "enable-notification")) { + switch (id) { + case SYNA_MOD_FOLIO: + pr_notice("using folio detection\n"); + data->folio_detection_enabled = true; + break; + case SYNA_MOD_CHARGER: + pr_notice("using charger detection\n"); + data->charger_detection_enabled = true; + break; + case SYNA_MOD_FPS: + pr_notice("using fingerprint sensor detection\n"); + data->fps_detection_enabled = true; + break; + case SYNA_MOD_WAKEUP: + pr_notice("using wakeup detection\n"); + data->wakeup_detection_enabled = true; + break; + case SYNA_MOD_AOD: + case SYNA_MOD_STATS: + break; + + } + } else { + config->effective = true; + dev_dbg(dev, "modifier %s enabled unconditionally\n", + node_name); + } + + dev_dbg(dev, "processing modifier %s[%d]\n", + node_name, config->id); + + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], true); + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], false); + of_node_put(np_mod); + } + + return 0; +} + +static int synaptics_dsx_of_init(struct i2c_client *client, + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned int key_codes[SYN_MAX_BUTTONS]; + struct synaptics_dsx_platform_data *pdata = &rmi4_data->board; + struct device_node *np = client->dev.of_node; + struct synaptics_dsx_cap_button_map *button_map = NULL; + + retval = synaptics_dsx_dt_parse_modifiers(rmi4_data); + rmi4_data->patching_enabled = !retval; + + if (!force_update_patch && (force_update_patch = kzalloc( + sizeof(struct synaptics_dsx_patch), GFP_KERNEL))) { + force_update_patch->name = "force-update"; + force_update_patch->cfg_num = 1; + sema_init(&force_update_patch->list_sema, 1); + INIT_LIST_HEAD(&force_update_patch->cfg_head); + list_add(&force_update_func_patch.link, + &force_update_patch->cfg_head); + } + + if (!power_sleep_patch && (power_sleep_patch = kzalloc( + sizeof(struct synaptics_dsx_patch), GFP_KERNEL))) { + power_sleep_patch->name = "power-sleep"; + power_sleep_patch->cfg_num = 1; + sema_init(&power_sleep_patch->list_sema, 1); + INIT_LIST_HEAD(&power_sleep_patch->cfg_head); + list_add(&power_sleep_func_patch.link, + &power_sleep_patch->cfg_head); + } + + retval = of_get_gpio(np, 0); + if (retval < 0) { + dev_err(&client->dev, "get irq pin failure\n"); + return -EINVAL; + } + pdata->irq_gpio = retval; + + pdata->reset_gpio = ARCH_NR_GPIOS; + if (of_gpio_count(np) > 1) { + retval = of_get_gpio(np, 1); + if (retval < 0) { + dev_err(&client->dev, "get reset pin failure\n"); + return -EINVAL; + } + pdata->reset_gpio = retval; + } else + dev_info(&client->dev, "only found 1 gpio, assume no rst pin\n"); + + memset(key_codes, 0, sizeof(key_codes)); + retval = of_property_read_u32_array(np, "synaptics,key-buttons", + key_codes, SYN_MAX_BUTTONS); + if (!retval) { + int ii; + unsigned char *button_codes; + + button_map = kzalloc(sizeof(*button_map), GFP_KERNEL); + if (IS_ERR_OR_NULL(button_map)) { + dev_err(&client->dev, "button allocation failure\n"); + return -EINVAL; + } + + for (ii = 0; ii < SYN_MAX_BUTTONS; ii++) + if (key_codes[ii]) + button_map->nbuttons++; + + button_codes = kzalloc(button_map->nbuttons, GFP_KERNEL); + if (IS_ERR_OR_NULL(button_codes)) { + dev_err(&client->dev, "button allocation failure\n"); + kfree(button_map); + return -EINVAL; + } + + for (ii = 0; ii < button_map->nbuttons; ii++) + *(button_codes + ii) = (unsigned char)key_codes[ii]; + + button_map->map = button_codes; + } + + pdata->irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + pdata->cap_button_map = button_map; + + if (of_property_read_bool(np, "synaptics,gpio-config")) { + pr_notice("using gpio config\n"); + pdata->gpio_config = synaptics_dsx_gpio_config; + } + + if (of_property_read_bool(np, "synaptics,x-flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "synaptics,y-flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + if (of_property_read_bool(np, "synaptics,purge-enabled")) { + pr_notice("using purge\n"); + rmi4_data->purge_enabled = true; + } + + if (of_property_read_bool(np, "synaptics,splash-screen-mode")) { + pr_notice("splash screen mode\n"); + rmi4_data->splash_screen_mode = true; + } + + retval = of_property_read_u32(np, "synaptics,pm-qos-latency", + &rmi4_data->pm_qos_latency); + if (!retval) + pr_notice("pm qos latency is %d\n", + rmi4_data->pm_qos_latency); + + retval = of_property_read_u32(np, "synaptics,control-dsi", + &rmi4_data->ctrl_dsi); + if (!retval) + pr_notice("control DSI %d\n", rmi4_data->ctrl_dsi); + + retval = of_property_read_string(np, "synaptics,bound-display", + &rmi4_data->bound_display); + if (!retval) + pr_notice("bound to display '%s'\n", rmi4_data->bound_display); + + retval = of_property_read_string(np, "synaptics,class-entry-name", + &rmi4_data->class_entry_name); + if (!retval) + pr_notice("class entry name '%s'\n", rmi4_data->class_entry_name); + + return 0; +} +#else +static inline int synaptics_dsx_of_init(struct i2c_client *client) +{ + return -EINVAL; +} +#endif + +static void synaptics_dsx_validate_product_string(unsigned char *id) +{ + unsigned char *s, *wc; + bool do_once = false, do_all = false; + + for (s = wc = id; *s; s++) { + if (*s == '-') { + do_all = true; + continue; + } + if (!isdigit(*s) && isupper(*s)) { + *s = tolower(*s); + do_once = true; + } + if (do_all || do_once) { + *wc = *s; + do_once = false; + } + wc++; + } + *wc = 0; +} + +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +static inline unsigned long long timediff_ms( + struct timespec start, struct timespec end) +{ + struct timespec temp; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return (temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC); +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data); + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data); + +#if defined(CONFIG_DRM) +extern bool dsi_display_is_panel_enable(int id, int *probe_status, char **pname); +#endif + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_pm_qos_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_pm_qos_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static ssize_t synaptics_rmi4_test_irq_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_test_irq_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_test_irq_data_contig_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_test_irq_data_contig_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_reporting_ntouch_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_ntouch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query1 { + union { + struct { + unsigned char custom_map:1; + unsigned char non_compliant:1; + unsigned char reserved:1; + unsigned char has_sensor_id:1; + unsigned char has_charger_input:1; + unsigned char has_adjustable_doze:1; + unsigned char has_doze_holdoff:1; + unsigned char has_query42:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query42 { + union { + struct { + unsigned char has_ds4_queries:1; + unsigned char has_multi_physical:1; + unsigned char has_guest:1; + unsigned char has_swr:1; + unsigned char has_report_rate:1; + unsigned char has_recalibration:1; + unsigned char reserved:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char reserved:5; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_3_4 { + unsigned char transmitterbutton; + unsigned char receiverbutton; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char *button_int_enable; + unsigned char *multi_button; + struct synaptics_rmi4_f1a_control_3_4 *electrode_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char button_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + enum ic_modes mode; + bool inserted; + int (*func_init)(struct synaptics_rmi4_data *rmi4_data); + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data); + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); + int (*func_status)(struct synaptics_rmi4_data *rmi4_data); + struct list_head link; +}; + +static struct device_attribute attrs[] = { + __ATTR(reset, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, S_IRUGO, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, S_IRUGO, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, S_IRUSR | S_IRGRP, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), + __ATTR(drv_irq, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_drv_irq_show, + synaptics_rmi4_drv_irq_store), + __ATTR(reporting, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_show, + synaptics_rmi4_reporting_store), + __ATTR(reporting_ntouch, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_ntouch_show, + synaptics_rmi4_reporting_ntouch_store), + __ATTR(hw_irqstat, S_IRUSR | S_IRGRP, + synaptics_rmi4_hw_irqstat_show, + synaptics_rmi4_store_error), + __ATTR(ic_ver, S_IRUGO, + synaptics_rmi4_ic_ver_show, + synaptics_rmi4_store_error), + __ATTR(mod, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_show, + synaptics_rmi4_mod_store), + __ATTR(mod_sw, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_sw_show, + synaptics_rmi4_mod_sw_store), + __ATTR(query, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_query_show, + synaptics_rmi4_query_store), + __ATTR(tsi, S_IRUSR | S_IRGRP, + synaptics_rmi4_ud_show, + synaptics_rmi4_store_error), + __ATTR(pm_qos, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_pm_qos_show, + synaptics_rmi4_pm_qos_store), +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + __ATTR(test_irq_delay_ms, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_test_irq_delay_ms_show, + synaptics_rmi4_test_irq_delay_ms_store), + __ATTR(test_irq_data_contig, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_test_irq_data_contig_show, + synaptics_rmi4_test_irq_data_contig_store), +#endif +}; + +static DEVICE_ATTR(poweron, S_IRUSR | S_IRGRP, + synaptics_rmi4_poweron_show, synaptics_rmi4_store_error); + +static irqreturn_t synaptics_dsx_reset_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + up(&rmi4_data->reset_semaphore); + return IRQ_HANDLED; +} + +int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data, + bool requery) { + int retval; + unsigned char page_number; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + unsigned short cmd_reg_addr; + bool cmd_reg_found = false; + + if (!requery) { + cmd_reg_addr = rmi4_data->f01_cmd_base_addr; + cmd_reg_found = true; + goto sw_reset; + } + + for (page_number = 0; !cmd_reg_found && page_number < PAGES_TO_SERVICE; + page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F01) { + cmd_reg_addr = rmi_fd.cmd_base_addr; + cmd_reg_found = true; + break; + } + } + } + +sw_reset: + if (cmd_reg_found) { + uint8_t command = 1; + retval = synaptics_rmi4_i2c_write(rmi4_data, + cmd_reg_addr, + &command, sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue SW reset command, error = %d\n", + __func__, retval); + return retval; + } + return 0; + } else + return -ENOENT; + +} + + +int synaptics_dsx_ic_reset(struct synaptics_rmi4_data *rmi4_data, int reset) +{ + int retval; + unsigned long start = jiffies; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + bool has_rst_pin = gpio_is_valid(platform_data->reset_gpio); + bool need_to_free_irq = true; + + sema_init(&rmi4_data->reset_semaphore, 0); + + if (has_rst_pin && (reset == RMI4_HW_RESET)) { + gpio_set_value(platform_data->reset_gpio, 0); + udelay(1500); + } else if (!has_rst_pin || (reset == RMI4_SW_RESET)) { + retval = synaptics_rmi4_sw_reset(rmi4_data, true); + if (retval < 0) + return retval; + } + + retval = request_irq(rmi4_data->irq, synaptics_dsx_reset_irq, + IRQF_TRIGGER_RISING, rmi4_data->reset_name, + rmi4_data); + if (retval < 0) { + need_to_free_irq = false; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Failed to request irq: %d\n", + __func__, retval); + } + + if (has_rst_pin && (reset == RMI4_HW_RESET)) + gpio_set_value(platform_data->reset_gpio, 1); + + retval = down_timeout(&rmi4_data->reset_semaphore, msecs_to_jiffies(100)); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: timed out waiting for reset to complete\n", __func__); + retval = -ETIMEDOUT; + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ GPIO=%d after HW reset is %d\n", + __func__, platform_data->irq_gpio, + gpio_get_value(platform_data->irq_gpio)); + + retval = (int)jiffies_to_msecs(jiffies-start); + /* insert delay to ensure 1st i2c bus access succeeds */ + udelay(1000); + } + + if (need_to_free_irq) + free_irq(rmi4_data->irq, rmi4_data); + + /* perform SW reset if HW reset failed */ + if (need_to_free_irq && reset != RMI4_SW_RESET && retval == -ETIMEDOUT) + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + + return retval; +} + +static int synaptics_dsx_alloc_input(struct synaptics_rmi4_data *rmi4_data) +{ + char *suffix, input_name[64]; + + rmi4_data->input_dev = input_allocate_device(); + if (IS_ERR_OR_NULL(rmi4_data->input_dev)) + return PTR_ERR(rmi4_data->input_dev); + + if (rmi4_data->class_entry_name) + suffix = (char *)rmi4_data->class_entry_name; + else + suffix = CLASS_PRIMARY_FNAME; + + snprintf(input_name, sizeof(input_name), DRIVER_NAME ".%s", suffix); + rmi4_data->input_dev->name = kstrdup(input_name, GFP_KERNEL); + rmi4_data->input_dev->phys = INSTANCE_RMI(INPUT_PHYS_NAME, rmi4_data); + rmi4_data->input_dev->id.bustype = BUS_I2C; + rmi4_data->input_dev->dev.parent = &rmi4_data->i2c_client->dev; + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + /* enable power key injection */ + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_POWER); + + pr_debug("allocated input device '%s'\n", rmi4_data->input_dev->name); + + return 0; +} + +static void synaptics_copy_multiple_subpkts( + struct synaptics_rmi4_packet_reg *reg, + struct synaptics_dsx_func_patch *fp) +{ + int i, b2c, leftover = fp->size; + unsigned char *data_ptr = fp->data; + struct synaptics_rmi4_subpkt *subpkt; + + for (i = fp->subpkt; i < reg->nr_subpkts; i++) { + subpkt = reg->subpkt + i; + if (!subpkt->present || !subpkt->data) + continue; + b2c = min(leftover, (int)subpkt->size); + memcpy(subpkt->data, data_ptr, b2c); + pr_debug("copied %d bytes to subpkt %d\n", b2c, i); + data_ptr += subpkt->size; + leftover -= subpkt->size; + } + pr_debug("%s misalignement detected\n", !leftover ? "no" : ""); +} + +static void synaptics_dsx_patch_function( + struct synaptics_rmi4_data *rmi4_data, int f_number, + struct synaptics_dsx_patch *patch) +{ + int r, error, function; + unsigned char *destination; + unsigned char *value, rt_mod; + struct device *dev = &rmi4_data->i2c_client->dev; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_dsx_func_patch *fp; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_func_packet_regs *regs = + find_function(rmi4_data, f_number); + + function = regs->f_number & 0xff; + rt_mod = register_type_to_ascii(regs->f_number & 0xf00); + pr_debug("patching F%x%c\n", function, rt_mod); + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + if (fp->func != f_number) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x%c@%d not present\n", + function, rt_mod, fp->regstr); + continue; + } + if (fp->subpkt >= reg->nr_subpkts) { + pr_err("F%x%c@%d:%d not present\n", + function, rt_mod, fp->regstr, fp->subpkt); + continue; + } + + /* read register only once */ + if (!reg->updated) { + error = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, reg->r_number); + if (error < 0) + goto unlock_and_leave; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d before patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + /* value has been updated */ + reg->updated = true; + } + + subpkt = reg->subpkt + fp->subpkt; + destination = (unsigned char *)subpkt->data; + if (!subpkt->present || !subpkt->data || + subpkt->size < fp->size) { + pr_debug("F%x%c@%d:%d subpkt: size=%d, offset=%d; reg-size=%d; fp-size=%d\n", + function, rt_mod, + fp->regstr, fp->subpkt, + subpkt->size, subpkt->offset, + reg->size, fp->size); + /* If more than one consequitive subpacket within */ + /* the same packet register needs to be updated, */ + /* then patch might include data for all subpackets */ + /* combined into a single array. Then its size will */ + /* be more than individual subpacket size. It should */ + /* not be a problem as packet register is updated */ + /* all in once. And as long as size of data array */ + /* fits the register, update should be allowed */ + if (reg->size <= (subpkt->offset + fp->size)) { + pr_debug("F%x%c@%d:%d improperly allocated\n", + function, rt_mod, + fp->regstr, fp->subpkt); + continue; + } else { + synaptics_copy_multiple_subpkts(reg, fp); + /* whole patch has been applied already */ + destination = NULL; + } + } + if (fp->bitmask && fp->size == 1) { + value = (unsigned char *)subpkt->data; + pr_debug("patching by mask: source 0x%x\n", *value); + *value &= ~(fp->bitmask); + pr_debug("patching by mask: clear-ed 0x%x\n", *value); + *value |= *fp->data; + pr_debug("patching by mask: result 0x%x\n", *value); + /* value has been modified already */ + destination = NULL; + } + + if (destination) + memcpy(destination, fp->data, fp->size); + + /* value has been changed */ + reg->modified = true; + + dev_dbg(dev, "patched F%x%c@%d:%d, sz=%d\n", + function, rt_mod, fp->regstr, fp->subpkt, fp->size); + } + + /* update loop */ + for (r = 0; r < regs->nr_regs; r++) { + reg = ®s->regs[r]; + if (reg->updated && reg->modified) { +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d after patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + error = synaptics_rmi4_write_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) + dev_warn(dev, "F%x%c@%d patch write failed\n", + function, rt_mod, reg->r_number); + reg->modified = false; + } + reg->updated = false; + } + +unlock_and_leave: + up(&patch->list_sema); +} + +static void synaptics_dsx_enable_wakeup_source( + struct synaptics_rmi4_data *rmi4_data, bool enable) +{ + int error; + error = irq_set_irq_wake(rmi4_data->irq, (int)enable); + pr_debug("%s wakeup; rc=%d\n", enable ? "enabled" : "disabled", error); +} + +static const char * const synaptics_state_names[] = {"UNKNOWN", + "ACTIVE", "SUSPEND", "UNUSED", "STANDBY", "BL", "INIT", + "FLASH", "QUERY", "LOADING", "INVALID" }; + +static const char *synaptics_dsx_state_name(int state) +{ + int index = state < 0 || state > STATE_INVALID ? STATE_INVALID : state; + return synaptics_state_names[index]; +} + +static void synaptics_dsx_apply_modifiers( + struct synaptics_rmi4_data *rmi4_data, int state) +{ + bool wakeup = false, update = false, sleep = false; + int i; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + goto no_modifiers; + } + + down(&rmi4_data->modifiers.list_sema); + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + struct config_modifier *cm; + + /* skip query registers */ + if (rmi4_data->config_regs[i].f_number & QUERY_TYPE) + continue; + + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + struct synaptics_dsx_patch *patch = NULL; + + /* skip disabled modifiers */ + if (!cm->effective) + continue; + + if (state == STATE_ACTIVE) + patch = cm->active; + else if (state == STATE_SUSPEND) + patch = cm->suspended; + + /* skip effective modifier without patch */ + if (!patch) + continue; + + /* keep track of flags among effective modifiers */ + if (!update && (patch->flags & FLAG_FORCE_UPDATE)) + update = true; + if (!wakeup && (patch->flags & FLAG_WAKEABLE)) + wakeup = true; + if (!sleep && (patch->flags & FLAG_POWER_SLEEP)) + sleep = true; + /* finally apply patch */ + synaptics_dsx_patch_function(rmi4_data, + rmi4_data->config_regs[i].f_number, patch); + } + } + up(&rmi4_data->modifiers.list_sema); + + /* force update regardless the current state */ + if (update && force_update_patch) { + pr_debug("applying forced update patch!\n"); + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F54 | COMMAND_TYPE, force_update_patch); + } + + /* power mode and wakeability only on entering suspend */ + if (state == STATE_SUSPEND) { + if (wakeup) { + rmi4_data->suspend_is_wakeable = true; + synaptics_dsx_enable_wakeup_source(rmi4_data, true); + } else if (sleep && power_sleep_patch) { + pr_debug("applying sleep patch!\n"); + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F01, power_sleep_patch); + } + } + + if (!wakeup && rmi4_data->suspend_is_wakeable) { + rmi4_data->suspend_is_wakeable = false; + synaptics_dsx_enable_wakeup_source(rmi4_data, false); + } + +no_modifiers: + /* keep page 0 active */ + synaptics_rmi4_set_page(rmi4_data, 0); +} + +static int synaptics_dsx_get_state_safe(struct synaptics_rmi4_data *rmi4_data) +{ + int state; + mutex_lock(&(rmi4_data->state_mutex)); + state = rmi4_data->state; + mutex_unlock(&(rmi4_data->state_mutex)); + return state; +} + +static void synaptics_dsx_set_state_safe(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + mutex_lock(&(rmi4_data->state_mutex)); + rmi4_data->state = state; + mutex_unlock(&(rmi4_data->state_mutex)); +} + +int synaptics_dsx_wait_for_idle(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned long start_wait_jiffies = jiffies; + + do { + int current_state; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (!(current_state == STATE_INIT || + current_state == STATE_FLASH || + current_state == STATE_LOADING || + current_state == STATE_UNKNOWN)) + break; + + usleep_range(1000, 1000); + + } while (1); + + if ((jiffies - start_wait_jiffies)) + pr_info("entering suspend delayed for %ums\n", + jiffies_to_msecs(jiffies - start_wait_jiffies)); + return 0; +} + +static int synaptics_rmi4_f01_flashprog_status( + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + return retval < 0 ? retval : status.flash_prog; +} + +int synaptics_dsx_sensor_ready_state( + struct synaptics_rmi4_data *rmi4_data, bool standby) +{ + bool ui_mode; + int retval, state, i; + + for (i = 0; i < 10; ++i) { + retval = synaptics_rmi4_f01_flashprog_status(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d): flashprog bit=%d\n", + __func__, i, retval); + /* if simple flashprog bit check reports BL mode */ + /* then do not bother checking F34 flash status */ + if (!retval && + rmi4_data->get_status != synaptics_rmi4_f01_flashprog_status) { + retval = rmi4_data->get_status(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d): f34 flash status %d\n", + __func__, i, retval); + } + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: (%d) Failed to query touch ic status\n", + __func__, i); + return retval; + } + + ui_mode = retval == 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d) UI mode: %s\n", + __func__, i, ui_mode ? "true" : "false"); + if (ui_mode) + break; + + msleep(20); + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + if (!ui_mode && state == STATE_SUSPEND && rmi4_data->input_registered) { + /* expecting touch IC to enter UI mode based on */ + /* its previous known good state */ + dev_err(&rmi4_data->i2c_client->dev, + "%s: Timed out waiting for UI mode - UI mode forced\n", + __func__); + ui_mode = 1; + } + + if (ui_mode) { + state = standby ? STATE_STANDBY : STATE_ACTIVE; + } else + if (!(state == STATE_INIT || state == STATE_FLASH)) + state = STATE_BL; + + synaptics_dsx_sensor_state(rmi4_data, state); + + return 0; +} + +static void synaptics_dsx_enforce_modifiers( + struct synaptics_rmi4_data *rmi4_data, + struct config_modifier *modifier) +{ + int error, state = synaptics_dsx_get_state_safe(rmi4_data); + + if (state == STATE_ACTIVE) { + if (!modifier->active) + return; + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + /* perform SW reset to restore defaults */ + error = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + if (error < 0) + dev_err(&rmi4_data->i2c_client->dev, + "folio: sw reset failed %d\n", error); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); +} + +void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + if (synaptics_dsx_get_state_safe(rmi4_data) == state) + return; + + switch (state) { + case STATE_UNKNOWN: + rmi4_data->in_bootloader = false; + case STATE_FLASH: + case STATE_LOADING: + /* no special handling for these states */ + break; + + case STATE_SUSPEND: + synaptics_dsx_wait_for_idle(rmi4_data); + + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_SUSPEND); + + if (!rmi4_data->suspend_is_wakeable) + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + + case STATE_ACTIVE: + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_ACTIVE); + + if (rmi4_data->input_registered) + synaptics_rmi4_irq_enable(rmi4_data, true); + else { + synaptics_rmi4_irq_enable(rmi4_data, false); + pr_err("Active state without input device\n"); + } + break; + + case STATE_STANDBY: + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + + case STATE_BL: + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = true; + case STATE_INIT: + synaptics_rmi4_irq_enable(rmi4_data, false); + + if (!rmi4_data->in_bootloader) { + int retval; + unsigned char mode = 1; + unsigned char intr_status[MAX_INTR_REGISTERS]; + + /* deactivate touch by putting touch IC into */ + /* sleep mode to prevent interference of touch */ + /* processing with flashing */ + pr_debug("enter sleep mode\n"); + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &mode, + sizeof(mode)); + + if (retval < 0) + pr_notice("failed to enter sleep mode\n"); + + /* clear unhandled interrupt if any */ + pr_debug("clear interrupt\n"); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status[0], + rmi4_data->num_of_intr_regs); + + if (retval < 0) + pr_notice("failed to clear interrupt\n"); + } + break; + } + + dev_info(&rmi4_data->i2c_client->dev, "state change %s -> %s\n", + synaptics_dsx_state_name(rmi4_data->state), + synaptics_dsx_state_name(state)); + + synaptics_dsx_set_state_safe(rmi4_data, state); +} + +#define PM_ADD 0 +#define PM_UPDATE 1 +#define PM_REMOVE 2 + +static void synaptics_dsx_pm_qos(struct synaptics_rmi4_data *rmi4_data, + int update, bool use_default) +{ +#if 0 + if (irq_can_set_affinity(rmi4_data->irq)) + rmi4_data->pm_qos_irq.type = PM_QOS_REQ_AFFINE_IRQ; + else + rmi4_data->pm_qos_irq.type = PM_QOS_REQ_ALL_CORES; + + switch (update) { + case PM_REMOVE: + pm_qos_remove_request(&rmi4_data->pm_qos_irq); + break; + case PM_ADD: + pm_qos_add_request(&rmi4_data->pm_qos_irq, + PM_QOS_CPU_DMA_LATENCY, + rmi4_data->pm_qos_latency); + break; + case PM_UPDATE: + pm_qos_update_request(&rmi4_data->pm_qos_irq, + use_default ? PM_QOS_DEFAULT_VALUE : + rmi4_data->pm_qos_latency); + break; + } +#endif +} + +static void ud_set_id(struct touch_area_stats *tas, int id) +{ + tas->ud_id = id; +} + +static void ud_log_status(struct touch_area_stats *tas, bool down) +{ + struct touch_up_down *ud = tas->ud; + ssize_t id = tas->ud_id; + + if (id >= tas->ud_len) + tas->unknown_counter++; + + if (!down) { /* up */ + if (ud[id].up_down == 0x10) { + pr_debug("%s UP[%zu]\n", tas->name, id); + ud[id].up_down |= 1; + ud[id].mismatch--; + } + } else if (down) { /* down */ + if (ud[id].up_down == 0) { + ud[id].up_down |= (1 << 4); + pr_debug("%s DOWN[%zu]\n", tas->name, id); + ud[id].mismatch++; + } else if (ud[id].up_down == 0x10) + return; + } + + if (ud[id].up_down == 0x11) { + pr_debug("%s CLEAR[%zu]\n", tas->name, id); + ud[id].up_down = 0; + ud[id].counter++; + } +} + +static void TSI_state(struct synaptics_rmi4_data *rmi4_data, + unsigned int tool, bool status) +{ + ud_log_status(&rmi4_data->touch_ud_stats, status); + input_mt_report_slot_state(rmi4_data->input_dev, tool, status); +} + +static void TSI_id(struct synaptics_rmi4_data *rmi4_data, int id) +{ + ud_set_id(&rmi4_data->touch_ud_stats, id); + input_mt_slot(rmi4_data->input_dev, id); +} + +#define INPUT_REPORT_MT_SLOT_STATE TSI_state +#define INPUT_REPORT_MT_SLOT TSI_id + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t total = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + total += scnprintf(buf + total, PAGE_SIZE - total, "display:\n"); + for (i = 0; i < rmi4_data->touch_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + rmi4_data->touch_ud_stats.ud[i].counter, + rmi4_data->touch_ud_stats.ud[i].mismatch); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, PAGE_SIZE - total, + "buttons:\n"); + for (i = 0; i < rmi4_data->button_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + rmi4_data->button_ud_stats.ud[i].counter, + rmi4_data->button_ud_stats.ud[i].mismatch); + } + return total; +} + +static ssize_t synaptics_dsx_ud_stat(struct synaptics_rmi4_data *rmi4_data, + char *buf, ssize_t size) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, size - total, "screen: "); + for (i = 0; i < rmi4_data->touch_ud_stats.ud_len; i++) + if (rmi4_data->touch_ud_stats.ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + rmi4_data->touch_ud_stats.ud[i].counter, + rmi4_data->touch_ud_stats.ud[i].mismatch); + else if (rmi4_data->touch_ud_stats.ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + rmi4_data->touch_ud_stats.ud[i].counter); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, size - total, "buttons: "); + for (i = 0; i < rmi4_data->button_ud_stats.ud_len; i++) + if (rmi4_data->button_ud_stats.ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + rmi4_data->button_ud_stats.ud[i].counter, + rmi4_data->button_ud_stats.ud[i].mismatch); + else if (rmi4_data->button_ud_stats.ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + rmi4_data->button_ud_stats.ud[i].counter); + } + return total; +} + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->rmi4_mod_info.product_id_string); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int firmware_id; + unsigned int config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + return scnprintf(buf, PAGE_SIZE, "%x-%08x\n", firmware_id, config_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->in_bootloader); +} + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + switch (gpio_get_value(rmi4_data->board.irq_gpio)) { + case 0: + return scnprintf(buf, PAGE_SIZE, "Low\n"); + case 1: + return scnprintf(buf, PAGE_SIZE, "High\n"); + default: + pr_err("Failed to get GPIO for irq %d\n", rmi4_data->irq); + return scnprintf(buf, PAGE_SIZE, "Unknown\n"); + } +} + +#define SPRINTF_PAGE(fmt, args...) {\ + blen += scnprintf(buf + blen, PAGE_SIZE - blen, fmt, ##args);\ + } + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (rmi4_data->rctrl.reporting_stopped) + length = scnprintf(buf, PAGE_SIZE, "STOPPED(%u)\n", + rmi4_data->rctrl.events_cnt); + else + length = scnprintf(buf, PAGE_SIZE, "RUNNING\n"); + return length; +} + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (*buf == 's' || *buf == 'S') { + rmi4_data->rctrl.reporting_stopped = true; + } else if (*buf == 'r' || *buf == 'R') { + rmi4_data->rctrl.reporting_stopped = false; + } else { + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + rmi4_data->rctrl.reporting_stopped = value == 0; + } + if (rmi4_data->rctrl.reporting_stopped) + rmi4_data->rctrl.events_cnt = 0; + return count; +} + +static ssize_t synaptics_rmi4_reporting_ntouch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + down(&rmi4_data->rctrl.ctrl_sema); + length = scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->rctrl.max_seen); + rmi4_data->rctrl.max_seen = 0; + up(&rmi4_data->rctrl.ctrl_sema); + + return length; +} + +static ssize_t synaptics_rmi4_reporting_ntouch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + down(&rmi4_data->rctrl.ctrl_sema); + rmi4_data->rctrl.expected = (int)value; + rmi4_data->rctrl.max_seen = 0; + up(&rmi4_data->rctrl.ctrl_sema); + + return count; +} + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + /* Disable irq */ + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + case 1: + /* Enable irq */ + synaptics_rmi4_irq_enable(rmi4_data, true); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static ssize_t synaptics_rmi4_pm_qos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->pm_qos_latency); +} + +static ssize_t synaptics_rmi4_pm_qos_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + uint32_t old; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + old = rmi4_data->pm_qos_latency; + + switch (value) { + case 0: + rmi4_data->pm_qos_latency = PM_QOS_DEFAULT_VALUE; + break; + default: + rmi4_data->pm_qos_latency = value; + break; + } + + if (old != rmi4_data->pm_qos_latency && + atomic_read(&rmi4_data->touch_stopped) == 0) { + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, false); + pr_debug("set pm qos latency to %d\n", + rmi4_data->pm_qos_latency); + } + + return count; +} + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static ssize_t synaptics_rmi4_test_irq_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->test_irq_delay_ms); +} + +static ssize_t synaptics_rmi4_test_irq_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + err = kstrtouint(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + rmi4_data->test_irq_delay_ms = value; + return count; +} + +static ssize_t synaptics_rmi4_test_irq_data_contig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->test_irq_data_contig); +} + +static ssize_t synaptics_rmi4_test_irq_data_contig_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + err = kstrtouint(buf, 10, &value); + if (err < 0 || !(value == 0 || value == 1)) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + if (rmi4_data->test_irq_data_contig != value) { + rmi4_data->test_irq_data_contig = value; + synaptics_rmi4_irq_enable(rmi4_data, false); + /* requery for change to take effect */ + if (synaptics_rmi4_query_device(rmi4_data) < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + } + synaptics_rmi4_irq_enable(rmi4_data, true); + } + return count; +} +#endif + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int build_id, config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + batohui(&build_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + return scnprintf(buf, PAGE_SIZE, + "%s%s\n%s%x\n%s%x\n", + "Product ID: ", rmi->product_id_string, + "Build ID: ", build_id, + "Config ID: ", config_id); +} + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&rmi4_data->touch_stopped) == 0 && + rmi4_data->flash_enabled); +} + +static ssize_t synaptics_dsx_patch_dump( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + int i; + unsigned int data_size = fp->size; + unsigned char *value = fp->data; + + blen += scnprintf(pout + blen, msize - blen, + "F%02x%c@%d:%d=", fp->func & 0xff, + register_type_to_ascii(fp->func & 0xf00), + fp->regstr, fp->subpkt); + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + if (fp->bitmask) + blen += scnprintf(pout + blen, msize - blen, + "&%02x", fp->bitmask); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + + if (patch->flags & FLAG_FORCE_UPDATE) + blen += scnprintf(pout + blen, msize - blen, "U; "); + if (patch->flags & FLAG_WAKEABLE) + blen += scnprintf(pout + blen, msize - blen, "W; "); + if (patch->flags & FLAG_POWER_SLEEP) + blen += scnprintf(pout + blen, msize - blen, "S; "); + blen += scnprintf(pout + blen, msize - blen, "\n"); + + return blen; +} + +static ssize_t synaptics_dsx_patch_query( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + int i, error, function; + unsigned short data_addr; + unsigned int data_size; + unsigned char *value, rt_mod; + + regs = find_function(rmi4_data, fp->func); + if (!regs) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x@%d not present\n", fp->func, fp->regstr); + continue; + } + + rt_mod = register_type_to_ascii(fp->func & 0xf00); + function = fp->func & 0xff; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) { + pr_err("F%x@%d register read failed\n", + function, fp->regstr); + continue; + } + /* calculate register address */ + data_addr = regs->base_addr + reg->offset; + /* dump whole register if subpacket is 255 */ + if (fp->subpkt == 0xff) { + value = reg->data; + data_size = reg->size; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d{%X}=", + function, rt_mod, fp->regstr, data_addr); + } else { + struct synaptics_rmi4_subpkt *subpkt; + + subpkt = reg->subpkt + fp->subpkt; + value = (unsigned char *)subpkt->data; + data_size = subpkt->size; + data_addr += subpkt->offset; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d:%d{%X}=", + function, rt_mod, fp->regstr, + fp->subpkt, data_addr); + } + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + blen += scnprintf(pout + blen, msize - blen, "\n"); + return blen; +} + +static ssize_t modifiers_show(struct synaptics_rmi4_data *rmi4_data, + char *buf, size_t count, bool show_data, bool show_query) +{ + struct config_modifier *cm; + ssize_t (*handler)(struct synaptics_rmi4_data *, + struct synaptics_dsx_patch *, + char *, ssize_t); + ssize_t added, blen = 0; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + return 0; + } + + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + const char *name = cm->name ? cm->name : "na"; + bool is_query = cm->id == SYNA_MOD_QUERY; + + /* skip printing modifiers, but query */ + if (!is_query && show_query) + continue; + blen += scnprintf(buf + blen, count - blen, "%c[%s]\n", + cm->effective ? '+' : '-', name); + if (!show_data) + continue; + + if (is_query && show_query) + handler = &synaptics_dsx_patch_query; + else + handler = &synaptics_dsx_patch_dump; + + if (cm->active) { + blen += scnprintf(buf + blen, + count - blen, " [active]"); + added = (*handler)(rmi4_data, cm->active, + buf + blen, count - blen); + blen += added; + count -= added; + } + + if (cm->clipa) { + blen += scnprintf(buf + blen, count - blen, + " [clip]<%u,%u,%u,%u>%s\n", + cm->clipa->xul_clip, cm->clipa->yul_clip, + cm->clipa->xbr_clip, cm->clipa->ybr_clip, + cm->clipa->inversion ? "in" : "out"); + } + + if (cm->suspended) { + blen += scnprintf(buf + blen, + count - blen, " [suspended]"); + added = (*handler)(rmi4_data, cm->suspended, + buf + blen, count - blen); + blen += added; + count -= added; + } + } + up(&rmi4_data->modifiers.list_sema); + + return blen; +} + +#define NAME_ID 0 +#define ACTION_ID 1 + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch **patch_ptr, *patch; + struct config_modifier *cm; + bool clear_only = false; + int id, bytes_left, ntokens = 0; + char *internal, *ptr, *token[2] = {0}; + + internal = kstrdup(buf, GFP_KERNEL); + for (ptr = internal; ntokens < 2; ntokens++) { + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, '['); + if (!ptr) + break; + token[ntokens] = ++ptr; /* advance to the name */ + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, ']'); + if (ptr) + *ptr++ = 0; + dev_dbg(dev, "token[%d]='%s'\n", ntokens, token[ntokens]); + } + + if (*buf != '[' && ntokens != 2) { + dev_err(dev, "invalid modifier syntax\n"); + count = -EINVAL; + goto leave_now; + } + + id = modifier_name2id(token[NAME_ID]); + switch (id) { + case SYNA_MOD_QUERY: + dev_err(dev, "use query attribute instead\n"); + count = -EINVAL; + break; + case -1: + dev_err(dev, "[%s] is invalid modifier\n", token[NAME_ID]); + count = -EINVAL; + break; + } + + if (count == -EINVAL) + goto leave_now; + + /* pointer has been advanced in the loop above to point to patch */ + if (*ptr == '\n') + clear_only = true; + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + if (clear_only) + goto leave_now; + + dev_info(dev, "allocating modifier [%s]\n", token[NAME_ID]); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + count = -ENOMEM; + goto leave_now; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[id]; + cm->id = id; + } + + if (!strncmp(token[ACTION_ID], "clip", 4)) { + bool clipping_is_active = false; + struct synaptics_clip_area clipa; + + if (cm->clipa) { + if (rmi4_data->clipping_on && + rmi4_data->clipa == cm->clipa) { + /* if currently effective clipping */ + /* is the one being edited */ + rmi4_data->clipa = NULL; + clipping_is_active = true; + } + kfree(cm->clipa); + cm->clipa = NULL; + } + + if (clear_only) { + if (clipping_is_active) { + rmi4_data->clipping_on = false; + pr_debug("removed [clip] from modifier [%s]\n", + token[NAME_ID]); + } + goto leave_now; + } + + ntokens = sscanf(ptr, "<%u %u %u %u %u>", &clipa.xul_clip, + &clipa.yul_clip, &clipa.xbr_clip, + &clipa.ybr_clip, &clipa.inversion); + if (ntokens != 5) { + dev_err(dev, "Invalid clip area modifier\n"); + goto leave_now; + } + + if (!cm->clipa) { + cm->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!cm->clipa) { + count = -ENOMEM; + goto leave_now; + } + } + + memcpy(cm->clipa, &clipa, sizeof(clipa)); + + if (clipping_is_active) + rmi4_data->clipa = cm->clipa; + + pr_debug("added [clip] area to modifier [%s]\n", + token[NAME_ID]); + goto leave_now; + } + + if (!strncmp(token[ACTION_ID], "active", 6)) + patch_ptr = &cm->active; + else + patch_ptr = &cm->suspended; + + if (*patch_ptr) { + synaptics_dsx_free_patch(*patch_ptr); + *patch_ptr = NULL; + } + + if (clear_only) { + pr_debug("removed [%s] from modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); + goto apply_and_leave; + } + + patch = synaptics_dsx_init_patch(token[ACTION_ID]); + if (!patch) { + count = -ENOMEM; + goto leave_now; + } + + synaptics_dsx_parse_string(rmi4_data, ptr, patch, true); + *patch_ptr = patch; + pr_debug("added [%s] patch to modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); +apply_and_leave: + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); +leave_now: + kfree(internal); + return count; +} + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, true, false); +} + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int id; + bool set_op; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm; + char mod_name[32]; + + if (*buf != '+' && *buf != '-') { + dev_err(dev, "invalid modifier syntax\n"); + return -EINVAL; + } + + set_op = *buf == '-' ? false : true; + if (sscanf(buf + 1, "[%s]", mod_name) != 1) { + dev_err(dev, "unable to read modifier name\n"); + return -EINVAL; + } + + id = modifier_name2id(mod_name); + if (id == -1) { + dev_err(dev, "invalid modifier name '%s'\n", mod_name); + return -EINVAL; + } + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + dev_err(dev, "modifier with id=%d does not exist\n", id); + return -EINVAL; + } + + if (cm->effective != set_op) { + cm->effective = set_op; + pr_debug("set [%s] %s\n", cm->name, + cm->effective ? "on" : "off"); + + if (cm->effective) { + if (cm->clipa) { + rmi4_data->clipping_on = true; + rmi4_data->clipa = cm->clipa; + pr_debug("enable clipping in [%s]\n", cm->name); + } + } else if (cm->clipa && rmi4_data->clipa == cm->clipa) { + rmi4_data->clipping_on = false; + rmi4_data->clipa = NULL; + pr_debug("disable clipping in [%s]\n", cm->name); + } + + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); + } + return count; +} + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, false, false); +} + +/* NOTE: query is stored in active */ +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch *patch; + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + if (!cm) { + dev_info(dev, "allocating modifier [%s]\n", + modifier_id2name(SYNA_MOD_QUERY)); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + dev_err(dev, "Cannot allocate query modifier\n"); + return -ENOMEM; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[SYNA_MOD_QUERY]; + cm->id = SYNA_MOD_QUERY; + } + + if (*buf == '\n') { + if (cm->active) { + synaptics_dsx_free_patch(cm->active); + cm->active = NULL; + pr_debug("discarded current query\n"); + } + goto leave_now; + } + + if (*buf != 'F') { + dev_err(dev, "invalid query syntax\n"); + return -EINVAL; + } + + patch = synaptics_dsx_init_patch(ascii_names[SYNA_MOD_QUERY]); + if (!patch) + return -ENOMEM; + + synaptics_dsx_parse_string(rmi4_data, buf, patch, false); + cm->active = patch; + pr_debug("new query added\n"); + +leave_now: + return count; +} + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + return !cm ? 0 : modifiers_show(rmi4_data, buf, PAGE_SIZE, true, true); +} + + /** + * synaptics_rmi4_set_page() + * + * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write(). + * + * This function writes to the page select register to switch to the + * assigned page. + */ +static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned int address) +{ + int retval = 0; + bool enforce_page_select = false; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = rmi4_data->i2c_client; + + if (address & PAGE_SELECT_ENFORCE) { + enforce_page_select = true; + dev_dbg(&i2c->dev, "%s: Enforced page select\n", __func__); + address &= ~PAGE_SELECT_ENFORCE; + } + + page = ((address >> 8) & MASK_8BIT); + if (page != rmi4_data->current_page || enforce_page_select) { + buf[0] = MASK_8BIT; + buf[1] = page; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN); + if (retval != PAGE_SELECT_LEN) { + dev_err(&i2c->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + usleep_range(5000, 5000); + } else { + rmi4_data->current_page = page; + break; + } + } + } else + return PAGE_SELECT_LEN; + return (retval == PAGE_SELECT_LEN) ? retval : -EIO; +} + +int alloc_buffer(struct temp_buffer *tb, size_t count) +{ + if (tb->buf_size != 0) { + kfree(tb->buf); + tb->buf = NULL; + tb->buf_size = 0; + } + + if (count > MAX_READ_WRITE_SIZE) { + pr_err("%s: Max buffer size exceeded\n", __func__); + return 1; + } + + if (count < MIN_READ_WRITE_BUF_SIZE) + count = MIN_READ_WRITE_BUF_SIZE; + + tb->buf = kzalloc(count, GFP_KERNEL); + if (!tb->buf) + return 1; + + tb->buf_size = count; + return 0; +} +EXPORT_SYMBOL(alloc_buffer); + + /** + * synaptics_rmi4_i2c_read() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function reads data of an arbitrary length from the sensor, + * starting from an assigned register address of the sensor, via I2C + * with a retry mechanism. + */ +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry = SYN_I2C_RETRY_TIMES; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = rmi4_data->i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & MASK_8BIT; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 2) == 2) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + usleep_range(5000, 5000); + } +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + + /** + * synaptics_rmi4_i2c_write() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function writes data of an arbitrary length to the sensor, + * starting from an assigned register address of the sensor, via I2C with + * a retry mechanism. + */ +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry = SYN_I2C_RETRY_TIMES; + struct temp_buffer *tb = &rmi4_data->write_buf; + struct i2c_msg msg[1]; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + if (tb->buf_size < (length+1) && alloc_buffer(tb, length+1) != 0) { + retval = -ENOMEM; + goto exit; + } + + msg[0].addr = rmi4_data->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = length + 1; + msg[0].buf = tb->buf; + + *(tb->buf) = addr & MASK_8BIT; + memcpy(tb->buf+1, &data[0], length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + +static int synaptics_rmi4_f12_wakeup_gesture( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_func_packet_regs *regs = + find_function(rmi4_data, SYNAPTICS_RMI4_F12 | DATA_TYPE); + struct f12_d4_s0_type *f12_d4_0; + struct synaptics_rmi4_packet_reg *reg_data_4; + struct synaptics_rmi4_subpkt *subpkt = + find_subpkt(regs, 4, 0, ®_data_4, (void **)&f12_d4_0); + + if (subpkt && subpkt->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "unable to clear wakeup gesture IRQ\n"); + return -EINVAL; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.data_base + reg_data_4->offset, + &f12_d4_0->gesture, sizeof(*f12_d4_0)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "failure clearing wakeup gesture IRQ rc=%d\n", retval); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "wakeup gesture status=0x%02x\n", f12_d4_0->gesture); + + if (f12_d4_0->gesture & DOUBLE_TAP_GESTURE) { + /* emulate power key press */ + input_report_key(rmi4_data->input_dev, KEY_POWER, 1); + input_report_key(rmi4_data->input_dev, KEY_POWER, 0); + input_sync(rmi4_data->input_dev); + + dev_dbg(&rmi4_data->i2c_client->dev, + "DBL_TAP wakeup gesture detected\n"); + } + + return 0; +} + +static void analyze_finger_data(struct synaptics_rmi4_data *rmi4_data, + struct f12_d1_s0_type *data) +{ + int id, finger_cnt = 0; + + id = finger_cnt; + for (id = 0; id < rmi4_data->num_of_fingers; id++, data++) + if (data->type_and_stylus) + finger_cnt++; + + down(&rmi4_data->rctrl.ctrl_sema); + if (finger_cnt >= rmi4_data->rctrl.expected && + finger_cnt > rmi4_data->rctrl.max_seen) + rmi4_data->rctrl.max_seen = finger_cnt; + up(&rmi4_data->rctrl.ctrl_sema); +} + + /** + * synaptics_rmi4_f12_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $12 + * finger data has been detected. + * + * This function reads the Function $12 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned short data_addr; + unsigned short data_size; + int x; + int y; + int p; + int w; + int id; +#ifdef USE_TIME_SYNC_EVENTS + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#endif + struct f12_d1_s0_type *finger_data; + struct f12_d1_s0_type *finger_data_buf; + struct synaptics_rmi4_packet_reg *reg_data_1 = + &rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX]; + static int print_xy = -1; +/* + if (unlikely(print_xy == -1)) { + print_xy = 0; + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) + print_xy = 1; + } +*/ + if (atomic_read(&rmi4_data->panel_off_flag)) + return 0; + + fingers_to_process = rmi4_data->num_of_fingers; + + if (rmi4_data->touch_data_contiguous) + finger_data_buf = (struct f12_d1_s0_type *)(rmi4_data->touch_data + + rmi4_data->num_of_intr_regs); + else { + data_addr = fhandler->full_addr.data_base; + data_size = fingers_to_process * + fhandler->size_of_data_register_block; + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr + reg_data_1->offset, + reg_data_1->data, + data_size); + if (retval < 0) + return 0; + + finger_data_buf = (struct f12_d1_s0_type *)reg_data_1->data; + } + + /* count valid event */ + rmi4_data->rctrl.events_cnt++; + if (rmi4_data->rctrl.reporting_stopped) { + analyze_finger_data(rmi4_data, finger_data_buf); + return 0; + } +#ifdef USE_TIME_SYNC_EVENTS + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); +#endif + finger_data = finger_data_buf; + for (finger = 0; finger < fingers_to_process; finger++, finger_data++) { + finger_status = finger_data->type_and_stylus; + if (finger_status) { + x = finger_data->x_lsb | (finger_data->x_msb << 8); + y = finger_data->y_lsb | (finger_data->y_msb << 8); + p = w = finger_data->z; + id = finger; + + if (rmi4_data->board.x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board.y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (inside == rmi4_data->clipa->inversion) { + /* Touch might still be active, but we're */ + /* sending release anyway to avoid touch */ + /* stuck at the last reported position. */ + /* Driver will suppress reporting to UI */ + /* touch events from within clipping area */ + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); + + dev_dbg(&rmi4_data->i2c_client->dev, + "finger id-%d (%d,%d) within clipping area\n", + finger, x, y); + + continue; + } + } +#ifdef TYPE_B_PROTOCOL + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, finger_status); +#endif + if (print_xy) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + } + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, p); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, w); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + /* Touch no longer active, close out slot */ + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); +#endif + } + } + + if (!touch_count) { +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + + return touch_count; +} + + /** + * synaptics_rmi4_f11_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char data_reg_blk_size; + unsigned char finger_status_reg[3]; + unsigned char data[F11_STD_DATA_LEN]; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int z; +#ifdef USE_TIME_SYNC_EVENTS + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#endif + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + data_reg_blk_size = fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + /* count valid event */ + rmi4_data->rctrl.events_cnt++; + if (rmi4_data->rctrl.reporting_stopped) + return 0; + + if (atomic_read(&rmi4_data->panel_off_flag)) + return 0; + +#ifdef USE_TIME_SYNC_EVENTS + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); +#endif + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * data_reg_blk_size); + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_offset, + data, + data_reg_blk_size); + if (retval < 0) + return 0; + + x = (data[0] << 4) | (data[2] & MASK_4BIT); + y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); + wx = (data[3] & MASK_4BIT); + wy = (data[3] >> 4) & MASK_4BIT; + z = data[4]; + + if (rmi4_data->board.x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board.y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (!inside) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%d,%d ouside clipping area\n", + x, y); + continue; + } + } +#ifdef TYPE_B_PROTOCOL + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, finger_status); +#endif + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n" + "z = %d\n", + __func__, finger, + x, y, wx, wy, z); + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, z); + +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + /* Touch no longer active, close out slot */ + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); +#endif + } + } + +#ifndef TYPE_B_PROTOCOL + if (!touch_count) + input_mt_sync(rmi4_data->input_dev); +#endif + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + + return touch_count; +} + +static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (rmi4_data->tsb_buff_clean_flag) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + rmi4_data->tsb_buff_clean_flag = 0; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read button data registers\n", + __func__); + return; + } + + if (atomic_read(&rmi4_data->panel_off_flag)) + return; + + data = f1a->button_data_buffer; + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + ud_set_id(&rmi4_data->button_ud_stats, button); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); +#endif + } + + input_sync(rmi4_data->input_dev); + + return; +} + +static void synaptics_rmi4_f01_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return; + + switch (status.status_code) { + case 0x00: + dev_info(&rmi4_data->i2c_client->dev, + "%s: No error.\n", __func__); + break; + + case 0x01: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC reset complete.\n", __func__); + break; + + case 0x02: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC configuration error--%s.\n", + __func__, "check platform settings"); + break; + + case 0x03: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC device failure.\n", __func__); + break; + + case 0x04: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Configuration CRC failure.\n", __func__); + break; + + case 0x05: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Firmware CRC failure.\n", __func__); + break; + + case 0x06: + dev_info(&rmi4_data->i2c_client->dev, + "%s: CRC in progress.\n", __func__); + break; + + default: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Unknown error 0x%02X received.\n", + __func__, status.status_code); + break; + } +} + +#define IM_BIT 0 +#define NMS_BIT 1 +#define CID_IM_BIT 2 +#define FS_IM_BIT 3 +#define FREQ_BIT 4 +#define GUARD_BIT 5 + +static void synaptics_rmi4_f51_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct f54_d6_s0_type *f54_d6_0; + struct f54_d10_s0_type *f54_d10_0; + struct f54_d14_s0_type *f54_d14_0; + struct f54_d16_s0_type *f54_d16_0; + struct f54_d17_s0_type *f54_d17_0; + struct f51_d0_s0_type *f51_d0_0; + unsigned char presence_mask = 0; + //ktime_t log = ktime_get(); + int ii, error; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) + return; + + reg = find_packet_reg(regs, 0); + if (!reg) + return; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, reg->r_number); + if (error < 0) { + pr_err("F51 read failed\n"); + return; + } + + subpkt = find_subpkt(regs, 0, 0, ®, (void **)&f51_d0_0); + if (!subpkt || !subpkt->present) + return; + + presence_mask |= (1 << GUARD_BIT); + pr_debug("F%x@D%d: int status [0]=0x%x, [1]=0x%x\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0->data[0], f51_d0_0->data[1]); +#if 0 + tk_debug("F%x@D%d: int status [%s][%s][%s] md = %d\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0->noise_state ? "N" : "-", + f51_d0_0->gear_change ? "H" : "-", + f51_d0_0->guard_state ? "M" : "-", + f51_d0_0->md_present); +#endif + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + return; + + /* not all features might be enabled, thus determine presence */ + for (ii = 0; ii < regs->nr_regs; ii++) + presence_mask |= (1 << ii); + + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return; + + find_subpkt(regs, 6, 0, NULL, (void **)&f54_d6_0); + find_subpkt(regs, 10, 0, NULL, (void **)&f54_d10_0); + find_subpkt(regs, 14, 0, NULL, (void **)&f54_d14_0); + find_subpkt(regs, 16, 0, NULL, (void **)&f54_d16_0); + find_subpkt(regs, 17, 0, NULL, (void **)&f54_d17_0); +#if 0 + if ((presence_mask & (1 << FREQ_BIT)) && f54_d17_0) { + statistics_log_time(gStat.dur, (int)f54_d17_0->freq, log); + statistics_log_time(gStat.hop, (int)f54_d17_0->freq, log); + } + + if ((presence_mask & (1 << NMS_BIT)) && f54_d10_0) + statistics_log_time(gStat.nms, (int)f54_d10_0->noise_state, log); + + if ((presence_mask & (1 << GUARD_BIT)) && f51_d0_0) + statistics_log_time(gStat.mpd, (int)f51_d0_0->md_present, log); + + tk_debug("F%x: %d:%d %5d:%5d:%5d\n", + regs->f_number & 0xff, + (presence_mask & (1 << FREQ_BIT)) && f54_d17_0 ? + (int)f54_d17_0->freq : -1, + (presence_mask & (1 << NMS_BIT)) && f54_d10_0 ? + (int)f54_d10_0->noise_state : -1, + (presence_mask & (1 << CID_IM_BIT)) && f54_d14_0 ? + (int)f54_d14_0->cid_im_lsb | + (f54_d14_0->cid_im_msb << 4) : -1, + (presence_mask & (1 << IM_BIT)) && f54_d6_0 ? + (int)f54_d6_0->interference_metric_lsb | + (f54_d6_0->interference_metric_msb << 4) : -1, + (presence_mask & (1 << FS_IM_BIT)) && f54_d16_0 ? + (int)f54_d16_0->freq_scan_im_lsb | + (f54_d16_0->freq_scan_im_msb << 4) : -1); +#endif +} + + /** + * synaptics_rmi4_report_touch() + * + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives and returns the number of + * fingers detected. + */ +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *touch_count) +{ + unsigned char touch_count_2d; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F01: + synaptics_rmi4_f01_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F51: + synaptics_rmi4_f51_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F12: + if (rmi4_data->suspend_is_wakeable) { + synaptics_rmi4_f12_wakeup_gesture(rmi4_data, fhandler); + break; + } + + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; + + default: + break; + } + + return; +} + + /** + * synaptics_rmi4_sensor_report() + * + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor + * and calls synaptics_rmi4_report_touch() with the appropriate + * function handler for each function with valid data inputs. + */ +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char touch_count = 0; + unsigned int intr_status_mask; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fn *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + rmi4_data->touch_data, + rmi4_data->touch_data_size); + if (retval < 0) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + rmi4_data->touch_data[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler, &touch_count); + } + } + } + + batohui(&intr_status_mask, rmi4_data->touch_data, + rmi4_data->num_of_intr_regs); + /* + * Go through external handlers list only when interrupt + * is not handled by currently active internal functions. + */ + if (intr_status_mask & (~rmi4_data->active_fn_intr_mask)) { + mutex_lock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (!list_empty(&rmi4_data->exp_fn_ctrl.fn_list)) { + list_for_each_entry(exp_fhandler, + &rmi4_data->exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->inserted && + (exp_fhandler->func_attn != NULL)) + exp_fhandler->func_attn( + rmi4_data, + rmi4_data->touch_data[0]); + } + } + mutex_unlock(&rmi4_data->exp_fn_ctrl.list_mutex); + } + + return touch_count; +} + + /** + * synaptics_rmi4_irq() + * + * Called by the kernel when an interrupt occurs (when the sensor + * asserts the attention irq). + * + * This function is the ISR thread and handles the acquisition + * and the reporting of finger data when the presence of fingers + * is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ=%d; rmi4_data ptr = %p\n", __func__, irq, rmi4_data); + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + if (rmi4_data->test_irq_delay_ms) { + int i; + for (i = 0; i < rmi4_data->test_irq_delay_ms; ++i) + udelay(1000); + } +#endif + synaptics_rmi4_sensor_report(rmi4_data); + + return IRQ_HANDLED; +} + + /** + * synaptics_rmi4_irq_enable() + * + * Called by synaptics_rmi4_probe() and the power management functions + * in this driver and also exported to other expansion Function modules + * such as rmi_dev. + * + * This function handles the enabling and disabling of the attention + * irq including the setting up of the ISR thread. + */ +int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + if (enable) { + if (rmi4_data->irq_enabled) + return retval; + + /* Clear interrupts first */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, platform_data->irq_flags, + rmi4_data->irq_name, rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create irq thread\n", + __func__); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ=%d: Started irq thread\n", __func__, rmi4_data->irq); + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Stopped irq thread\n", __func__); + } + } + + return retval; +} + + /** + * synaptics_rmi4_f11_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned char query[F11_STD_QUERY_LEN]; + unsigned char control[F11_STD_CTRL_LEN]; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + query, + sizeof(query)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if ((query[1] & MASK_3BIT) <= 4) + fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1; + else if ((query[1] & MASK_3BIT) == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) | + ((control[7] & MASK_4BIT) << 8); + rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) | + ((control[9] & MASK_4BIT) << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + /* Reporting mode */ + if (!(control[0] & MASK_3BIT)) { + pr_warn("Reporting mode: continuous\n"); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: thresholds: x=0x%x, y=0x%x\n", + __func__, control[2], control[3]); + } + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + abs_data_size = query[5] & MASK_2BIT; + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + fhandler->size_of_data_register_block = abs_data_blk_size; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 15, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + 15, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->button_count = f1a->button_query.max_button_count + 1; + f1a->button_bitmask_size = (f1a->button_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->button_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f51_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct { + unsigned char num_of_query_regs; + unsigned char num_of_data_regs; + unsigned char num_of_ctrl_regs; + } f51_query = {0}; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + (unsigned char *)&f51_query, + sizeof(f51_query)); + pr_debug("F%x: # of query=%d, data=%d, ctrl=%d\n", + fd->fn_number, + f51_query.num_of_query_regs, + f51_query.num_of_data_regs, + f51_query.num_of_ctrl_regs); + /* check for error and mandatory registers presense */ + if (retval < 0 || f51_query.num_of_query_regs < 3) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Error querying F51\n", + __func__); + return -EIO; + } + /* check for mandatory registers presense */ + if (f51_query.num_of_data_regs < 2 || + f51_query.num_of_ctrl_regs < 10) { + return -ENODEV; + } + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + /* FIXME: interrupt bit 5 (belongs F54) is used for some reason */ + fhandler->intr_mask = 0x40; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x data not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.data_base; + reg = ®s->regs[0]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@D%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x ctrl not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.ctrl_base; + for (ii = 0; ii < regs->nr_regs; ii++) { + reg = ®s->regs[ii]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@C%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + } + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, "Error reading F51\n"); + + return 0; +} + + /** + * synaptics_rmi4_f12_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 12 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_subpkt *subpkt; + struct f12_d15_s0_type *f12_d15_0; + struct f12_c8_s0_type *f12_c8_0; + struct f12_c23_s1_type *f12_c23_1; + struct f12_c28_s0_type *f12_c28_0; + + regs = rmi4_data->f12_data_registers_ptr; + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.data_base, regs); + if (retval < 0) + return retval; + + subpkt = find_subpkt(regs, 15, 0, NULL, (void **)&f12_d15_0); + if (subpkt && subpkt->present) + pr_debug("F12 has data register 15\n"); + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F12); + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.ctrl_base, regs); + if (retval < 0) + return retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + subpkt = find_subpkt(regs, 23, 1, NULL, (void**)&f12_c23_1); + if (subpkt && subpkt->present) { + fhandler->num_of_data_points = + f12_c23_1->max_num_reported_objects; + rmi4_data->num_of_fingers = f12_c23_1->max_num_reported_objects; + } else + return -ENOENT; + + subpkt = find_subpkt(regs, 8, 0, NULL, (void **)&f12_c8_0); + if (subpkt && subpkt->present) { + rmi4_data->sensor_max_x = + (f12_c8_0->max_x_msb << 8) | f12_c8_0->max_x_lsb; + rmi4_data->sensor_max_y = + (f12_c8_0->max_y_msb << 8) | f12_c8_0->max_y_lsb; + } else + return -ENOENT; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + /* Data size per touch */ + subpkt = find_subpkt(regs, 28, 0, NULL, (void **)&f12_c28_0); + if (subpkt && subpkt->present) { + for (ii = 0; ii < 8; ii++) + if (f12_c28_0->reported_bytes_per_object & (1 << ii)) + fhandler->size_of_data_register_block++; + } else + return -ENOENT; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 255, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_cap_button_map( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + struct synaptics_dsx_platform_data *pdata = &rmi4_data->board; + + if (!pdata->cap_button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: cap_button_map is \ + NULL in board file\n", + __func__); + return -ENODEV; + } else if (!pdata->cap_button_map->map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (pdata->cap_button_map->nbuttons != + f1a->button_count) { + f1a->valid_button_count = min(f1a->button_count, + pdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->button_count; + } + + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + + for (ii = 0; ii < f1a->valid_button_count; ii++) { + f1a->button_map[ii] = + pdata->cap_button_map->map[ii]; + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } + + return; +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned short intr_offset; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + retval = -EINVAL; + goto error_exit; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_cap_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kzalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static unsigned char *simple_init_packet_reg( + struct synaptics_rmi4_packet_reg *reg, short r_offset) +{ + unsigned char *data_ptr = NULL; + if (reg->subpkt[0].data && reg->subpkt[0].size) { + reg->subpkt[0].present = true; + reg->subpkt[0].offset = 0; + memset(reg->subpkt[0].data, 0, reg->subpkt[0].size); + + reg->offset = r_offset; + kfree(reg->data); + data_ptr = kzalloc(reg->size, GFP_KERNEL); + } + return data_ptr; +} + +static void simple_deinit_packet_reg(struct synaptics_rmi4_packet_reg *reg) +{ + reg->expected = false; + reg->offset = -1; + reg->size = 0; + reg->subpkt[0].expected = false; + reg->subpkt[0].offset = -1; +} + +#define F01_RMI_Ctrl0 0 +#define F01_RMI_Ctrl2 2 +#define F01_RMI_Ctrl3 3 +#define F01_RMI_Ctrl5 5 +#define F01_RMI_Ctrl9 9 + +static void synaptics_rmi4_scan_f01_reg_info( + struct synaptics_rmi4_data *rmi4_data) +{ + int r, s, retval; + unsigned short query42_offset = F01_QUERY21_OFFSET; + unsigned short query43_size = 0; + unsigned short current_ctrl_offset = rmi4_data->num_of_intr_regs; + bool has_recalibration = false; + bool can_access_ctrl9 = false; + u8 query43[4]; + struct synaptics_rmi4_f01_query42 query42; + struct synaptics_rmi4_f01_query1 query1; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F01); + if (unlikely(regs == NULL)) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: F01 registers not defined\n", __func__); + return; + } + /* invalidate offsets only and keep statically defined register size */ + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + kfree(regs->regs[r].data); + regs->regs[r].data = NULL; + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = rmi4_data->f01_ctrl_base_addr; + pr_debug("F01_RMI_Ctrl base addr 0x%x\n", regs->base_addr); + + reg = find_packet_reg(regs, F01_RMI_Ctrl0); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl0 not defined\n", + __func__); + return; + } + + if (regs && reg && reg->nr_subpkts) { + reg->data = simple_init_packet_reg(reg, 0); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_QUERY1_OFFSET, + query1.data, + sizeof(query1.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query1\n", __func__); + return; + } + + if (query1.has_sensor_id) { + query42_offset++; /* has F01_RMI_Query22 */ + pr_debug("has F01_RMI_Query22\n"); + } + + if (query1.has_adjustable_doze) { + pr_debug("has F01_RMI_Ctrl2 and F01_RMI_Ctrl3\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl2); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl3); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + if (query1.reserved) { + current_ctrl_offset++; /* has F01_RMI_Ctrl4 */ + query42_offset++; /* has F01_RMI_Query21 */ + query42_offset += 18; /* has F01_RMI_Query23-41 */ + pr_debug("has F01_RMI_Query21,23-41 and F01_RMI_Ctrl4\n"); + } + + if (query1.has_doze_holdoff) { + pr_debug("has F01_RMI_Ctrl5\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl5); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + query42.data[0] = 0; + if (query1.has_query42) { + pr_debug("F01_RMI_Query42 offset %d\n", query42_offset); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset, + query42.data, + sizeof(query42.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query42\n", + __func__); + } else + has_recalibration = query42.has_recalibration; + } + + if (query42.has_ds4_queries) { + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset + 1, + query43, + sizeof(query43)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query43\n", + __func__); + } else { + can_access_ctrl9 = true; + query43_size = query43[0] & 0xf; + pr_debug("F01_RMI_Query43 size: %d\n", query43_size); + } + } + + if (query43_size) { + if (query43[2] & 0x1) { + current_ctrl_offset++; /* has F01_RMI_Ctrl6 */ + pr_debug("has F01_RMI_Ctrl6\n"); + } + if (query43[2] & 0x2) { + current_ctrl_offset++; /* has F01_RMI_Ctrl7 */ + pr_debug("has F01_RMI_Ctrl7\n"); + } + if (query43[2] & 0x4) { + current_ctrl_offset++; /* has F01_RMI_Ctrl8 */ + pr_debug("has F01_RMI_Ctrl8\n"); + } + } + + reg = find_packet_reg(regs, F01_RMI_Ctrl9); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl9 not present\n", __func__); + return; + } + + if (regs && reg) { + if (has_recalibration == false || can_access_ctrl9 == false) { + simple_deinit_packet_reg(reg); + dev_warn(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + return; + } + + current_ctrl_offset++; + pr_debug("F01_RMI_Ctrl9 offset %d\n", current_ctrl_offset); + + reg->data = simple_init_packet_reg(reg, current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } +} + +struct synaptics_rmi4_fn *get_fhandler(struct synaptics_rmi4_data *rmi4_data, + unsigned char fn_number) +{ + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi = + &(rmi4_data->rmi4_mod_info); + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == fn_number) + return fhandler; + } + return NULL; +} + /** + * synaptics_rmi4_query_device() + * + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets + * to the register types of Function $01, sets up the function handlers + * for Function $11 and Function $12, determines the number of interrupt + * sources from the sensor, adds valid Functions with data inputs to the + * Function linked list, parses information from the query registers of + * Function $01, and enables the interrupt sources from the valid Functions + * with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char page_number; + unsigned char intr_count = 0; + unsigned char f01_query[F01_STD_QUERY_LEN] = {0}; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct f34_properties f34_query; + struct f34_properties_v2 f34_query_v2; + struct synaptics_rmi4_f01_device_status status; + int finger_data_addr; + int intr_status_addr; + int touch_data_size; + struct synaptics_rmi4_fn *f12_handler; + + rmi = &(rmi4_data->rmi4_mod_info); + + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x v%d found (page %d)\n", + __func__, rmi_fd.fn_number, rmi_fd.fn_version, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F51: + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f51_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + /* not critical, just continue */ + kfree(fhandler); + fhandler = NULL; + dev_err(&rmi4_data->i2c_client->dev, + "%s: No handler for F%x\n", + __func__, rmi_fd.fn_number); + } + break; + + case SYNAPTICS_RMI4_F34: + if (rmi_fd.fn_version < 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET, + &f34_query.data[0], + sizeof(f34_query)); + if (retval < 0) + return retval; + + if (f34_query.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } else if (rmi_fd.fn_version == 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET_V2, + &f34_query_v2.data[0], + sizeof(f34_query_v2)); + if (retval < 0) + return retval; + + if (f34_query_v2.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } + break; + + case SYNAPTICS_RMI4_F01: + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + pr_debug("query_base_addr 0x%04x\n", + rmi4_data->f01_query_base_addr); + pr_debug("ctrl_base_addr 0x%04x\n", + rmi4_data->f01_ctrl_base_addr); + pr_debug("data_base_addr 0x%04x\n", + rmi4_data->f01_data_base_addr); + pr_debug("cmd_base_addr 0x%04x\n", + rmi4_data->f01_cmd_base_addr); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + /* FlashProg bit might be inaccurate, so */ + /* do not change the flag if it is set */ + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = + status.flash_prog == 1; + break; + + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F11\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F12\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + switch (retval) { + case -ENOMEM: + return retval; + case -ENODEV: + /* removing buttons handler if there */ + /* were no key codes in platform data */ + kfree(fhandler); + fhandler = NULL; + rmi_fd.intr_src_count = 0; + break; + } + break; + } + + /* Accumulate the interrupt count */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + + if (fhandler && rmi_fd.intr_src_count) { + pr_debug("adding handler for F%x\n", + fhandler->fn_number); + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + if (rmi4_data->num_of_intr_regs >= MAX_INTR_REGISTERS) + return -EINVAL; + + f12_handler = get_fhandler(rmi4_data, SYNAPTICS_RMI4_F12); + if (!f12_handler) { + rmi4_data->touch_data_contiguous = 0; + goto skip_f12_single_i2c_setup; + } + + finger_data_addr = f12_handler->full_addr.data_base + + rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX].offset; + intr_status_addr = rmi4_data->f01_data_base_addr + 1; + + /* is touch data contiguous? */ + if (finger_data_addr != + intr_status_addr + rmi4_data->num_of_intr_regs) { + rmi4_data->touch_data_contiguous = 0; + goto skip_f12_single_i2c_setup; + } + + touch_data_size = rmi4_data->num_of_intr_regs + + rmi4_data->num_of_fingers * f12_handler->size_of_data_register_block; + + /* size of contiguous block changed or not allocated? */ + if (touch_data_size != rmi4_data->touch_data_size || + !rmi4_data->touch_data) { + if (rmi4_data->touch_data) + kfree(rmi4_data->touch_data); + + rmi4_data->touch_data_size = touch_data_size; + rmi4_data->touch_data = kzalloc(rmi4_data->touch_data_size, + GFP_KERNEL); + if (rmi4_data->touch_data) + rmi4_data->touch_data_contiguous = 1; + else { + /* fall back to non-contigous handling */ + rmi4_data->touch_data_contiguous = 0; + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for touch_data\n", + __func__); + } + } + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* Test mode can only be enforced in userdebug build */ + /* When test mode enforced, clear contiguous flag to */ + /* do two I2C transactions to retrieve touch data */ + if (!rmi4_data->test_irq_data_contig) { + rmi4_data->touch_data_contiguous = 0; + pr_notice("Forcing non-contiguous data flag\n"); + } +#endif + +skip_f12_single_i2c_setup: + if (!rmi4_data->touch_data_contiguous) { + rmi4_data->touch_data_size = rmi4_data->num_of_intr_regs; + pr_notice("touch interrupt status and finger data not contiguous\n"); + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + memcpy(&rmi->product_info[0], &f01_query[2], + SYNAPTICS_RMI4_PRODUCT_INFO_SIZE); + memcpy(rmi->serial, &f01_query[4], SYNAPTICS_RMI4_SERIAL_SIZE); + memcpy(rmi->product_id_string, &f01_query[11], + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + rmi->product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + + synaptics_dsx_validate_product_string(rmi->product_id_string); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+PACKAGE_ID_OFFSET, + rmi->package_id, + sizeof(rmi->package_id)); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+FW_VERSION_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + if (rmi->manufacturer_id != 1) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Non-Synaptics device found," + "manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + if (!rmi4_data->in_bootloader && !rmi4_data->input_registered) { + retval = input_register_device(rmi4_data->input_dev); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register input device\n", + __func__); + return retval; + } + rmi4_data->input_registered = true; + } + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + + /* interrupt mask for currently active functions */ + batohui(&rmi4_data->active_fn_intr_mask, + rmi4_data->intr_mask, rmi4_data->num_of_intr_regs); + pr_debug("active func intr_mask 0x%x\n", + rmi4_data->active_fn_intr_mask); + + if (rmi4_data->in_bootloader) + pr_info("Product: %s is in bootloader mode\n", + rmi->product_id_string); + else { + unsigned int config_id, firmware_id; + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(config_id)); + pr_info("Product: %s, firmware id: %x, config id: %08x\n", + rmi->product_id_string, firmware_id, config_id); + /* has to be called after determining */ + /* the number of interrupt registers */ + synaptics_rmi4_scan_f01_reg_info(rmi4_data); + } + + return 0; +} + +static void synaptics_rmi4_cleanup(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler, *next_list_entry; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry_safe(fhandler, next_list_entry, + &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + list_del(&fhandler->link); + kfree(fhandler); + } +} + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data) +{ + /* reset some TSB global vars like fingers_on_2d after resume + * of reset touch IC + */ + if (rmi4_data->button_0d_enabled) { + rmi4_data->tsb_buff_clean_flag = 1; + rmi4_data->fingers_on_2d = false; + } + + if (rmi4_data->input_dev) { + /* + * Enforce touch release event report to work-around + * such event missing while touch IC is off. + */ +#ifdef TYPE_B_PROTOCOL + int i; + for (i = 0; i < rmi4_data->num_of_fingers; i++) { + INPUT_REPORT_MT_SLOT(rmi4_data, i); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, false); + } +#else + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + } +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) +{ + int current_state, retval; + bool need_to_query = false; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (current_state == STATE_UNKNOWN) { + synaptics_rmi4_cleanup(rmi4_data); + need_to_query = true; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + if (need_to_query) { + if (!rmi4_data->input_dev) { + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + return retval; + } + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + return retval; + } + + /* kick off detection work after touch ic changes its mode */ + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, 0); + } + + return 0; +} + +/** +* synaptics_rmi4_detection_work() +* +* Called by the kernel at the scheduled time. +* +* This function is armed by synaptics_new_function call. It checks for +* the insertion and removal of other expansion Function modules such as +* rmi_dev and calls their initialization and removal callback functions +* accordingly. +*/ +static void synaptics_rmi4_detection_work(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct synaptics_exp_fn_ctrl *exp_fn_ctrl = + container_of(dw, struct synaptics_exp_fn_ctrl, det_work); + struct synaptics_rmi4_data *rmi4_data = + container_of(exp_fn_ctrl, struct synaptics_rmi4_data, exp_fn_ctrl); + struct synaptics_rmi4_exp_fn *exp_fhandler, *next_list_entry; + struct device *dev = &rmi4_data->i2c_client->dev; + int state, error, probe_status; + unsigned int scheduled_delay = 0; + bool f34_inserted = false; + bool terminate = false; + bool panel_ready = true; + char *pname = NULL; + +#if defined(CONFIG_DRM) + if (rmi4_data->splash_screen_mode) { + /* DRM panel status */ + panel_ready = dsi_display_is_panel_enable(rmi4_data->ctrl_dsi, + &probe_status, &pname); + dev_dbg(dev, "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, pname); + /* check if primary panel is not a dummy one */ + if (pname && strstr(pname, "dummy")) { + dev_info(dev, "%s: dummy panel detected\n", __func__); + rmi4_data->drm_state = DRM_ST_TERM; + } else if (panel_ready) { + if (rmi4_data->drm_state != DRM_ST_TERM) + rmi4_data->drm_state = DRM_ST_READY; + dev_dbg(dev, "%s: panel ready; drm_st=%d\n", __func__, rmi4_data->drm_state); + } + } +#endif + /* pname only gets initialized by DRM */ + /* it implies RMI_DRM_FRAMEWORK handler running */ + if (pname) { + if (ASSERT(DRM_ST_UNDEF)) + goto exit_reschedule; + + if (ASSERT(DRM_ST_TERM)) { + /* hw init failed on previous step, thus + * continue shutting things down */ + terminate = true; + dev_dbg(dev, "%s: set terminate flag\n", __func__); + } + } + + mutex_lock(&exp_fn_ctrl->list_mutex); + if (list_empty(&exp_fn_ctrl->fn_list)) + goto release_mutex; + + /* F34 resets touch IC and might interfere with */ + /* reporting touch events. Need to ensure it was */ + /* successfully inserted before enabling interrupts */ + list_for_each_entry(exp_fhandler, + &exp_fn_ctrl->fn_list, + link) { + if (exp_fhandler->fn_type == RMI_FW_UPDATER && + exp_fhandler->inserted) { + f34_inserted = true; + dev_dbg(dev, "%s: F34 handler inserted\n", __func__); + break; + } + } + + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_ctrl->fn_list, + link) { + /* terminate all handlers */ + if (terminate) { + dev_dbg(dev, "%s: terminating the rest of handler: %d\n", + __func__, exp_fhandler->fn_type); + /* DRM handler still needs to run for proper cleanup */ + if (exp_fhandler->fn_type != RMI_DRM_FRAMEWORK) + exp_fhandler->func_init = NULL; + } + + if (exp_fhandler->inserted == true) { + if (exp_fhandler->func_init) { + dev_dbg(dev, "%s: handler %d already inserted\n", + __func__, exp_fhandler->fn_type); + continue; + } + + dev_dbg(dev, "%s: calling remove func for handler: %d\n", + __func__, exp_fhandler->fn_type); + exp_fhandler->func_remove(rmi4_data); + + /* need to restore status function on F34 removal */ + if (exp_fhandler->fn_type == RMI_FW_UPDATER && + exp_fhandler->func_status == rmi4_data->get_status) { + rmi4_data->get_status = synaptics_rmi4_f01_flashprog_status; + dev_info(dev, + "%s: using default status retrieval function\n", + __func__); + } + + if (exp_fhandler->fn_type == RMI_DRM_FRAMEWORK) { + if (terminate) { + cancel_delayed_work_sync(&rmi4_data->exp_fn_ctrl.det_work); + flush_workqueue(det_workqueue); + + i2c_set_clientdata(rmi4_data->i2c_client, NULL); + kfree(rmi4_data); + + dev_info(dev, "%s: driver removed\n", __func__); + } else if (f34_inserted) { + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); + dev_info(dev, "%s: drm panel ready\n", __func__); + dev_err(dev, "%s: touch_stopped=%d, flash_enabled=%d\n", + __func__, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->flash_enabled); + } else { + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + dev_dbg(dev, "%s: keep drm fhandler\n", __func__); + continue; + } + } + + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + continue; + } + + /* postpone all handlers, except for RMI_DRM_FRAMEWORK */ + /* until RMI_FW_UPDATER processed */ + if (!f34_inserted && + !(exp_fhandler->fn_type == RMI_FW_UPDATER || + exp_fhandler->fn_type == RMI_DRM_FRAMEWORK)) { + dev_dbg(dev, "%s: hold handler %d until f34 inserted\n", + __func__, exp_fhandler->fn_type); + continue; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(dev, "%s: current state: %s, in_bootloader: %s\n", + __func__, synaptics_dsx_state_name(state), + rmi4_data->in_bootloader ? "true" : "false"); + + if (exp_fhandler->mode == IC_MODE_UI) { + if (rmi4_data->in_bootloader) { + dev_dbg(dev, "%s: handler %d requires UI mode\n", + __func__, exp_fhandler->fn_type); + continue; + } + /* UI mode requires IC powered on */ + if (state == STATE_SUSPEND) { + dev_dbg(dev, "%s: handler %d cannot operate while suspended\n", + __func__, exp_fhandler->fn_type); + scheduled_delay = 500; + continue; + } + /* postpone suspend for loading RMI_F54 and */ + if (exp_fhandler->fn_type == RMI_F54) + synaptics_dsx_sensor_state(rmi4_data, STATE_LOADING); + } + + exp_fhandler->func_init(rmi4_data); + exp_fhandler->inserted = true; + dev_dbg(dev, "%s: handler %d inserted\n", + __func__, exp_fhandler->fn_type); + + if (exp_fhandler->fn_type == RMI_F54 && rmi4_data->f54_data) { + int scan_failures = 0; + struct synaptics_rmi4_func_packet_regs *regs; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_ctrl_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Ctrl scan failed\n", __func__); + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | COMMAND_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_cmd_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Cmd scan failed\n", __func__); + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_data_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Data scan failed\n", __func__); + scan_failures++; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | QUERY_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_query_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Query scan failed\n", __func__); + scan_failures++; + } + } + + if (exp_fhandler->fn_type == RMI_FW_UPDATER && exp_fhandler->func_status) { + int status; + + status = exp_fhandler->func_status(rmi4_data); + /* consider changing to */ + /* if (status == 1) */ + /* if Synaptics confirm only 1 is valid */ + if (status) { + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + rmi4_data->in_bootloader = true; + synaptics_dsx_sensor_state(rmi4_data, STATE_BL); + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(dev, "%s: current state %s\n", + __func__, synaptics_dsx_state_name(state)); + dev_info(dev, "%s: Product: %s is in bootloader mode\n", + __func__, rmi->product_id_string); + } + /* replace default status retrieval function */ + rmi4_data->get_status = exp_fhandler->func_status; + dev_info(dev, "%s: using F34 status retrieval function\n", __func__); + /* allowing sufficient time for possible upgrade to start */ + scheduled_delay = 3000; + } + + if (exp_fhandler->fn_type == RMI_DRM_FRAMEWORK) { + /* allowing sufficient time for F34 to complete */ + scheduled_delay = 3000; + error = synaptics_rmi4_hw_init(rmi4_data); + exp_fhandler->func_init = NULL; + dev_dbg(dev, "%s: removing DRM\n", __func__); + if (error) { + dev_err(dev, "%s: hw init failed!!!\n", __func__); + /* not dummy panel found, but touch hw init failed, */ + /* thus we need to terminate further processing */ + rmi4_data->drm_state = DRM_ST_TERM; + scheduled_delay = 20; /* no reason to wait anymore */ + break; + } + } + +restore_state: + if (STATE_LOADING == synaptics_dsx_get_state_safe(rmi4_data)) + synaptics_dsx_sensor_state(rmi4_data, state); + } + + if (scheduled_delay) { + queue_delayed_work(det_workqueue, &exp_fn_ctrl->det_work, + msecs_to_jiffies(scheduled_delay)); + dev_dbg(dev, "%s: re-scheduled %d\n", __func__, scheduled_delay); + } + +release_mutex: + mutex_unlock(&exp_fn_ctrl->list_mutex); + + return; + +exit_reschedule: + dev_dbg(dev, "%s: re-scheduling...\n", __func__); + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); +} + +static int synaptics_exp_fn_ctrl_init( + struct synaptics_rmi4_data *rmi4_data) +{ + struct device *dev = &rmi4_data->i2c_client->dev; + + mutex_init(&rmi4_data->exp_fn_ctrl.ctrl_mutex); + mutex_init(&rmi4_data->exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&rmi4_data->exp_fn_ctrl.fn_list); + if (!det_workqueue) { + det_workqueue = create_singlethread_workqueue("rmi_det_workqueue"); + if (!det_workqueue) { + dev_err(dev, "%s: unable to create a workqueue\n", __func__); + return -ENODEV; + } + } + + INIT_DELAYED_WORK(&rmi4_data->exp_fn_ctrl.det_work, synaptics_rmi4_detection_work); + rmi4_data->exp_fn_ctrl.inited = true; + + return 0; +} + +/** +* synaptics_rmi4_new_function() +* +* Called by other expansion Function modules in their module init and +* module exit functions. +* +* This function is used by other expansion Function modules such as +* rmi_dev to register themselves with the driver by providing their +* initialization and removal callback function pointers so that they +* can be inserted or removed dynamically at module init and exit times, +* respectively. +*/ +void synaptics_rmi4_new_function( + struct synaptics_rmi4_data *rmi4_data, + enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), + int (*func_status)(struct synaptics_rmi4_data *rmi4_data), + enum ic_modes mode) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler; + + mutex_lock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("failed to alloc mem for expansion function\n"); + goto exit; + } + exp_fhandler->fn_type = fn_type; + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_status = func_status; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + exp_fhandler->mode = mode; + list_add_tail(&exp_fhandler->link, &rmi4_data->exp_fn_ctrl.fn_list); + } else { + list_for_each_entry(exp_fhandler, &rmi4_data->exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->func_init != func_init) + continue; + /* insert flag needed to run remove function in detection */ + /* work even though function was never properly inserted */ + if (!exp_fhandler->inserted) { + exp_fhandler->inserted = true; + dev_info(&rmi4_data->i2c_client->dev, + "removing not inserted handler %d\n", + exp_fhandler->fn_type); + } + exp_fhandler->func_init = NULL; + goto exit; + } + } + +exit: + mutex_unlock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, 0); + return; +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + +static void synaptics_dsx_free_patch(struct synaptics_dsx_patch *patch_set) +{ + struct synaptics_dsx_func_patch *fp, *next_list_entry; + down(&patch_set->list_sema); + list_for_each_entry_safe(fp, next_list_entry, + &patch_set->cfg_head, link) { + kfree(fp->data); + list_del(&fp->link); + } + up(&patch_set->list_sema); +} + +static struct synaptics_dsx_patch *synaptics_dsx_init_patch(const char *name) +{ + struct synaptics_dsx_patch *patch_set; + patch_set = kzalloc(sizeof(struct synaptics_dsx_patch), GFP_KERNEL); + if (patch_set) { + patch_set->name = name; + sema_init(&patch_set->list_sema, 1); + INIT_LIST_HEAD(&patch_set->cfg_head); + } + return patch_set; +} + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +static void synaptics_dsx_queued_resume(struct work_struct *w) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(w, struct synaptics_rmi4_data, resume_work); + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); +} +#endif + +static inline int synaptics_dsx_display_off(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + cancel_work_sync(&rmi4_data->resume_work); + return synaptics_rmi4_suspend(dev); +} + +static inline int synaptics_dsx_display_on(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + pr_debug("queue resume\n"); + queue_work(system_wq, &rmi4_data->resume_work); + return 0; +} + +static int rmi_reboot(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, rmi_reboot); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + struct regulator *reg_ptr; + int state = STATE_INVALID; + + if (likely(rmi4_data)) + state = synaptics_dsx_get_state_safe(rmi4_data); + + if (STATE_INVALID == state) + goto out; + + /* At this point, we're all good with clean-up works */ + synaptics_dsx_set_state_safe(rmi4_data, STATE_INVALID); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_DRM) + msm_drm_unregister_client(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + if (rmi4_data->irq_enabled) { + rmi4_data->irq_enabled = false; + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + } + dev_info(&rmi4_data->i2c_client->dev, "touch shutdown\n"); + + if (!IS_ERR(rmi4_data->vdd_quirk)) { + reg_ptr = rmi4_data->vdd_quirk; + rmi4_data->vdd_quirk = ERR_PTR(-ENODEV); + regulator_force_disable(reg_ptr); + regulator_put(reg_ptr); + } + + if (platform_data->regulator_en && !IS_ERR(rmi4_data->regulator)) { + reg_ptr = rmi4_data->regulator; + rmi4_data->regulator = ERR_PTR(-ENODEV); + pr_debug("touch reboot - disable regulators\n"); + regulator_force_disable(reg_ptr); + regulator_put(reg_ptr); + msleep(1000); + } +out: + return NOTIFY_DONE; +} + +int synaptics_dsx_charger_mode(struct synaptics_rmi4_data *rmi4_data , int is_plugged) +{ + struct config_modifier *cm; + int state; + + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + if (cm->id == SYNA_MOD_CHARGER) { + cm->effective = is_plugged; + break; + } + } + up(&rmi4_data->modifiers.list_sema); + /* in case there is no patch required */ + if (!cm) + return -ENODEV; + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_info(&rmi4_data->i2c_client->dev, + "power supply presence %d in state %d\n", is_plugged, state); + /* reset touch ic on power supply presence change */ + if (state == STATE_ACTIVE) { + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + if (!is_plugged) { + int retval; + /* perform SW reset to restore defaults */ + retval = synaptics_dsx_ic_reset( + rmi4_data, RMI4_SW_RESET); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, + "power supply: sw reset failed %d\n", retval); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* TODO: just apply a single patch on insertion??? */ + } + + return 0; +} + +static int synaptics_rmi4_hw_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval = 0, attr_count; + struct pinctrl *pinctrl; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + pinctrl = devm_pinctrl_get_select(&rmi4_data->i2c_client->dev, + "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + dev_err(&rmi4_data->i2c_client->dev, + "%s: pinctrl failed err %ld\n", __func__, error); + return (int)error; + } + + if (platform_data->gpio_config) + retval = platform_data->gpio_config(platform_data, true); + else if (gpio_is_valid(platform_data->reset_gpio)) { + retval = gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME); + if (!retval) + retval = gpio_direction_output( + platform_data->reset_gpio, 1); + } + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to configure GPIO\n", + __func__); + return retval; + } + + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + goto err_gpio_free; + } + + rmi4_data->regulator = devm_regulator_get( + &rmi4_data->i2c_client->dev, "touch_vdd"); + if (IS_ERR(rmi4_data->regulator)) { + retval = PTR_ERR(rmi4_data->regulator); + if (PTR_ERR(rmi4_data->regulator) == -EPROBE_DEFER) + goto err_gpio_free; + + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Failed to get regulator\n", + __func__); + goto err_gpio_free; + } else { + retval = regulator_enable(rmi4_data->regulator); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Error %d enabling touch-vdd regulator\n", + __func__, retval); + goto err_touch_vdd; + } + platform_data->regulator_en = true; + } + + rmi4_data->vdd_quirk = devm_regulator_get( + &rmi4_data->i2c_client->dev, "vdd_quirk"); + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, "Failed to enable vdd-quirk\n"); + goto err_vdd_quirk; + } + } else { + retval = PTR_ERR(rmi4_data->regulator); + if (retval == -EPROBE_DEFER) + goto err_vdd_quirk; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + dev_dbg(&rmi4_data->i2c_client->dev, "%s: successful reset took %dms\n", + __func__, retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + INIT_WORK(&rmi4_data->resume_work, synaptics_dsx_queued_resume); +#endif + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + + rmi4_data->pm_qos_irq.irq = rmi4_data->irq; + synaptics_dsx_sensor_ready_state(rmi4_data, true); + + rmi4_data->rmi_reboot.notifier_call = rmi_reboot; + rmi4_data->rmi_reboot.next = NULL; + rmi4_data->rmi_reboot.priority = 1; + retval = register_reboot_notifier(&rmi4_data->rmi_reboot); + if (retval) + dev_err(&rmi4_data->i2c_client->dev, "register for reboot failed\n"); + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) { + /* register notifier at the end of probe to */ + /* avoid unnecessary reset in STANDBY state */ + rmi4_data->folio_notif.notifier_call = folio_notifier_callback; + dev_dbg(&rmi4_data->i2c_client->dev, "registering folio notifier\n"); + retval = mmi_hall_register_notifier(&rmi4_data->folio_notif, + MMI_HALL_FOLIO, true); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "Error registering folio_notifier: %d\n", + retval); + /* inability to register folio notifications handler */ + /* is not fatal, thus reset return value to success */ + retval = 0; + } + } +#endif +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + synaptics_dsx_sysfs_touchscreen(rmi4_data, true); +#endif + synaptics_dsx_pm_qos(rmi4_data, PM_ADD, false); + + /* init reporting semaphore and set expected # of fingers to 1 */ + sema_init(&rmi4_data->rctrl.ctrl_sema, 1); + rmi4_data->rctrl.expected = 1; + + return retval; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + device_remove_file(&rmi4_data->i2c_client->dev, &dev_attr_poweron); + +err_query_device: + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + +err_vdd_quirk: + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + +err_touch_vdd: + synaptics_rmi4_cleanup(rmi4_data); + if (rmi4_data->input_registered) + input_unregister_device(rmi4_data->input_dev); + else + input_free_device(rmi4_data->input_dev); + +err_gpio_free: + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + if (gpio_is_valid(platform_data->reset_gpio)) { + gpio_set_value(platform_data->reset_gpio, 0); + gpio_free(platform_data->reset_gpio); + } + + return retval; +} + +static int dummy_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_data *ptr; + ptr = rmi4_data; + return 0; +} + +static void dummy_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_data *ptr = rmi4_data; + ptr = rmi4_data; +} + + /** + * synaptics_rmi4_probe() + * + * Called by the kernel when an association with an I2C device of the + * same name is made (after doing i2c_add_driver). + * + * This funtion allocates and initializes the resources for the driver + * as an input driver, turns on the power to the sensor, queries the + * sensor for its supported Functions and characteristics, registers + * the driver to the input subsystem, sets up the interrupt, and creates + * a work queue for detection of other expansion Function modules. + */ +static int synaptics_rmi4_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int rc = 0; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_dsx_platform_data *platform_data; +#if defined(CONFIG_DRM) + int panel_ready, probe_status; + char *display_name = NULL; +#endif + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data not supported\n", + __func__); + return -EIO; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&client->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + dev_dbg(&client->dev, "%s: rmi4_data ptr = %p\n", __func__, rmi4_data); + + /* assign pointer to client structure right away for further use */ + rmi4_data->i2c_client = client; + + rmi4_data->pm_qos_latency = PM_QOS_DEFAULT_VALUE; + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* TEST OPTIONS */ + rmi4_data->test_irq_delay_ms = 0; + rmi4_data->test_irq_data_contig = 1; /* enforce test mode */ +#endif + if (client->dev.of_node) { + rc = synaptics_dsx_of_init(client, rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: No platform data found\n", + __func__); + kfree(rmi4_data); + return -EINVAL; + } + } else + rmi4_data->board = *(struct synaptics_dsx_platform_data *) + client->dev.platform_data; + platform_data = &rmi4_data->board; + +#if defined(CONFIG_DRM) + /* DRM status of panel associated with control DSI */ + panel_ready = dsi_display_is_panel_enable(rmi4_data->ctrl_dsi, + &probe_status, &display_name); + dev_dbg(&client->dev, + "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, display_name); + + if (rmi4_data->bound_display) { + if (probe_status == -ENODEV || + (display_name && strncmp(display_name, rmi4_data->bound_display, + strlen(rmi4_data->bound_display)))) { + dev_err(&client->dev, + "%s: display binding failed\n", __func__); + kfree(rmi4_data); + return -ENODEV; + } + } else { + /* check for dummy panel */ + if (display_name && strstr(display_name, "dummy")) { + dev_info(&client->dev, + "%s: dummy panel detected; exiting...\n", __func__); + kfree(rmi4_data); + return -ENODEV; + } + } +#endif + + rc = synaptics_dsx_config_regs_alloc(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: cannot allocate resources\n", __func__); + kfree(rmi4_data); + return -ENOMEM; + } + + rmi4_data->touch_ud_stats.ud = devm_kzalloc(&client->dev, + TOUCH_UD_BUFF*sizeof(struct touch_up_down), GFP_KERNEL); + rmi4_data->touch_ud_stats.ud_len = TOUCH_UD_BUFF; + rmi4_data->touch_ud_stats.name = "ts"; + + if (rmi4_data->button_0d_enabled) { + rmi4_data->button_ud_stats.ud = devm_kzalloc(&client->dev, + BUTTON_UD_BUFF*sizeof(struct touch_up_down), GFP_KERNEL); + rmi4_data->button_ud_stats.ud_len = BUTTON_UD_BUFF; + rmi4_data->button_ud_stats.name = "btn"; + } + + rmi4_data->state = STATE_INVALID; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->irq_enabled = false; + atomic_set(&rmi4_data->touch_stopped, 1); + rmi4_data->ic_on = true; + rmi4_data->flash_enabled = false; + rmi4_data->tsb_buff_clean_flag = 1; + rmi4_data->f12_data_registers_ptr = + find_function(rmi4_data, SYNAPTICS_RMI4_F12 | DATA_TYPE); + rmi4_data->i2c_read = synaptics_rmi4_i2c_read; + rmi4_data->i2c_write = synaptics_rmi4_i2c_write; + rmi4_data->set_state = synaptics_dsx_sensor_state; + rmi4_data->ready_state = synaptics_dsx_sensor_ready_state; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->reset_device = synaptics_rmi4_reset_device; + rmi4_data->get_status = synaptics_rmi4_f01_flashprog_status; + + /* get irq number initialized before calling reset */ + rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio); + + i2c_set_clientdata(client, rmi4_data); + + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->state_mutex)); + + rc = synaptics_exp_fn_ctrl_init(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: Cannot alloc exp_fn_ctrl\n", + __func__); + goto free_and_exit; + } + + rc = device_create_file(&client->dev, &dev_attr_poweron); + if (rc < 0) { + dev_err(&client->dev, + "%s: Cannot create poweron\n", + __func__); + goto free_and_exit; + } + + /* add driver's instance to the list */ + rmi4_data->instance = ++drv_instance_counter; + mutex_lock(&instances_mutex); + list_add_tail(&rmi4_data->node, &drv_instances_list); + mutex_unlock(&instances_mutex); + rmi4_data->irq_name = INSTANCE_RMI(DRIVER_NAME, rmi4_data); + rmi4_data->reset_name = INSTANCE_RMI("synaptics_reset", rmi4_data); + + rc = synaptics_mmi_data_init(rmi4_data, true); + if (rc < 0) { + dev_err(&client->dev, "%s: Class init failure\n", __func__); + } + + if (!rmi4_data->splash_screen_mode) { + rc = synaptics_rmi4_hw_init(rmi4_data); + if (rc < 0) { + i2c_set_clientdata(rmi4_data->i2c_client, NULL); + goto free_and_exit; + } + + synaptics_rmi4_resume(&client->dev); + dev_info(&client->dev, "%s: drm panel ready\n", __func__); + dev_err(&client->dev, "%s: touch_stopped=%d, flash_enabled=%d\n", __func__, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->flash_enabled); + return 0; + } + + dev_dbg(&client->dev, + "%s: delay hw init until DRM reports panel is ready\n", + __func__); + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + synaptics_rmi4_new_function(rmi4_data, RMI_DRM_FRAMEWORK, true, + dummy_init, dummy_remove, NULL, NULL, IC_MODE_ANY); + return 0; + +free_and_exit: + kfree(rmi4_data); + return rc; +} + + /** + * synaptics_rmi4_remove() + * + * Called by the kernel when the association with an I2C device of the + * same name is broken (when the driver is unloaded). + * + * This funtion terminates the work queue, stops sensor data acquisition, + * frees the interrupt, unregisters the driver from the input subsystem, + * turns off the power to the sensor, and frees other allocated resources. + */ +static int synaptics_rmi4_remove(struct i2c_client *client) +{ + struct pinctrl *pinctrl; + int attr_count; + struct synaptics_dsx_platform_data *platform_data; + struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); + + if (rmi4_data == NULL) + return 0; + + /* At this point, we're all good with clean-up works */ + synaptics_dsx_set_state_safe(rmi4_data, STATE_STANDBY); + atomic_set(&rmi4_data->touch_stopped, 1); + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) + mmi_hall_unregister_notifier(rmi4_data); +#endif + unregister_reboot_notifier(&rmi4_data->rmi_reboot); + + /* power down touch ic */ + platform_data = &rmi4_data->board; + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); + + pinctrl = devm_pinctrl_get_select_default(&rmi4_data->i2c_client->dev); + if (IS_ERR(pinctrl)) + dev_err(&rmi4_data->i2c_client->dev, + "%s: pinctrl default failed\n", __func__); + + if (rmi4_data->ic_on) { + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + if (rmi4_data->board.regulator_en) + regulator_disable(rmi4_data->regulator); + /* regulators are bound to device, thus no need to put here !!! */ + } + + if (rmi4_data->exp_fn_ctrl.inited) { + cancel_delayed_work_sync(&rmi4_data->exp_fn_ctrl.det_work); + flush_workqueue(det_workqueue); + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + device_remove_file(&rmi4_data->i2c_client->dev, &dev_attr_poweron); +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + synaptics_dsx_sysfs_touchscreen(rmi4_data, false); +#endif + synaptics_rmi4_cleanup(rmi4_data); + + if (rmi4_data->input_registered) + input_unregister_device(rmi4_data->input_dev); + else + input_free_device(rmi4_data->input_dev); + + list_del(&rmi4_data->node); + if (list_empty(&drv_instances_list)) { + destroy_workqueue(det_workqueue); + } + + synaptics_dsx_pm_qos(rmi4_data, PM_REMOVE, false); + + i2c_set_clientdata(client, NULL); + + kfree(rmi4_data); + + return 0; +} + + /** + * synaptics_rmi4_suspend() + * + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep, disables the interrupt, and turns off the power to the sensor. + */ +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct pinctrl *pinctrl; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + static char ud_stats[PAGE_SIZE]; + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 0, 1) == 1) + return 0; + + rmi4_data->flash_enabled = false; + synaptics_dsx_sensor_state(rmi4_data, STATE_SUSPEND); + + /* print UD statistics and send release events thereafter */ + synaptics_dsx_ud_stat(rmi4_data, ud_stats, sizeof(ud_stats)); + dev_info(dev, "%s\n", ud_stats); + synaptics_dsx_release_all(rmi4_data); + + if (rmi4_data->purge_enabled) { + int value = 1; /* set flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + if (rmi4_data->ic_on) { + /* use pinctrl to put touch RESET GPIO into SUSPEND state */ + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); + pinctrl = devm_pinctrl_get_select_default( + &rmi4_data->i2c_client->dev); + if (IS_ERR(pinctrl)) + dev_err(&rmi4_data->i2c_client->dev, + "pinctrl failed err %ld\n", PTR_ERR(pinctrl)); + + if (!rmi4_data->splash_screen_mode) { + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + /* if touch REGULATOR is available - turn it OFF */ + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + } + + rmi4_data->ic_on = false; + } + + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, true); + + return 0; +} + + /** + * synaptics_rmi4_resume() + * + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +static int synaptics_rmi4_resume(struct device *dev) +{ + int retval; + int reset = RMI4_HW_RESET; + struct pinctrl *pinctrl; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 1, 0) == 0) + return 0; + + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, false); + pr_debug("set pm_qos latency %d\n", rmi4_data->pm_qos_latency); + + if (!rmi4_data->ic_on) { + if (!rmi4_data->splash_screen_mode) { + /* if touch REGULATOR is avaialble - turn it ON */ + if (platform_data->regulator_en) { + retval = regulator_enable(rmi4_data->regulator); + if (retval) + pr_err("failed enabling touch-vdd: %d\n", retval); + } + + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) + pr_err("failed enabling vdd-quirk: %d\n", retval); + } + } + + /* if RESET GPIO is in SUSPEND state - no HW reset */ + if (gpio_is_valid(platform_data->reset_gpio)) { + retval = gpio_get_value(platform_data->reset_gpio); + pr_debug("reset gpio state: %d\n", retval); + if (retval == 0) + reset = RMI4_WAIT_READY; + else /* if not in reset, IRQ might be enabled */ + synaptics_rmi4_irq_enable(rmi4_data, false); + } else + synaptics_rmi4_irq_enable(rmi4_data, false); + + pinctrl = devm_pinctrl_get_select(&rmi4_data->i2c_client->dev, + "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + dev_err(&rmi4_data->i2c_client->dev, + "pinctrl failed err %ld\n", error); + } + + if (gpio_is_valid(platform_data->reset_gpio)) { + if (gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME) < 0) + pr_err("failed to request reset gpio\n"); + + gpio_direction_output(platform_data->reset_gpio, 1); + } + rmi4_data->ic_on = true; + } + + /* perform HW reset if needed and wait for touch IC boot completion */ + retval = synaptics_dsx_ic_reset(rmi4_data, reset); + if (retval > 0) + pr_debug("waited for idle %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + /* read touch IC state */ + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* transition to active state is completed - allow flashing */ + rmi4_data->flash_enabled = true; + + if (rmi4_data->purge_enabled) { + int value = 0; /* clear flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + return 0; +} + +#if !defined(CONFIG_PANEL_NOTIFICATIONS) +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +static struct i2c_driver synaptics_rmi4_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#if !defined(CONFIG_PANEL_NOTIFICATIONS) + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, + .id_table = synaptics_rmi4_id_table, +}; + + /** + * synaptics_rmi4_init() + * + * Called by the kernel during do_initcalls (if built-in) + * or when the driver is loaded (if a module). + * + * This function registers the driver to the I2C subsystem. + * + */ +static int __init synaptics_rmi4_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_driver); +} + + /** + * synaptics_rmi4_exit() + * + * Called by the kernel when the driver is unloaded. + * + * This funtion unregisters the driver from the I2C subsystem. + * + */ +static void __exit synaptics_rmi4_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_driver); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Touch Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.h b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.h new file mode 100644 index 000000000000..c6d4ad54bb5a --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.h @@ -0,0 +1,1104 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DSX_DRIVER_VERSION "DSX 1.1" + +/* define to enable USB charger detection */ +#undef USB_CHARGER_DETECTION + +#include +#include +#include +#include +#include +#include +#include +#if defined(USB_CHARGER_DETECTION) +#include +#include +#endif +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +#include +#elif defined(CONFIG_DRM) +#include +#endif + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)) +#define KERNEL_ABOVE_3_7 +#endif + +#define CLASS_PRIMARY_FNAME "primary" +#define DRIVER_NAME "synaptics_mmi" +#define INPUT_PHYS_NAME "synaptics_dsx_i2c/input" +#define TYPE_B_PROTOCOL + +#define RMI4_WAIT_READY 0 +#define RMI4_HW_RESET 1 +#define RMI4_SW_RESET 2 + +#define NO_0D_WHILE_2D +/* #define REPORT_2D_Z */ +#define REPORT_2D_W + +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ +#define POLLING_PERIOD 1 /* ms */ +#define SYN_I2C_RETRY_TIMES 10 +#define MAX_ABS_MT_TOUCH_MAJOR 15 +#define SYN_MAX_BUTTONS 4 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 +#define F01_QUERY1_OFFSET 1 +#define F01_QUERY21_OFFSET 21 +#define F11_STD_QUERY_LEN 9 +#define F11_STD_CTRL_LEN 10 +#define F11_STD_DATA_LEN 12 +#define F12_STD_QUERY_LEN 10 +#define F12_STD_CTRL_LEN 4 +#define F12_STD_DATA_LEN 80 + +#define RESET_GPIO_NAME "touch_reset" +#define IRQ_GPIO_NAME "touch_irq" + +#define PDT_PROPS (0x00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x000A) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define PAGE_SELECT_ENFORCE (0x8000) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1a) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F35 (0x35) +#define SYNAPTICS_RMI4_F38 (0x38) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) +#define SYNAPTICS_RMI4_FDB (0xdb) + +#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2 +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10 +#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3 +#define SYNAPTICS_RMI4_SERIAL_SIZE 7 +#define SYNAPTICS_RMI4_CONFIG_ID_SIZE 4 +#define SYNAPTICS_RMI4_PACKAGE_ID_SIZE 4 +#define SYNAPTICS_RMI4_FILENAME_SIZE 80 + +#define PACKAGE_ID_OFFSET 17 +#define FW_VERSION_OFFSET 18 +#define F34_PROPERTIES_OFFSET 1 +#define F34_PROPERTIES_OFFSET_V2 0 + +#define MAX_NUMBER_OF_FINGERS 10 +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +#define PRODUCT_INFO_SIZE 2 +#define PRODUCT_ID_SIZE 10 + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for data registers + * @ctrl_base: 16-bit base address for command registers + * @data_base: 16-bit base address for control registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_fn - function handler data structure + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @size_of_data_register_block: data register block size + * @data1_offset: offset to data1 register from data base address + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char size_of_data_register_block; + unsigned char data1_offset; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: rmi protocol major version number + * @version_minor: rmi protocol minor version number + * @manufacturer_id: manufacturer id + * @product_props: product properties information + * @product_info: product info array + * @date_code: device manufacture date + * @tester_id: tester id array + * @serial_number: device serial number + * @product_id_string: device product id + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + unsigned char serial[SYNAPTICS_RMI4_SERIAL_SIZE]; + unsigned char package_id[SYNAPTICS_RMI4_PACKAGE_ID_SIZE]; + unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE]; + unsigned char config_id[SYNAPTICS_RMI4_CONFIG_ID_SIZE]; + struct list_head support_fn_list; +}; + +#define TOUCH_UD_BUFF 20 +#define BUTTON_UD_BUFF 10 + +struct touch_up_down { + int mismatch; + unsigned char up_down; + unsigned int counter; +}; + +struct touch_area_stats { + struct touch_up_down *ud; + ssize_t ud_len; + ssize_t ud_id; + ssize_t unknown_counter; + const char *name; +}; + +struct synaptics_dsx_func_patch { + unsigned short func; + unsigned char regstr; + unsigned char subpkt; + unsigned char size; + unsigned char bitmask; + unsigned char *data; + struct list_head link; +}; + +struct synaptics_clip_area { + unsigned xul_clip, yul_clip, xbr_clip, ybr_clip; + unsigned inversion; /* clip inside (when 1) or ouside otherwise */ +}; + +enum { + SYNA_MOD_AOD, + SYNA_MOD_STATS, + SYNA_MOD_FOLIO, + SYNA_MOD_CHARGER, + SYNA_MOD_WAKEUP, + SYNA_MOD_FPS, + SYNA_MOD_QUERY, /* run time query; active only */ + SYNA_MOD_RT, /* run time patch; active only; always effective */ + SYNA_MOD_MAX +}; + +#define FLAG_FORCE_UPDATE 1 +#define FLAG_WAKEABLE 2 +#define FLAG_POWER_SLEEP 4 + +struct synaptics_dsx_patch { + const char *name; + int cfg_num; + unsigned int flags; + struct semaphore list_sema; + struct list_head cfg_head; +}; + +struct config_modifier { + const char *name; + int id; + bool effective; + struct synaptics_dsx_patch *active; + struct synaptics_dsx_patch *suspended; + struct synaptics_clip_area *clipa; + struct list_head link; +}; + +struct synaptics_dsx_modifiers { + int mods_num; + struct semaphore list_sema; + struct list_head mod_head; + +}; + +struct f34_properties { + union { + struct { + unsigned char regmap:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_perm_config:1; + unsigned char has_bl_config:1; + unsigned char has_display_config:1; + unsigned char has_blob_config:1; + unsigned char reserved:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_properties_v2 { + union { + struct { + unsigned char query0_subpkt1_size:3; + unsigned char has_config_id:1; + unsigned char reserved:1; + unsigned char has_thqa:1; + unsigned char reserved2:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_control_95n { + union { + struct { + /* byte 0 - flags*/ + unsigned char c95_filter_bw:3; + unsigned char c95_byte0_b3_b6:4; + unsigned char c95_disable:1; + + /* bytes 1 - 10 */ + unsigned char c95_first_burst_length_lsb; + unsigned char c95_first_burst_length_msb; + unsigned char c95_addl_burst_length_lsb; + unsigned char c95_addl_burst_length_msb; + unsigned char c95_i_stretch; + unsigned char c95_r_stretch; + unsigned char c95_noise_control1; + unsigned char c95_noise_control2; + unsigned char c95_noise_control3; + unsigned char c95_noise_control4; + } __packed; + struct { + unsigned char data[11]; + } __packed; + }; +}; + +enum { + STATE_UNKNOWN, + STATE_ACTIVE, + STATE_SUSPEND, + STATE_STANDBY = 4, + STATE_BL, + STATE_INIT, + STATE_FLASH, + STATE_QUERY, + STATE_LOADING, + STATE_INVALID +}; + +#define STATE_UI (STATE_ACTIVE | STATE_SUSPEND) + +enum ic_modes { + IC_MODE_ANY = 0, + IC_MODE_BL, + IC_MODE_UI +}; + +enum exp_fn { + RMI_DEV = 0, + RMI_F34, + RMI_F54, + RMI_FW_UPDATER, + RMI_CTRL_ACCESS_BLK, + RMI_DRM_FRAMEWORK, + RMI_LAST, +}; + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +static inline void batohui(unsigned int *dest, unsigned char *src, size_t size) +{ + int si = size; + *dest = 0; + for (; --si >= 0;) + *dest += src[si] << (si << 3); +} + +struct synaptics_rmi4_subpkt { + bool present; + bool expected; + short offset; + unsigned int size; + void *data; +}; + +struct synaptics_rmi4_packet_reg { + unsigned short r_number; + bool updated; /* indicate that value in *data has been updated */ + bool modified; /* indicate that value in *data has been modified */ + bool expected; + short offset; + unsigned int size; + unsigned char *data; + unsigned char nr_subpkts; + struct synaptics_rmi4_subpkt *subpkt; +}; + +#define MAX_CONFIG_REGS 10 + +struct synaptics_rmi4_func_packet_regs { + unsigned short f_number; + unsigned short base_addr; + unsigned short query_offset; + int nr_regs; + struct synaptics_rmi4_packet_reg *regs; +}; + +struct reporting_ctrl { + bool reporting_stopped; + unsigned int events_cnt; + int expected, max_seen; + struct semaphore ctrl_sema; +}; + +#define MAX_READ_WRITE_SIZE 8096 +#define MIN_READ_WRITE_BUF_SIZE 256 + +struct temp_buffer { + unsigned char *buf; + unsigned short buf_size; +}; + +struct synaptics_exp_fn_ctrl { + bool inited; + struct mutex ctrl_mutex; + struct mutex list_mutex; + struct list_head fn_list; + struct delayed_work det_work; +}; + +/* + * struct synaptics_rmi4_data - rmi4 device instance data + * @i2c_client: pointer to associated i2c client + * @input_dev: pointer to associated input device + * @board: constant pointer to platform data + * @rmi4_mod_info: device information + * @regulator: pointer to associated regulator + * @vdd_quirk: pointer to associated regulator for 'quirk' config + * @rmi4_io_ctrl_mutex: mutex for i2c i/o control + * @det_work: work thread instance for expansion function detection + * @det_workqueue: pointer to work queue for work thread instance + * @early_suspend: instance to support early suspend power management + * @current_page: current page in sensor to acess + * @button_0d_enabled: flag for 0d button support + * @full_pm_cycle: flag for full power management cycle in early suspend stage + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f01 + * @f01_cmd_base_addr: command base address for f01 + * @f01_ctrl_base_addr: control base address for f01 + * @f01_data_base_addr: data base address for f01 + * @irq: attention interrupt + * @sensor_max_x: sensor maximum x value + * @sensor_max_y: sensor maximum y value + * @irq_enabled: flag for indicating interrupt enable status + * @touch_stopped: touch is in suspend state + * @flash_enabled: allow flashing once transition to active state is complete + * @ic_on: touch ic power state + * @fingers_on_2d: flag to indicate presence of fingers in 2d area + * @number_irq: total number of remembered interrupt times + * @last_irq: last interrup time's number (index of the location of interrupt) + * @irq_info: information about last few interrupt times + * @i2c_read: pointer to i2c read function + * @i2c_write: pointer to i2c write function + * @irq_enable: pointer to irq enable function + */ +struct synaptics_rmi4_data { + struct i2c_client *i2c_client; + struct device *ts_class_dev; + struct input_dev *input_dev; + struct synaptics_dsx_platform_data board; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct regulator *regulator; + struct regulator *vdd_quirk; + struct mutex rmi4_io_ctrl_mutex; + struct mutex state_mutex; +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + struct mmi_notifier panel_nb; +#elif defined(CONFIG_FB) + struct notifier_block panel_nb; +#endif + atomic_t panel_off_flag; + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char num_of_rx; + unsigned char num_of_tx; + unsigned char num_of_fingers; + unsigned char aod_mt; + unsigned char f01_ctrl_register_0; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; + unsigned int active_fn_intr_mask; + int state; + int irq; + int ctrl_dsi; + const char *class_entry_name; + const char *bound_display; + int sensor_max_x; + int sensor_max_y; + bool irq_enabled; + atomic_t touch_stopped; + bool splash_screen_mode; + bool flash_enabled; + bool ic_on; + bool fingers_on_2d; + bool input_registered; + bool in_bootloader; + int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + void (*set_state)(struct synaptics_rmi4_data *rmi4_data, int state); + int (*ready_state)(struct synaptics_rmi4_data *rmi4_data, bool standby); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data); + int (*get_status)(struct synaptics_rmi4_data *rmi4_data); + int number_irq; + int last_irq; + struct synaptics_rmi4_irq_info *irq_info; + + /* features enablers */ + bool charger_detection_enabled; + bool folio_detection_enabled; + bool fps_detection_enabled; + bool wakeup_detection_enabled; + bool patching_enabled; + bool purge_enabled; + + bool suspend_is_wakeable; + bool clipping_on; + struct synaptics_clip_area *clipa; + struct synaptics_dsx_modifiers modifiers; + + struct work_struct resume_work; + struct synaptics_rmi4_func_packet_regs *f12_data_registers_ptr; + struct notifier_block rmi_reboot; +#if defined(USB_CHARGER_DETECTION) + struct power_supply psy; +#endif +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + struct notifier_block folio_notif; +#endif + bool is_fps_registered; /* FPS notif registration might be delayed */ + struct notifier_block fps_notif; + + struct mutex rmi4_exp_init_mutex; + uint32_t pm_qos_latency; + struct pm_qos_request pm_qos_irq; + + struct reporting_ctrl rctrl; + unsigned char tsb_buff_clean_flag; + + bool touch_data_contiguous; + uint8_t *touch_data; + uint16_t touch_data_size; + + struct temp_buffer write_buf; + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* TEST OPTIONS */ + int test_irq_delay_ms; + int test_irq_data_contig; +#endif + + struct synaptics_exp_fn_ctrl exp_fn_ctrl; + struct list_head node; + struct semaphore reset_semaphore; + int drm_state; + int instance; + char *irq_name; + char *reset_name; + + void *fwu_data; + void *rmidev_data; + void *f54_data; + + int (*scan_f54_ctrl_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_cmd_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_query_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_data_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + + struct touch_area_stats touch_ud_stats; + struct touch_area_stats button_ud_stats; + + struct synaptics_rmi4_func_packet_regs config_regs[MAX_CONFIG_REGS]; +}; + +enum { + DRM_ST_UNDEF = 0, + DRM_ST_READY, + DRM_ST_TERM, + DRM_ST_MAX +}; + +#define ASSERT(s) (rmi4_data->drm_state == s) + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct partition_table { + unsigned char partition_id:5; + unsigned char byte_0_reserved:3; + unsigned char byte_1_reserved; + unsigned char partition_length_7_0; + unsigned char partition_length_15_8; + unsigned char start_physical_address_7_0; + unsigned char start_physical_address_15_8; + unsigned char partition_properties_7_0; + unsigned char partition_properties_15_8; +} __packed; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_0 { + union { + struct { + unsigned char subpacket_1_size:3; + unsigned char has_config_id:1; + unsigned char f34_query0_b4:1; + unsigned char has_thqa:1; + unsigned char f34_query0_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_1_7 { + union { + struct { + /* query 1 */ + unsigned char bl_minor_revision; + unsigned char bl_major_revision; + + /* query 2 */ + unsigned char bl_fw_id_7_0; + unsigned char bl_fw_id_15_8; + unsigned char bl_fw_id_23_16; + unsigned char bl_fw_id_31_24; + + /* query 3 */ + unsigned char minimum_write_size; + unsigned char block_size_7_0; + unsigned char block_size_15_8; + unsigned char flash_page_size_7_0; + unsigned char flash_page_size_15_8; + + /* query 4 */ + unsigned char adjustable_partition_area_size_7_0; + unsigned char adjustable_partition_area_size_15_8; + + /* query 5 */ + unsigned char flash_config_length_7_0; + unsigned char flash_config_length_15_8; + + /* query 6 */ + unsigned char payload_length_7_0; + unsigned char payload_length_15_8; + + /* query 7 */ + unsigned char f34_query7_b0:1; + unsigned char has_bootloader:1; + unsigned char has_device_config:1; + unsigned char has_flash_config:1; + unsigned char has_manufacturing_block:1; + unsigned char has_guest_serialization:1; + unsigned char has_global_parameters:1; + unsigned char has_core_code:1; + unsigned char has_core_config:1; + unsigned char has_guest_code:1; + unsigned char has_display_config:1; + unsigned char f34_query7_b11__15:5; + unsigned char f34_query7_b16__23; + unsigned char f34_query7_b24__31; + } __packed; + unsigned char data[21]; + }; +}; + +struct f34_v7_data_1_5 { + union { + struct { + unsigned char partition_id:5; + unsigned char f34_data1_b5__7:3; + unsigned char block_offset_7_0; + unsigned char block_offset_15_8; + unsigned char transfer_length_7_0; + unsigned char transfer_length_15_8; + unsigned char command; + unsigned char payload_0; + unsigned char payload_1; + } __packed; + unsigned char data[8]; + }; +}; + +struct f34_v5v6_flash_properties { + union { + struct { + unsigned char reg_map:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_pm_config:1; + unsigned char has_bl_config:1; + unsigned char has_disp_config:1; + unsigned char has_ctrl1:1; + unsigned char has_query4:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v5v6_flash_properties_2 { + union { + struct { + unsigned char has_guest_code:1; + unsigned char reserved:7; + } __packed; + unsigned char data[1]; + }; +}; + +struct register_offset { + unsigned char properties; + unsigned char properties_2; + unsigned char block_size; + unsigned char block_count; + unsigned char gc_block_count; + unsigned char flash_status; + unsigned char partition_id; + unsigned char block_number; + unsigned char transfer_length; + unsigned char flash_cmd; + unsigned char payload; +}; + +struct block_count { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short fl_config; + unsigned short pm_config; + unsigned short bl_config; + unsigned short lockdown; + unsigned short guest_code; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short guest_code; +}; + +struct container_descriptor { + unsigned char content_checksum[4]; + unsigned char container_id[2]; + unsigned char minor_version; + unsigned char major_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char container_option_flags[4]; + unsigned char content_options_length[4]; + unsigned char content_options_address[4]; + unsigned char content_length[4]; + unsigned char content_address[4]; +}; + +struct image_header_10 { + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char minor_header_version; + unsigned char major_header_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char top_level_container_start_addr[4]; +}; + +struct image_header_05_06 { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_bootloader:1; + unsigned char options_guest_code:1; + unsigned char options_tddi:1; + unsigned char options_reserved:4; + unsigned char header_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char bootloader_addr[4]; + unsigned char bootloader_size[4]; + unsigned char ui_addr[4]; + unsigned char ui_size[4]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + union { + struct { + unsigned char cstmr_product_id[PRODUCT_ID_SIZE]; + unsigned char reserved_4a_4f[6]; + }; + struct { + unsigned char dsp_cfg_addr[4]; + unsigned char dsp_cfg_size[4]; + unsigned char reserved_48_4f[8]; + }; + }; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_disp_config; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int disp_config_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + struct block_data bootloader; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data fl_config; + struct block_data bl_config; + struct block_data guest_code; + struct block_data lockdown; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, + BL_V8 = 8, +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool force_update; + bool do_lockdown; + bool has_guest_code; + bool new_partition_table; + bool has_erase_all; + unsigned int data_pos; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char config_id[32]; + unsigned char flash_status; + unsigned char partitions; + unsigned short block_size; + unsigned short config_size; + unsigned short config_area; + unsigned short config_block_count; + unsigned short flash_config_length; + unsigned short payload_length; + unsigned short partition_table_bytes; + unsigned short read_config_buf_size; + const unsigned char *config_data; + const unsigned char *image; + unsigned char *image_name; + unsigned int image_size; + struct image_metadata img; + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct f34_v5v6_flash_properties flash_properties; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_fn_desc f35_fd; + struct device *dev; + struct work_struct fwu_work; + bool irq_enabled; + struct semaphore irq_sema; + struct wakeup_source flash_wake_src; + struct completion remove_complete; + + int (*firmware_update)(struct device *dev, const char *fwname); + int (*firmware_erase)(struct device *dev); +}; + +struct image_header { + unsigned int checksum; + unsigned int image_size; + unsigned int config_size; + unsigned char options; + unsigned char bootloader_version; + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; +}; + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + dev_warn(&rmi4_data->i2c_client->dev, + "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + dev_warn(&rmi4_data->i2c_client->dev, + "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +/* Touchscreen MMI class related section */ +extern int drv_instance_counter; +extern struct list_head drv_instances_list; +extern struct mutex instances_mutex; + +int synaptics_mmi_data_init(struct synaptics_rmi4_data *ts, bool enable); +int synaptics_dsx_sensor_ready_state( + struct synaptics_rmi4_data *rmi4_data, bool standby); +int synaptics_dsx_wait_for_idle(struct synaptics_rmi4_data *rmi4_data); +int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, bool on); +void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state); +int synaptics_dsx_ic_reset(struct synaptics_rmi4_data *rmi4_data, int reset); +int synaptics_dsx_charger_mode(struct synaptics_rmi4_data *rmi4_data, int state); + +/* end of class */ + +struct synaptics_rmi4_exp_fn_ptr { + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); +}; + +void synaptics_rmi4_new_function(struct synaptics_rmi4_data *rmi4_data, + enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), + int (*func_status)(struct synaptics_rmi4_data *rmi4_data), + enum ic_modes mode); + +int synaptics_rmi4_scan_packet_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs); + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int idx); + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs); + +int alloc_buffer(struct temp_buffer *tb, size_t count); + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) + return -EINVAL; + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline int synaptics_rmi4_reg_read( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->i2c_read(rmi4_data, addr, data, len); +} + +static inline int synaptics_rmi4_reg_write( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->i2c_write(rmi4_data, addr, data, len); +} + +extern int FPS_register_notifier(struct notifier_block *nb, + unsigned long stype, bool report); +extern int FPS_unregister_notifier(struct notifier_block *nb, + unsigned long stype); + +extern struct synaptics_rmi4_data *synaptics_driver_getdata( + struct synaptics_rmi4_data *prev); + +#endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_instance.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_instance.c new file mode 100644 index 000000000000..f268c75b4696 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_instance.c @@ -0,0 +1,45 @@ +/* + * Synaptics DSX touchscreen driver MMI class extention + * + * Copyright (C) 2019 Motorola Mobility + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include + +#include "synaptics_dsx_i2c.h" + +int drv_instance_counter; +DEFINE_MUTEX(instances_mutex); +LIST_HEAD(drv_instances_list); + +struct synaptics_rmi4_data *synaptics_driver_getdata( + struct synaptics_rmi4_data *prev) +{ + struct synaptics_rmi4_data *next = NULL; + mutex_lock(&instances_mutex); + if (!prev) { + if (!list_empty(&drv_instances_list)) + next = list_first_entry(&drv_instances_list, + struct synaptics_rmi4_data, node); + } else { + if (!list_is_last(&prev->node, &drv_instances_list)) + next = list_next_entry(prev, node); + } + mutex_unlock(&instances_mutex); + pr_debug("instance ptr %p\n", next); + return next; +} +EXPORT_SYMBOL(synaptics_driver_getdata); + + diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_rmi_dev.c new file mode 100644 index 000000000000..13b4a07c12f9 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_rmi_dev.c @@ -0,0 +1,890 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "synaptics_dsx_i2c.h" + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define DEV_NUMBER 1 +#define MAX_INSTANCE 5 +#define REG_ADDR_LIMIT 0xFFFF + +static char *rmi_cdev_node(struct device *dev, umode_t *mode) +{ + if (mode) + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static struct class rmi_device_class = { + .owner = THIS_MODULE, + .name = DEVICE_CLASS_NAME, + .devnode = rmi_cdev_node, +}; + +static int rmidev_major_num; + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +struct rmidev_handle { + dev_t dev_no; + unsigned short address; + unsigned int length; + struct device dev; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct completion remove_complete; + void *data; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; + struct temp_buffer data_buf; +}; + +static const struct bin_attribute attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = rmidev_sysfs_data_show, + .write = rmidev_sysfs_data_store, +}; + +static const struct device_attribute attrs[] = { + __ATTR(open, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_open_store), + __ATTR(release, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_release_store), + __ATTR(address, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_address_store), + __ATTR(length, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_length_store), + __ATTR(attn_state, S_IRUSR | S_IRGRP, + rmidev_sysfs_attn_state_show, + NULL), +}; + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct rmidev_data *dev_data = data_file->private_data; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int retval; + unsigned int data_length = rmidev->length; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (count < data_length) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Not enough space (%zu bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + if (data_length) { + retval = rmidev->fn_ptr->read(rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return data_length; +} + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct rmidev_data *dev_data = data_file->private_data; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int retval; + unsigned int data_length = rmidev->length; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (data_length) { + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmi4_data, false); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmi4_data, true); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->address = (unsigned short)input; + + return count; +} + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->length = input; + + return count; +} + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int attn_state; + const struct synaptics_dsx_platform_data *platform_data = + &rmi4_data->board; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + attn_state = gpio_get_value(platform_data->irq_gpio); + + return snprintf(buf, PAGE_SIZE, "%u\n", attn_state); +} + +/* + * rmidev_llseek - used to set up register address + * + * @filp: file structure for seek + * @off: offset + * if whence == SEEK_SET, + * high 16 bits: page address + * low 16 bits: register address + * if whence == SEEK_CUR, + * offset from current position + * if whence == SEEK_END, + * offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: current position set %lld\n", + __func__, newpos); +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: - use to read data from rmi device + * + * @filp: file structure for read + * @buf: user space buffer pointer + * @count: number of bytes to read + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + struct temp_buffer *tb; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + tb = &dev_data->data_buf; + + if (tb->buf_size < count && alloc_buffer(tb, count) != 0) { + retval = -ENOMEM; + goto clean_up; + } + + retval = rmidev->fn_ptr->read(rmidev->rmi4_data, + *f_pos, + tb->buf, + count); + if (retval < 0) + goto clean_up; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: read %zu bytes at offset %lld\n", + __func__, count, *f_pos); + + if (copy_to_user(buf, tb->buf, count)) + retval = -EFAULT; + else + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_write: - used to write data to rmi device + * + * @filep: file structure for write + * @buf: user space buffer pointer + * @count: number of bytes to write + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + struct temp_buffer *tb; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + + tb = &dev_data->data_buf; + + if (tb->buf_size < count && alloc_buffer(tb, count) != 0) { + retval = -ENOMEM; + goto clean_up; + } + + if (copy_from_user(tb->buf, buf, count)) { + retval = -EFAULT; + goto clean_up; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: write %zu bytes at offset %lld\n", + __func__, count, *f_pos); + + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + *f_pos, + tb->buf, + count); + if (retval >= 0) + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_open: enable access to rmi device + * @inp: inode struture + * @filp: file structure + */ +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + struct rmidev_handle *rmidev; + + if (!dev_data) + return -EACCES; + + rmidev = dev_data->rmi_dev; + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: device data %p\n", + __func__, dev_data); + + filp->private_data = dev_data; + mutex_lock(&(dev_data->file_mutex)); + + rmidev->fn_ptr->enable(rmidev->rmi4_data, false); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_release: - release access to rmi device + * @inp: inode structure + * @filp: file structure + */ +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + struct rmidev_handle *rmidev; + + if (!dev_data) + return -EACCES; + + rmidev = dev_data->rmi_dev; + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + rmidev->fn_ptr->enable(rmidev->rmi4_data, true); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + if (dev_data) { + dev_t devno; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev device removed\n", + __func__); + } + + return; +} + +static void rmidev_registered_release(struct device *dev) +{ + struct rmidev_handle *rmidev = + container_of(dev, struct rmidev_handle, dev); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev release function called\n", + __func__); +} + +static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) +{ + struct rmidev_handle *rmidev = + (struct rmidev_handle *)rmi4_data->rmidev_data; + int retval; + dev_t dev_no; + unsigned char attr_count; + struct rmidev_data *dev_data; + struct device *device_ptr; + + pr_debug("%s: enter rmi4_data %p\n", __func__, rmi4_data); + + rmidev->fn_ptr = kzalloc(sizeof(*(rmidev->fn_ptr)), GFP_KERNEL); + if (!rmidev) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto err_fn_ptr; + } + + rmidev->fn_ptr->read = rmi4_data->i2c_read; + rmidev->fn_ptr->write = rmi4_data->i2c_write; + rmidev->fn_ptr->enable = rmi4_data->irq_enable; + rmidev->rmi4_data = rmi4_data; + + if (!rmidev_major_num) { + retval = alloc_chrdev_region(&dev_no, rmi4_data->instance, + MAX_INSTANCE, CHAR_DEVICE_NAME); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate char device region %d\n", + __func__, retval); + goto err_device_region; + } + + rmidev_major_num = MAJOR(dev_no); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Major number of rmidev = %d\n", + __func__, rmidev_major_num); + } else + dev_no = MKDEV(rmidev_major_num, rmi4_data->instance); + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for dev_data\n", + __func__); + retval = -ENOMEM; + goto err_dev_data; + } + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + dev_data->device_class = &rmi_device_class; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Device data %p\n", __func__, dev_data); + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to add rmi char device\n", + __func__); + goto err_char_device; + } + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create rmi char device\n", + __func__); + retval = -ENODEV; + goto err_char_device; + } + + retval = gpio_export(rmi4_data->board.irq_gpio, false); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(rmi4_data->i2c_client->dev), + "attn", rmi4_data->board.irq_gpio); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s Failed to create gpio symlink\n", + __func__); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Exported attention gpio %d\n", + __func__, rmi4_data->board.irq_gpio); + } + } + + rmidev->dev.class = &rmi_device_class; + rmidev->dev.parent = &rmi4_data->i2c_client->dev; + rmidev->dev.release = rmidev_registered_release; + + dev_set_name(&rmidev->dev, DEVICE_CLASS_NAME "%d", rmi4_data->instance); + dev_set_drvdata(&rmidev->dev, dev_data); + + retval = device_register(&rmidev->dev); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register device\n", + __func__); + goto err_sysfs_dir; + } + + retval = device_create_bin_file(&rmidev->dev, &attr_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto err_sysfs_bin; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = device_create_file(&rmidev->dev, &attrs[attr_count]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + device_remove_file(&rmidev->dev, &attrs[attr_count]); + + device_remove_bin_file(&rmidev->dev, &attr_data); + +err_sysfs_bin: + device_unregister(&rmidev->dev); + +err_sysfs_dir: +err_char_device: + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, MAX_INSTANCE); + +err_device_region: + kfree(rmidev->fn_ptr); + +err_fn_ptr: + return retval; +} + +static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) +{ + struct rmidev_handle *rmidev = + (struct rmidev_handle *)rmi4_data->rmidev_data; + unsigned char attr_count; + struct rmidev_data *dev_data; + + pr_debug("%s: rmi4_data %p\n", __func__, rmi4_data); + + if (!rmidev) + return; + + gpio_unexport(rmi4_data->board.irq_gpio); + + sysfs_remove_link(&rmi4_data->i2c_client->dev.kobj, "attn"); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + device_remove_file(&rmidev->dev, &attrs[attr_count]); + + device_remove_bin_file(&rmidev->dev, &attr_data); + + device_unregister(&rmidev->dev); + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + kfree(rmidev->fn_ptr); + + complete(&rmidev->remove_complete); + + return; +} + +static int __init rmidev_module_init(void) +{ + int err; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *next = NULL; + + err = class_register(&rmi_device_class); + if (err) { + pr_err("%s: couldn't register rmi device class\n", __func__); + return err; + } + + while ((next = synaptics_driver_getdata(next)) != NULL) { + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + dev_err(&next->i2c_client->dev, + "%s: cannot allocate rmidev for rmi4_data %p\n", + __func__, next); + continue; + } + dev_dbg(&next->i2c_client->dev, + "%s: new rmidev %p\n", __func__, rmidev); + next->rmidev_data = (void *)rmidev; + synaptics_rmi4_new_function(next, RMI_DEV, true, + rmidev_init_device, + rmidev_remove_device, + NULL, NULL, IC_MODE_UI); + } + + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + rmidev = (struct rmidev_handle *)next->rmidev_data; + dev_dbg(&next->i2c_client->dev, + "%s: removing rmidev %p\n", __func__, rmidev); + init_completion(&rmidev->remove_complete); + synaptics_rmi4_new_function(next, RMI_DEV, false, + rmidev_init_device, + rmidev_remove_device, + NULL, NULL, IC_MODE_UI); + wait_for_completion(&rmidev->remove_complete); + kfree(rmidev); + next->rmidev_data = NULL; + } + + unregister_chrdev_region(MKDEV(rmidev_major_num, 1), MAX_INSTANCE); + class_unregister(&rmi_device_class); + + return; +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_test_reporting.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_test_reporting.c new file mode 100644 index 000000000000..0527a84e6ff1 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_test_reporting.c @@ -0,0 +1,5979 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_i2c.h" + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#undef WATCHDOG_HRTIMER +#define WATCHDOG_TIMEOUT_S 1 +#define STATUS_WORK_INTERVAL 20 /* ms */ + +/* enable for report status polling */ +#define REPORT_STATUS_POLLING + +/* +#define RAW_HEX +#define HUMAN_READABLE +*/ + +#define STATUS_IDLE 0 +#define STATUS_BUSY 1 +#define STATUS_ERROR 2 + +#define DATA_REPORT_INDEX_OFFSET 1 +#define DATA_REPORT_DATA_OFFSET 3 + +#define COMMAND_GET_REPORT 1 +#define COMMAND_FORCE_CAL 2 +#define COMMAND_FORCE_UPDATE 4 +#define COMMAND_ENTER_IN_CELL_TESTMODE 16 + +#define HIGH_RESISTANCE_DATA_SIZE 6 +#define FULL_RAW_CAP_MIN_MAX_DATA_SIZE 4 +#define TREX_DATA_SIZE 7 + +#define NO_AUTO_CAL_MASK 0x01 + +struct synaptics_rmi4_f54_handle; + +static inline ssize_t synaptics_dsx_test_reporting_show_error( + struct synaptics_rmi4_f54_handle *f54, char *buf); +static inline ssize_t synaptics_dsx_test_reporting_store_error( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count); + +struct f54_attribute { + struct attribute attr; + ssize_t (*show)(struct synaptics_rmi4_f54_handle *f54, char *buf); + ssize_t (*store)(struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count); +}; + +#define concat(a, b) a##b + +#define GROUP(_attrs) {\ + .attrs = _attrs,\ +} + +#define attrify(propname) (&f54_attr_##propname.attr) + +#define show_prototype_ext(propname, perm)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, (perm),\ + concat(synaptics_rmi4_f54, _##propname##_show),\ + synaptics_dsx_test_reporting_store_error); + +#define show_prototype(propname)\ + show_prototype_ext(propname, S_IRUSR | S_IRGRP | S_IROTH) + +#define store_prototype(propname)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, S_IWUSR | S_IWGRP,\ + synaptics_dsx_test_reporting_show_error,\ + concat(synaptics_rmi4_f54, _##propname##_store)); + +#define show_store_prototype_ext(propname, perm)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf);\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, (perm),\ + concat(synaptics_rmi4_f54, _##propname##_show),\ + concat(synaptics_rmi4_f54, _##propname##_store)); + +#define show_store_prototype(propname)\ + show_store_prototype_ext(propname, \ + S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP) + +#define simple_show_func(rtype, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: stored value " #rtype "." #propname "\n", __func__);\ + return snprintf(buf, PAGE_SIZE, fmt, f54->rtype.propname);\ +} \ + +#define simple_show_func_unsigned(rtype, propname)\ +simple_show_func(rtype, propname, "%u\n") + +#define show_func(rtype, rgrp, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + return snprintf(buf, PAGE_SIZE, fmt,\ + f54->rtype.rgrp->propname);\ +} \ + +#define show_func_unsigned(rtype, rgrp, propname)\ +show_func(rtype, rgrp, propname, "%u\n") + +#define show_store_func(rtype, rgrp, propname, fmt)\ +show_func(rtype, rgrp, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned long setting;\ + unsigned long o_setting;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ +\ + retval = sstrtoul(buf, 10, &setting);\ + if (retval)\ + return retval;\ +\ + mutex_lock(&f54->rtype##_mutex);\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + if (retval < 0) {\ + mutex_unlock(&f54->rtype##_mutex);\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + if (f54->rtype.rgrp->propname == setting) {\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ + } \ +\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: write value %ld " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, setting, f54->rtype.rgrp->address);\ +\ + o_setting = f54->rtype.rgrp->propname;\ + f54->rtype.rgrp->propname = setting;\ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp "\n",\ + __func__);\ + f54->rtype.rgrp->propname = o_setting;\ + mutex_unlock(&f54->rtype##_mutex);\ + return retval;\ + } \ +\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ +} \ + +#define show_store_func_unsigned(rtype, rgrp, propname)\ +show_store_func(rtype, rgrp, propname, "%u\n") + +#define show_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname "." #subpkt " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + return retval;\ + } \ +\ + return snprintf(buf, PAGE_SIZE, fmt,\ + f54->rtype.rgrp->sp##subpkt->propname);\ +} \ + +#define show_subpkt_func_unsigned(rtype, rgrp, subpkt, propname)\ +show_subpkt_func(rtype, rgrp, subpkt, propname, "%u\n") + +#define show_store_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +show_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned long setting;\ + unsigned long o_setting;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read then write value " #rtype "." #rgrp "." #propname "." #subpkt " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + retval = sstrtoul(buf, 10, &setting);\ + if (retval)\ + return retval;\ +\ + mutex_lock(&f54->rtype##_mutex);\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + if (retval < 0) {\ + mutex_unlock(&f54->rtype##_mutex);\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + return retval;\ + } \ +\ + if (f54->rtype.rgrp->sp##subpkt->propname == setting) {\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ + } \ +\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: write value %ld " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, setting, f54->rtype.rgrp->address);\ +\ + o_setting = f54->rtype.rgrp->sp##subpkt->propname;\ + f54->rtype.rgrp->sp##subpkt->propname = setting;\ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + f54->rtype.rgrp->sp##subpkt->propname = o_setting;\ + mutex_unlock(&f54->rtype##_mutex);\ + return retval;\ + } \ +\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ +} \ + +#define show_store_subpkt_func_unsigned(rtype, rgrp, subpkt, propname)\ +show_store_subpkt_func(rtype, rgrp, subpkt, propname, "%u\n") + +#define show_replicated_func(rtype, rgrp, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + int size = 0;\ + unsigned char ii;\ + unsigned char length;\ + unsigned char element_count;\ + unsigned char *temp;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + length = f54->rtype.rgrp->length;\ + element_count = length / sizeof(*f54->rtype.rgrp->data);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + } \ +\ + temp = buf;\ +\ + for (ii = 0; ii < element_count; ii++) {\ + retval = snprintf(temp, PAGE_SIZE - size, fmt " ",\ + f54->rtype.rgrp->data[ii].propname);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write output\n",\ + __func__);\ + return retval;\ + } \ + size += retval;\ + temp += retval;\ + } \ +\ + retval = snprintf(temp, PAGE_SIZE - size, "\n");\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Faild to write null terminator\n",\ + __func__);\ + return retval;\ + } \ +\ + return size + retval;\ +} \ + +#define show_replicated_func_unsigned(rtype, rgrp, propname)\ +show_replicated_func(rtype, rgrp, propname, "%u") + +#define show_store_replicated_func(rtype, rgrp, propname, fmt)\ +show_replicated_func(rtype, rgrp, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned int setting;\ + unsigned char ii;\ + unsigned char length;\ + unsigned char element_count;\ + const unsigned char *temp;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read then write value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + length = f54->rtype.rgrp->length;\ + element_count = length / sizeof(*f54->rtype.rgrp->data);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + if (retval < 0) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + } \ +\ + temp = buf;\ +\ + for (ii = 0; ii < element_count; ii++) {\ + if (sscanf(temp, fmt, &setting) == 1) {\ + f54->rtype.rgrp->data[ii].propname = setting;\ + } else {\ + mutex_unlock(&f54->rtype##_mutex);\ + return -EINVAL;\ + } \ +\ + while (*temp != 0) {\ + temp++;\ + if (isspace(*(temp - 1)) && !isspace(*temp))\ + break;\ + } \ + } \ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + return count;\ +} \ + +#define show_store_replicated_func_unsigned(rtype, rgrp, propname)\ +show_store_replicated_func(rtype, rgrp, propname, "%u") + +#define DATA_REG_ADD(reg, skip, cond) \ + do { if (cond) { \ + attrs_data_regs_exist[reg_num] = true;\ + data->reg_##reg = kzalloc(sizeof(*data->reg_##reg),\ + GFP_KERNEL);\ + if (!data->reg_##reg)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: d%s addr = 0x%02x added (%d)\n",\ + __func__, #reg, reg_addr, reg_num);\ + data->reg_##reg->address = reg_addr;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define DATA_REG_PRESENCE(reg, skip, cond) \ + do { if (cond) { \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: d%s addr = 0x%02x\n",\ + __func__, #reg, reg_addr);\ + reg_addr += skip;\ + } \ + } while (0) + +#define CTRL_REG_ADD(reg, skip, cond) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true;\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x added (%d)\n",\ + __func__, #reg, reg_addr, reg_num);\ + control->reg_##reg->address = reg_addr | PAGE_SELECT_ENFORCE;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define CTRL_REG_ADD_EXT(reg, skip, cond, size) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true;\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + control->reg_##reg->data = kzalloc(size, GFP_KERNEL);\ + if (!control->reg_##reg->data)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x size = %zu added (%d)\n",\ + __func__, #reg, reg_addr, size, reg_num);\ + control->reg_##reg->length = size;\ + control->reg_##reg->address = reg_addr | PAGE_SELECT_ENFORCE;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define CTRL_SUBPKT_SZ(reg, subpkt) \ + sizeof(*((struct f54_control_##reg *)0)->sp##subpkt) + +#define CTRL_PKT_REG_START(reg, max_size) \ + do {\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + control->reg_##reg->data = kzalloc(max_size,\ + GFP_KERNEL);\ + if (!control->reg_##reg->data)\ + goto exit_no_mem;\ + } while (0) + +#define CTRL_SUBPKT_ADD(reg, subpkt, cond) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true; \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: subpkt c%s_%d added (%d)\n", \ + __func__, #reg, subpkt, reg_num); \ + control->reg_##reg->sp##subpkt = \ + (void *)control->reg_##reg->data + \ + control->reg_##reg->size; \ + control->reg_##reg->size += \ + sizeof(*control->reg_##reg->sp##subpkt); \ + } \ + reg_num++;\ + } while (0) + +#define CTRL_PKT_REG_END(reg, skip) \ + do { if (control->reg_##reg->size) { \ + control->reg_##reg->address = reg_addr; \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x\n", \ + __func__, #reg, reg_addr); \ + reg_addr += skip; \ + } \ + else { \ + kfree(control->reg_##reg->data); \ + kfree(control->reg_##reg); \ + } } while (0) + +#define CTRL_REG_PRESENCE(reg, skip, cond) \ + do { if ((cond)) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x\n",\ + __func__, #reg, reg_addr);\ + reg_addr += (skip);\ + } } while (0) + +#define CTRL_REG_RESERVED_PRESENCE(reg, skip, cond) \ + do { if ((cond)) { \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x (reserved)\n",\ + __func__, #reg, reg_addr);\ + reg_addr += skip;\ + } } while (0) + +#define QUERY_REG_READ(reg, cond)\ + do { if (cond) { \ + retval = f54->fn_ptr->read(rmi4_data,\ + reg_addr,\ + f54->query##reg.data,\ + sizeof(f54->query##reg.data));\ + if (retval < 0) { \ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read query %s register\n",\ + #reg, __func__);\ + goto err;\ + } \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: q%s addr = 0x%02x, val = 0x%02x\n",\ + __func__, #reg, reg_addr, f54->query##reg.data[0]);\ + reg_addr += 1;\ + } \ + else { \ + memset(&f54->query##reg.data, 0, sizeof(f54->query##reg.data));\ + } } while (0) + +enum f54_report_types { + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_HIGH_RESISTANCE = 4, + F54_TX_TO_TX_SHORT = 5, + F54_RX_TO_RX1 = 7, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP_MIN_MAX = 13, + F54_RX_OPENS1 = 14, + F54_TX_OPEN = 15, + F54_TX_TO_GROUND = 16, + F54_RX_TO_RX2 = 17, + F54_RX_OPENS2 = 18, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20, + F54_SENSOR_SPEED = 22, + F54_ADC_RANGE = 23, + F54_TREX_OPENS = 24, + F54_TREX_TO_GND = 25, + F54_TREX_SHORTS = 26, + F54_HYB_ABS_RX_TX_DELTA = 59, + F54_HYB_ABS_RX_TX_RAW = 63, + INVALID_REPORT_TYPE = -1, +}; + +struct f54_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char f54_query2_b0__1:2; + unsigned char has_baseline:1; + unsigned char has_image8:1; + unsigned char f54_query2_b4__5:2; + unsigned char has_image16:1; + unsigned char f54_query2_b7:1; + + /* queries 3.0 and 3.1 */ + unsigned short clock_rate; + + /* query 4 */ + unsigned char touch_controller_family; + + /* query 5 */ + unsigned char has_pixel_touch_threshold_adjustment:1; + unsigned char f54_query5_b1__7:7; + + /* query 6 */ + unsigned char has_sensor_assignment:1; + unsigned char has_interference_metric:1; + unsigned char has_sense_frequency_control:1; + unsigned char has_firmware_noise_mitigation:1; + unsigned char has_ctrl11:1; + unsigned char has_two_byte_report_rate:1; + unsigned char has_one_byte_report_rate:1; + unsigned char has_relaxation_control:1; + + /* query 7 */ + unsigned char curve_compensation_mode:2; + unsigned char f54_query7_b2__7:6; + + /* query 8 */ + unsigned char f54_query8_b0:1; + unsigned char has_iir_filter:1; + unsigned char has_cmn_removal:1; + unsigned char has_cmn_maximum:1; + unsigned char has_touch_hysteresis:1; + unsigned char has_edge_compensation:1; + unsigned char has_per_frequency_noise_control:1; + unsigned char has_enhanced_stretch:1; + + /* query 9 */ + unsigned char has_force_fast_relaxation:1; + unsigned char has_multi_metric_state_machine:1; + unsigned char has_signal_clarity:1; + unsigned char has_variance_metric:1; + unsigned char has_0d_relaxation_control:1; + unsigned char has_0d_acquisition_control:1; + unsigned char has_status:1; + unsigned char has_slew_metric:1; + + /* query 10 */ + unsigned char has_h_blank:1; + unsigned char has_v_blank:1; + unsigned char has_long_h_blank:1; + unsigned char has_startup_fast_relaxation:1; + unsigned char has_esd_control:1; + unsigned char has_noise_mitigation2:1; + unsigned char has_noise_state:1; + unsigned char has_energy_ratio_relaxation:1; + + /* query 11 */ + unsigned char has_excessive_noise_reporting:1; + unsigned char has_slew_option:1; + unsigned char has_two_overhead_bursts:1; + unsigned char has_query13:1; + unsigned char has_one_overhead_burst:1; + unsigned char f54_query11_b5:1; + unsigned char has_ctrl88:1; + unsigned char has_query15:1; + } __packed; + unsigned char data[13]; + }; +}; + +struct f54_query12 { + union { + struct { + unsigned char number_of_sensing_frequencies:4; + unsigned char f54_query12_b4__7:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query13 { + union { + struct { + unsigned char has_ctrl86:1; + unsigned char has_ctrl87:1; + unsigned char has_ctrl87_sub0:1; + unsigned char has_ctrl87_sub1:1; + unsigned char has_ctrl87_sub2:1; + unsigned char has_cid_im:1; + unsigned char has_noise_mitigation_enh:1; + unsigned char has_rail_im:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query14 { + union { + struct { + unsigned char size_of_ctr87; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query15 { + union { + struct { + unsigned char has_ctrl90:1; + unsigned char has_traismit_strength:1; + unsigned char has_ctrl87_sub3:1; + unsigned char has_query16:1; + unsigned char has_query20:1; + unsigned char has_query21:1; + unsigned char has_query22:1; + unsigned char has_query25:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query16 { + union { + struct { + unsigned char has_query17:1; + unsigned char has_data17:1; + unsigned char has_ctrl92:1; + unsigned char has_ctrl93:1; + unsigned char has_ctrl94_query18:1; + unsigned char has_ctrl95_query19:1; + unsigned char has_ctrl99:1; + unsigned char has_ctrl100:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query17 { + union { + struct { + unsigned char q17_num_of_sense_freqs; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query18 { + union { + struct { + unsigned char has_ctrl94_sub0:1; + unsigned char has_ctrl94_sub1:1; + unsigned char has_ctrl94_sub2:1; + unsigned char has_ctrl94_sub3:1; + unsigned char has_ctrl94_sub4:1; + unsigned char has_ctrl94_sub5:1; + unsigned char has_ctrl94_sub6:1; + unsigned char has_ctrl94_sub7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query19 { + union { + struct { + unsigned char size_of_ctrl95; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query20 { + union { + struct { + unsigned char adc_clock_divisor; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query21 { + union { + struct { + unsigned char has_abs_rx:1; + unsigned char has_abs_tx:1; + unsigned char has_ctrl91:1; + unsigned char has_ctrl96:1; + unsigned char has_ctrl97:1; + unsigned char has_ctrl98:1; + unsigned char has_data19:1; + unsigned char has_query24_data18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query22 { + union { + struct { + unsigned char has_packed_image:1; + unsigned char has_ctrl101:1; + unsigned char has_dynamic_sense_display_ratio:1; + unsigned char has_query23:1; + unsigned char has_ctrl103_query26:1; + unsigned char has_ctrl104:1; + unsigned char has_ctrl105:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query23 { + union { + struct { + unsigned char has_ctrl102:1; + unsigned char has_ctrl102_sub1:1; + unsigned char has_ctrl102_sub2:1; + unsigned char has_ctrl102_sub4:1; + unsigned char has_ctrl102_sub5:1; + unsigned char has_ctrl102_sub9:1; + unsigned char has_ctrl102_sub10:1; + unsigned char has_ctrl102_sub11:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query24 { + union { + struct { + unsigned char size_of_data18; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query25 { + union { + struct { + unsigned char has_ctrl106:1; + unsigned char has_ctrl102_sub12:1; + unsigned char has_ctrl107:1; + unsigned char has_ctrl108:1; + unsigned char has_ctrl109:1; + unsigned char has_data20:1; + unsigned char has_tags_for_moisture:1; + unsigned char has_query27:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query26 { + union { + struct { + unsigned char has_ctrl103_sub0:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query27 { + union { + struct { + unsigned char has_ctrl110:1; + unsigned char has_data21:1; + unsigned char has_ctrl111:1; + unsigned char has_ctrl112:1; + unsigned char has_ctrl113:1; + unsigned char has_data22:1; + unsigned char has_ctrl114:1; + unsigned char has_query29:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query28 { + union { + struct { + unsigned char has_capacitance_correction; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query29 { + union { + struct { + unsigned char has_ctrl115:1; + unsigned char has_ground_ring_options:1; + unsigned char has_lost_burst_tuning:1; + unsigned char has_aux_exvcom2_select:1; + unsigned char has_ctrl116:1; + unsigned char has_data23:1; + unsigned char has_ctrl117:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query30 { + union { + struct { + unsigned char has_ctrl118:1; + unsigned char has_ctrl119:1; + unsigned char has_ctrl120:1; + unsigned char has_ctrl121:1; + unsigned char has_ctrl122_query31:1; + unsigned char has_ctrl123:1; + unsigned char has_ctrl124:1; + unsigned char has_query32:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query31 { + union { + struct { + unsigned char num_of_active_stylus_sensing_freqs; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query32 { + union { + struct { + unsigned char has_ctrl125:1; + unsigned char has_ctrl126:1; + unsigned char has_ctrl127:1; + unsigned char has_abs_charge_pump_disable:1; + unsigned char has_query33:1; + unsigned char has_data24:1; + unsigned char has_query34:1; + unsigned char has_query35:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query33 { + union { + struct { + unsigned char has_ctrl128:1; + unsigned char has_ctrl129:1; + unsigned char has_ctrl130:1; + unsigned char has_ctrl131:1; + unsigned char has_ctrl132:1; + unsigned char has_ctrl133:1; + unsigned char has_ctrl134:1; + unsigned char has_query36:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query34 { + union { + struct { + unsigned char max_fnm_sliding_window_width; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query35 { + union { + struct { + unsigned char has_data25:1; + unsigned char has_ctrl135:1; + unsigned char has_ctrl136:1; + unsigned char has_ctrl137:1; + unsigned char has_ctrl138:1; + unsigned char has_ctrl139:1; + unsigned char has_data26:1; + unsigned char has_ctrl140:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query36 { + union { + struct { + unsigned char has_ctrl141:1; + unsigned char has_ctrl142:1; + unsigned char has_query37:1; + unsigned char has_ctrl143:1; + unsigned char has_ctrl144:1; + unsigned char has_ctrl145:1; + unsigned char has_ctrl146:1; + unsigned char has_query38:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query38 { + union { + struct { + unsigned char has_ctrl147:1; + unsigned char has_ctrl148:1; + unsigned char has_ctrl149:1; + unsigned char has_ctrl150:1; + unsigned char has_ctrl151:1; + unsigned char has_ctrl152:1; + unsigned char has_ctrl153:1; + unsigned char has_query39:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query39 { + union { + struct { + unsigned char has_ctrl154:1; + unsigned char has_ctrl155:1; + unsigned char has_ctrl156:1; + unsigned char has_ctrl160:1; + unsigned char has_ctrl157_158:1; + unsigned char f54_q39_b5b6:2; + unsigned char has_query40:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query40 { + union { + struct { + unsigned char has_ctrl169:1; + unsigned char has_ctrl163_query41:1; + unsigned char f54_q40_b2:1; + unsigned char has_ctrl165_query42:1; + unsigned char has_ctrl166:1; + unsigned char has_ctrl167:1; + unsigned char has_ctrl168:1; + unsigned char has_query43:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query41 { + union { + struct { + unsigned char size_of_ctrl163; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query42 { + union { + struct { + unsigned char size_of_ctrl165; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query43 { + union { + struct { + unsigned char f54_q43_b0b1:2; + unsigned char has_ctrl171:1; + unsigned char has_ctrl172_query44_query45:1; + unsigned char has_ctrl173:1; + unsigned char has_ctrl174:1; + unsigned char has_ctrl175:1; + unsigned char has_query46:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query46 { + union { + struct { + unsigned char has_ctrl176:1; + unsigned char has_ctrl177_178:1; + unsigned char has_ctrl179:1; + unsigned char f54_q46_b3:1; + unsigned char has_data27:1; + unsigned char has_data28:1; + unsigned char f54_q46_b6:1; + unsigned char has_query47:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query47 { + union { + struct { + unsigned char f54_q47_b0:1; + unsigned char has_ctrl182:1; + unsigned char has_ctrl183:1; + unsigned char f54_q47_b3:1; + unsigned char has_ctrl185:1; + unsigned char has_ctrl186:1; + unsigned char has_ctrl187:1; + unsigned char has_query49:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query49 { + union { + struct { + unsigned char f54_q49_b0:1; + unsigned char has_data30:1; + unsigned char has_ctrl188:1; + unsigned char has_data31:1; + unsigned char f54_q49_b4b5:2; + unsigned char has_ctrl190:1; + unsigned char has_query50:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query50 { + union { + struct { + unsigned char f54_q50_b0b1:2; + unsigned char has_ctrl193:1; + unsigned char has_query52:1; + unsigned char f54_q50_b4b6:3; + unsigned char has_query51:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query51 { + union { + struct { + unsigned char has_signal_clarity:1; + unsigned char f54_q51_b1:1; + unsigned char has_ctrl197:1; + unsigned char has_negative_cp:1; + unsigned char f54_q51_b4:1; + unsigned char has_query53_query54_ctrl198:1; + unsigned char has_ctrl199:1; + unsigned char has_query55:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query55 { + union { + struct { + unsigned char has_query56:1; + unsigned char has_data33_data34:1; + unsigned char has_alt_report_rate:1; + unsigned char has_ctrl200:1; + unsigned char has_ctrl201_ctrl202:1; + unsigned char has_ctrl203:1; + unsigned char has_ctrl204:1; + unsigned char has_query57:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query57 { + union { + struct { + unsigned char has_ctrl205:1; + unsigned char has_ctrl206:1; + unsigned char has_usb_bulk_read:1; + unsigned char has_ctrl207:1; + unsigned char has_ctrl208:1; + unsigned char has_ctrl209:1; + unsigned char has_ctrl210:1; + unsigned char has_query58:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query58 { + union { + struct { + unsigned char has_query59:1; + unsigned char has_query60:1; + unsigned char has_ctrl211:1; + unsigned char has_ctrl212:1; + unsigned char has_hybrid_abs_tx_axis_filtering:1; + unsigned char has_hybrid_abs_tx_interpolation:1; + unsigned char has_ctrl213:1; + unsigned char has_query61:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query61 { + union { + struct { + unsigned char has_ctrl214:1; + unsigned char has_ctrl215_query62_query63:1; + unsigned char f54_query_61_b2:1; + unsigned char has_ctrl216:1; + unsigned char has_ctrl217:1; + unsigned char has_misc_host_ctrl:1; + unsigned char has_ctrl219:1; + unsigned char has_query64:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query64 { + union { + struct { + unsigned char has_ctrl101_sub1:1; + unsigned char has_ctrl220:1; + unsigned char has_ctrl221:1; + unsigned char has_ctrl222:1; + unsigned char has_ctrl219_sub1:1; + unsigned char has_ctrl103_sub3:1; + unsigned char has_ctrl224_ctrl226_ctrl227:1; + unsigned char has_query65:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query65 { + union { + struct { + unsigned char f54_q65_b0b1:2; + unsigned char has_ctrl101_sub2:1; + unsigned char f54_65_b3:1; + unsigned char has_ctrl230:1; + unsigned char has_query66_ctrl231:1; + unsigned char has_ctrl232:1; + unsigned char has_query67:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query67 { + union { + struct { + unsigned char has_abs_doze_spatial_filter_en:1; + unsigned char has_abs_doze_avg_filter_enhancement_en:1; + unsigned char has_single_display_pulse:1; + unsigned char f54_q67_b3b4:2; + unsigned char has_ctrl235_ctrl236:1; + unsigned char has_ctrl109_sub1_sub2:1; + unsigned char has_query68:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query68 { + union { + struct { + unsigned char has_ctrl237:1; + unsigned char has_ctrl238:1; + unsigned char has_ctrl238_sub1:1; + unsigned char has_ctrl238_sub2:1; + unsigned char has_ctrl239:1; + unsigned char has_freq_filter_ext:1; + unsigned char is_tddi_hic:1; + unsigned char has_query69:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query69 { + union { + struct { + unsigned char has_ctrl240sub0:1; + unsigned char has_ctrl240sub1sub2:1; + unsigned char has_ctrl240sub3:1; + unsigned char has_ctrl240sub4:1; + unsigned char f54_q69_b4b6:3; + unsigned char has_query70:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query70 { + union { + struct { + unsigned char f54_q70_b0b1:2; + unsigned char has_ctrl242:1; + unsigned char f54_q70_b3b6:4; + unsigned char has_query71:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query71 { + union { + struct { + unsigned char f54_q71_b0b6:7; + unsigned char has_query72:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query72 { + union { + struct { + unsigned char f54_q72_b0b6:7; + unsigned char has_query73:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query73 { + union { + struct { + unsigned char f54_q73_b0b6:7; + unsigned char has_query74:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query74 { + union { + struct { + unsigned char f54_q74_b0b6:7; + unsigned char has_query75:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query75 { + union { + struct { + unsigned char f54_q75_b0b1:2; + unsigned char has_ctrl269:1; + unsigned char f54_q75_b3b7:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_control_0 { + union { + struct { + unsigned char no_relax:1; + unsigned char no_scan:1; + unsigned char force_fast_relaxation:1; + unsigned char startup_fast_relaxation:1; + unsigned char gesture_cancels_sfr:1; + unsigned char enable_energy_ratio_relaxation:1; + unsigned char excessive_noise_attn_enable:1; + unsigned char f54_control0_b7:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_1 { + union { + struct { + unsigned char bursts_per_cluster:4; + unsigned char f54_ctrl1_b4__7:4; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_2 { + union { + struct { + unsigned short saturation_cap; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_3 { + union { + struct { + unsigned char pixel_touch_threshold; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_4__6 { + union { + struct { + /* control 4 */ + unsigned char rx_feedback_cap:2; + unsigned char bias_current:2; + unsigned char f54_ctrl4_b4__7:4; + + /* control 5 */ + unsigned char low_ref_cap:2; + unsigned char low_ref_feedback_cap:2; + unsigned char low_ref_polarity:1; + unsigned char f54_ctrl5_b5__7:3; + + /* control 6 */ + unsigned char high_ref_cap:2; + unsigned char high_ref_feedback_cap:2; + unsigned char high_ref_polarity:1; + unsigned char f54_ctrl6_b5__7:3; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_7 { + union { + struct { + unsigned char cbc_cap:2; + unsigned char cbc_polarity:2; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl7_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_8__9 { + union { + struct { + /* control 8 */ + unsigned short integration_duration:10; + unsigned short f54_ctrl8_b10__15:6; + + /* control 9 */ + unsigned char reset_duration; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_10 { + union { + struct { + unsigned char noise_sensing_bursts_per_image:4; + unsigned char f54_ctrl10_b4__7:4; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_11 { + union { + struct { + unsigned short f54_ctrl11; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_12__13 { + union { + struct { + /* control 12 */ + unsigned char slow_relaxation_rate; + + /* control 13 */ + unsigned char fast_relaxation_rate; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_14 { + union { + struct { + unsigned char rxs_on_xaxis:1; + unsigned char curve_comp_on_txs:1; + unsigned char f54_ctrl14_b2__7:6; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_15n { + unsigned char sensor_rx_assignment; +}; + +struct f54_control_15 { + struct f54_control_15n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_16n { + unsigned char sensor_tx_assignment; +}; + +struct f54_control_16 { + struct f54_control_16n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_17n { + unsigned char burst_count_b8__10:3; + unsigned char disable:1; + unsigned char f54_ctrl17_b4:1; + unsigned char filter_bandwidth:3; +}; + +struct f54_control_17 { + struct f54_control_17n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_18n { + unsigned char burst_count_b0__7; +}; + +struct f54_control_18 { + struct f54_control_18n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_19n { + unsigned char stretch_duration; +}; + +struct f54_control_19 { + struct f54_control_19n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_20 { + union { + struct { + unsigned char disable_noise_mitigation:1; + unsigned char f54_ctrl20_b2__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_21 { + union { + struct { + unsigned short freq_shift_noise_threshold; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_22__26 { + union { + struct { + /* control 22 */ + unsigned char f54_ctrl22; + + /* control 23 */ + unsigned short medium_noise_threshold; + + /* control 24 */ + unsigned short high_noise_threshold; + + /* control 25 */ + unsigned char noise_density; + + /* control 26 */ + unsigned char frame_count; + } __packed; + struct { + unsigned char data[7]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_27 { + union { + struct { + unsigned char iir_filter_coef; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_28 { + union { + struct { + unsigned short quiet_threshold; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_29 { + union { + struct { + /* control 29 */ + unsigned char f54_ctrl29_b0__6:7; + unsigned char cmn_filter_disable:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_30 { + union { + struct { + unsigned char cmn_filter_max; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_31 { + union { + struct { + unsigned char touch_hysteresis; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_32__35 { + union { + struct { + /* control 32 */ + unsigned short rx_low_edge_comp; + + /* control 33 */ + unsigned short rx_high_edge_comp; + + /* control 34 */ + unsigned short tx_low_edge_comp; + + /* control 35 */ + unsigned short tx_high_edge_comp; + } __packed; + struct { + unsigned char data[8]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_36n { + unsigned char axis1_comp; +}; + +struct f54_control_36 { + struct f54_control_36n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_37n { + unsigned char axis2_comp; +}; + +struct f54_control_37 { + struct f54_control_37n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_38n { + unsigned char noise_control_1; +}; + +struct f54_control_38 { + struct f54_control_38n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_39n { + unsigned char noise_control_2; +}; + +struct f54_control_39 { + struct f54_control_39n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_40n { + unsigned char noise_control_3; +}; + +struct f54_control_40 { + struct f54_control_40n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_89_subpkt0 { + unsigned char c89_cid_sel_opt:2; + unsigned char c89_cid_voltage_sel:3; + unsigned char c89_byte0_b5_b7:3; + unsigned char c89_cid_im_noise_threshold_lsb; + unsigned char c89_cid_im_noise_threshold_msb; +} __packed; + +struct f54_control_89_subpkt1 { + unsigned char c89_fnm_pixel_touch_mult; + unsigned char c89_freq_scan_threshold_lsb; + unsigned char c89_freq_scan_threshold_msb; + unsigned char c89_quiet_im_threshold_lsb; + unsigned char c89_quiet_im_threshold_msb; +} __packed; + +struct f54_control_89_subpkt2 { + unsigned char c89_rail_sel_opt:2; + unsigned char c89_byte0_b3_b7:6; + unsigned char c89_rail_im_noise_threshold_lsb; + unsigned char c89_rail_im_noise_threshold_msb; + unsigned char c89_rail_im_quiet_threshold_lsb; + unsigned char c89_rail_im_quiet_threshold_msb; +} __packed; + +struct f54_control_89 { + unsigned char *data; + int size; + unsigned short address; + struct f54_control_89_subpkt0 *sp0; + struct f54_control_89_subpkt1 *sp1; + struct f54_control_89_subpkt2 *sp2; +}; + +struct f54_control_93 { + union { + struct { + unsigned char c93_freq_shift_noise_threshold_lsb; + unsigned char c93_freq_shift_noise_threshold_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_95 { + struct f54_control_95n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_99 { + union { + struct { + unsigned char c99_int_dur_lsb; + unsigned char c99_int_dur_msb; + unsigned char c99_reset_dur; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_107 { + union { + struct { + unsigned char c107_abs_int_dur; + unsigned char c107_abs_reset_dur; + unsigned char c107_abs_filter_bw; + unsigned char c107_abs_rstretch; + unsigned char c107_abs_burst_count_1; + unsigned char c107_abs_burst_count_2; + unsigned char c107_abs_stretch_dur; + unsigned char c107_abs_adc_clock_div; + unsigned char c107_abs_sub_burtst_size; + unsigned char c107_abs_trigger_delay; + } __packed; + struct { + unsigned char data[10]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_113 { + union { + struct { + unsigned char c113_en_hybrid_on_tx:1; + unsigned char c113_en_hybrid_on_rx:1; + unsigned char c113_tx_consistency_chk:1; + unsigned char c113_rx_consistency_chk:1; + unsigned char c113_en_gradient_baseline:1; + unsigned char c113_byte0_b5b7:3; + unsigned char c113_consistency_thresh; + unsigned char c113_tx_obj_thresh; + unsigned char c113_rx_obj_thresh; + unsigned char c113_tx_thermal_update_int_lsb; + unsigned char c113_tx_thermal_update_int_msb; + unsigned char c113_rx_thermal_update_int_lsb; + unsigned char c113_rx_thermal_update_int_msb; + unsigned char c113_tx_negative_thresh; + unsigned char c113_rx_negative_thresh; + } __packed; + struct { + unsigned char data[10]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_116 { + union { + struct { + unsigned char c116_im_thresh; + unsigned char c116_debounce; + unsigned char c116_quiet_im; + unsigned char c116_filter; + } __packed; + struct { + unsigned char data[4]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_137 { + union { + struct { + unsigned char c137_cmnr_adjust; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_145 { + union { + struct { + unsigned char c145_bursts_per_cluster_rx; + unsigned char c145_bursts_per_cluster_tx; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_146 { + union { + struct { + unsigned char c146_int_dur_lsb; + unsigned char c146_int_dur_msb; + unsigned char c146_reset_dur; + unsigned char c146_filter_bandwidth; + unsigned char c146_stretch_dur; + unsigned char c146_burst_count1; + unsigned char c146_burst_count2; + unsigned char c146_stretch_dur2; + unsigned char c146_adc_clock_div; + } __packed; + struct { + unsigned char data[9]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_242 { + union { + struct { + unsigned char c242_cbc_servo_disable:1; + unsigned char c242_b1b7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_269 { + union { + struct { + unsigned char c269_bsc_calibration_en:1; + unsigned char c269_b1b7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control { + struct f54_control_0 *reg_0; + struct f54_control_1 *reg_1; + struct f54_control_2 *reg_2; + struct f54_control_3 *reg_3; + struct f54_control_4__6 *reg_4__6; + struct f54_control_7 *reg_7; + struct f54_control_8__9 *reg_8__9; + struct f54_control_10 *reg_10; + struct f54_control_11 *reg_11; + struct f54_control_12__13 *reg_12__13; + struct f54_control_14 *reg_14; + struct f54_control_15 *reg_15; + struct f54_control_16 *reg_16; + struct f54_control_17 *reg_17; + struct f54_control_18 *reg_18; + struct f54_control_19 *reg_19; + struct f54_control_20 *reg_20; + struct f54_control_21 *reg_21; + struct f54_control_22__26 *reg_22__26; + struct f54_control_27 *reg_27; + struct f54_control_28 *reg_28; + struct f54_control_29 *reg_29; + struct f54_control_30 *reg_30; + struct f54_control_31 *reg_31; + struct f54_control_32__35 *reg_32__35; + struct f54_control_36 *reg_36; + struct f54_control_37 *reg_37; + struct f54_control_38 *reg_38; + struct f54_control_39 *reg_39; + struct f54_control_40 *reg_40; + struct f54_control_89 *reg_89; + struct f54_control_93 *reg_93; + struct f54_control_95 *reg_95; + struct f54_control_99 *reg_99; + struct f54_control_107 *reg_107; + struct f54_control_113 *reg_113; + struct f54_control_116 *reg_116; + struct f54_control_137 *reg_137; + struct f54_control_145 *reg_145; + struct f54_control_146 *reg_146; + struct f54_control_242 *reg_242; + struct f54_control_269 *reg_269; + struct f55_control_0 *reg_0_f55; + struct f55_control_8 *reg_8_f55; +}; + +struct f54_data_4 { + union { + struct { + unsigned char d4_sense_freq_sel:4; + unsigned char d4_baseline_sel:2; + unsigned char d4_b6:1; + unsigned char d4_inhibit_freq_shift:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; +struct f54_data_6 { + union { + struct { + unsigned char d6_interference_metric_lsb; + unsigned char d6_interference_metric_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + + }; +}; + +struct f54_data_7_0 { + union { + struct { + unsigned char d7_current_report_rate_lsb; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_7_1 { + union { + struct { + unsigned char d7_current_report_rate_msb; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_8 { + union { + struct { + unsigned char d8_variance_metric_lsb; + unsigned char d8_variance_metric_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_9 { + union { + struct { + unsigned char d9_averaged_im_lsb; + unsigned char d9_averaged_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_10 { + union { + struct { + unsigned char d10_noise_state; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_14 { + union { + struct { + unsigned char d14_cid_im_lsb; + unsigned char d14_cid_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_16 { + union { + struct { + unsigned char d16_freq_scan_im_lsb; + unsigned char d16_freq_scan_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_17 { + union { + struct { + unsigned char d17_freq:7; + unsigned char d17_inhibit_freq_shift:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data { + struct f54_data_4 *reg_4; + struct f54_data_6 *reg_6; + struct f54_data_7_0 *reg_7_0; + struct f54_data_7_1 *reg_7_1; + struct f54_data_8 *reg_8; + struct f54_data_9 *reg_9; + struct f54_data_10 *reg_10; + struct f54_data_14 *reg_14; + struct f54_data_16 *reg_16; + struct f54_data_17 *reg_17; +}; + +struct f55_query_0_2 { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char has_sensor_assignment:1; + unsigned char has_edge_compensation:1; + unsigned char curve_compensation_mode:2; + unsigned char has_ctrl6:1; + unsigned char has_alternate_tx_assignment:1; + unsigned char f55_q2_has_single_layer_multitouch:1; + unsigned char has_query5:1; + } __packed; + unsigned char data[3]; + }; +}; + +struct f55_query_3 { + union { + struct { + unsigned char f55_q3_has_ctrl8:1; + unsigned char has_ctrl9:1; + unsigned char has_on_cell_pattern:1; + unsigned char has_data0:1; + unsigned char has_single_wide_pattern:1; + unsigned char has_mirrored_tx_pattern:1; + unsigned char has_discrete_pattern:1; + unsigned char has_query9:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_control_0 { + union { + struct { + unsigned char f55_c0_receivers_on_x:1; + unsigned char f55_c0_curve_compensation_on_tx:1; + unsigned char f55_c0_trx_sense:1; + unsigned char f55_c0_trx_config:1; + unsigned char f55_c0_guard_disable:1; + unsigned char f55_c0_b5_b7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f55_control_8 { + union { + struct { + unsigned char f55_c8_pattern_type; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct synaptics_rmi4_fn55_desc { + unsigned short query_base_addr; + unsigned short control_base_addr; +}; + +struct f54_kobj_data { + struct kobject kobj; + struct completion kobj_unregistered; + struct bin_attribute *dev_report_data; + struct synaptics_rmi4_f54_handle *f54; +}; + +#define to_f54_kobj_data(x) container_of(x, struct f54_kobj_data, kobj) +#define to_f54_attr(x) container_of(x, struct f54_attribute, attr) + +struct synaptics_rmi4_f54_handle { + bool init_done; + bool no_auto_cal; + int status; + unsigned char intr_mask; + unsigned char intr_reg_num; + unsigned char *report_data; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short fifoindex; + unsigned int report_size; + unsigned int data_buffer_size; + enum f54_report_types report_type; + enum f54_report_types user_report_type1; + enum f54_report_types user_report_type2; + struct mutex status_mutex; + struct mutex data_mutex; + struct mutex control_mutex; + struct f54_query query; + struct f54_query12 query12; + struct f54_query13 query13; + struct f54_query14 query14; + struct f54_query15 query15; + struct f54_query16 query16; + struct f54_query17 query17; + struct f54_query18 query18; + struct f54_query19 query19; + struct f54_query20 query20; + struct f54_query21 query21; + struct f54_query22 query22; + struct f54_query23 query23; + struct f54_query24 query24; + struct f54_query25 query25; + struct f54_query26 query26; + struct f54_query27 query27; + struct f54_query28 query28; + struct f54_query29 query29; + struct f54_query30 query30; + struct f54_query31 query31; + struct f54_query32 query32; + struct f54_query33 query33; + struct f54_query34 query34; + struct f54_query35 query35; + struct f54_query36 query36; + struct f54_query38 query38; + struct f54_query39 query39; + struct f54_query40 query40; + struct f54_query41 query41; + struct f54_query42 query42; + struct f54_query43 query43; + struct f54_query46 query46; + struct f54_query47 query47; + struct f54_query49 query49; + struct f54_query50 query50; + struct f54_query51 query51; + struct f54_query55 query55; + struct f54_query57 query57; + struct f54_query58 query58; + struct f54_query61 query61; + struct f54_query64 query64; + struct f54_query65 query65; + struct f54_query67 query67; + struct f54_query68 query68; + struct f54_query69 query69; + struct f54_query70 query70; + struct f54_query71 query71; + struct f54_query72 query72; + struct f54_query73 query73; + struct f54_query74 query74; + struct f54_query75 query75; + struct f54_control control; + struct f54_data data; + struct hrtimer watchdog; + struct work_struct timeout_work; + struct delayed_work status_work; + struct workqueue_struct *status_workqueue; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_fn55_desc *fn55; + struct f55_query_0_2 query_f55_0_2; + struct f55_query_3 query_f55_3; + struct wakeup_source test_wake_lock; + struct completion remove_complete; + struct f54_kobj_data *f54_kdata; +}; + +static ssize_t f54_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct f54_attribute *attribute = to_f54_attr(attr); + struct f54_kobj_data *f54_kobj_data = to_f54_kobj_data(kobj); + + if (!attribute->show) + return -EIO; + return attribute->show(f54_kobj_data->f54, buf); +} + +static ssize_t f54_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct f54_attribute *attribute = to_f54_attr(attr); + struct f54_kobj_data *f54_kobj_data = to_f54_kobj_data(kobj); + + if (!attribute->store) + return -EIO; + return attribute->store(f54_kobj_data->f54, buf, len); +} + +static const struct sysfs_ops f54_sysfs_ops = { + .show = f54_attr_show, + .store = f54_attr_store, +}; + +void f54_kobj_data_release(struct kobject *kobj) +{ + struct f54_kobj_data *f54_kobj_data; + + f54_kobj_data = to_f54_kobj_data(kobj); + dev_dbg(&f54_kobj_data->f54->rmi4_data->i2c_client->dev, + "%s: f54_kobj_data released\n", __func__); + complete(&f54_kobj_data->kobj_unregistered); +} + +static struct kobj_type f54_ktype = { + .sysfs_ops = &f54_sysfs_ops, + .release = f54_kobj_data_release, +}; + +store_prototype(reset) +show_prototype_ext(status, S_IRUGO) +show_prototype_ext(report_size, S_IRUGO) +show_prototype_ext(num_of_mapped_rx, S_IRUGO) +show_prototype_ext(num_of_mapped_tx, S_IRUGO) +show_store_prototype(no_auto_cal) +show_store_prototype_ext(report_type, S_IRUGO | S_IWUSR | S_IWGRP) +show_store_prototype_ext(user_report_type1, S_IRUGO | S_IWUSR | S_IWGRP) +show_store_prototype_ext(user_report_type2, S_IRUGO | S_IWUSR | S_IWGRP) +show_prototype_ext(user_get_report1, S_IRUGO) +show_prototype_ext(user_get_report2, S_IRUGO) +show_store_prototype(fifoindex) +store_prototype(get_report) +show_store_prototype(force_update) +store_prototype(force_cal) +show_store_prototype(enter_in_cell_test_mode) +show_prototype_ext(num_of_rx_electrodes, S_IRUGO) +show_prototype_ext(num_of_tx_electrodes, S_IRUGO) +show_prototype(has_image16) +show_prototype(has_image8) +show_prototype(has_baseline) +show_prototype(clock_rate) +show_prototype(touch_controller_family) +show_prototype(has_pixel_touch_threshold_adjustment) +show_prototype(has_sensor_assignment) +show_prototype(has_interference_metric) +show_prototype(has_sense_frequency_control) +show_prototype(has_firmware_noise_mitigation) +show_prototype(has_two_byte_report_rate) +show_prototype(has_one_byte_report_rate) +show_prototype(has_relaxation_control) +show_prototype(curve_compensation_mode) +show_prototype(has_iir_filter) +show_prototype(has_cmn_removal) +show_prototype(has_cmn_maximum) +show_prototype(has_touch_hysteresis) +show_prototype(has_edge_compensation) +show_prototype(has_per_frequency_noise_control) +show_prototype(has_enhanced_stretch) +show_prototype(has_force_fast_relaxation) +show_prototype(has_multi_metric_state_machine) +show_prototype(has_signal_clarity) +show_prototype(has_variance_metric) +show_prototype(has_0d_relaxation_control) +show_prototype(has_0d_acquisition_control) +show_prototype(has_status) +show_prototype(has_slew_metric) +show_prototype(has_h_blank) +show_prototype(has_v_blank) +show_prototype(has_long_h_blank) +show_prototype(has_startup_fast_relaxation) +show_prototype(has_esd_control) +show_prototype(has_noise_mitigation2) +show_prototype(has_noise_state) +show_prototype(has_energy_ratio_relaxation) +show_prototype(number_of_sensing_frequencies) +show_prototype(has_data17) +show_prototype(has_ctrl113) +show_prototype(has_ctrl145) +show_prototype(has_ctrl146) +show_prototype(q17_num_of_sense_freqs) +show_store_prototype(no_relax) +show_store_prototype(no_scan) +show_store_prototype(bursts_per_cluster) +show_store_prototype(saturation_cap) +show_store_prototype(pixel_touch_threshold) +show_store_prototype(rx_feedback_cap) +show_store_prototype(low_ref_cap) +show_store_prototype(low_ref_feedback_cap) +show_store_prototype(low_ref_polarity) +show_store_prototype(high_ref_cap) +show_store_prototype(high_ref_feedback_cap) +show_store_prototype(high_ref_polarity) +show_store_prototype(cbc_cap) +show_store_prototype(cbc_polarity) +show_store_prototype(cbc_tx_carrier_selection) +show_store_prototype(integration_duration) +show_store_prototype(reset_duration) +show_store_prototype(noise_sensing_bursts_per_image) +show_store_prototype(slow_relaxation_rate) +show_store_prototype(fast_relaxation_rate) +show_store_prototype(rxs_on_xaxis) +show_store_prototype(curve_comp_on_txs) +show_prototype(sensor_rx_assignment) +show_prototype(sensor_tx_assignment) +show_prototype(burst_count) +show_prototype(disable) +show_prototype(filter_bandwidth) +show_prototype(stretch_duration) +show_store_prototype(disable_noise_mitigation) +show_store_prototype(freq_shift_noise_threshold) +show_store_prototype(medium_noise_threshold) +show_store_prototype(high_noise_threshold) +show_store_prototype(noise_density) +show_store_prototype(frame_count) +show_store_prototype(iir_filter_coef) +show_store_prototype(quiet_threshold) +show_store_prototype(cmn_filter_disable) +show_store_prototype(cmn_filter_max) +show_store_prototype(touch_hysteresis) +show_store_prototype(rx_low_edge_comp) +show_store_prototype(rx_high_edge_comp) +show_store_prototype(tx_low_edge_comp) +show_store_prototype(tx_high_edge_comp) +show_store_prototype(axis1_comp) +show_store_prototype(axis2_comp) +show_prototype(noise_control_1) +show_prototype(noise_control_2) +show_prototype(noise_control_3) +show_store_prototype(d4_sense_freq_sel) +show_store_prototype(d4_baseline_sel) +show_store_prototype(d4_inhibit_freq_shift) +show_prototype(d6_interference_metric_lsb) +show_prototype(d6_interference_metric_msb) +show_prototype(d7_current_report_rate_lsb) +show_prototype(d7_current_report_rate_msb) +show_prototype(d8_variance_metric_lsb) +show_prototype(d8_variance_metric_msb) +show_prototype(d9_averaged_im_lsb) +show_prototype(d9_averaged_im_msb) +show_prototype(d10_noise_state) +show_store_prototype(d16_freq_scan_im_lsb) +show_store_prototype(d16_freq_scan_im_msb) +show_prototype(d14_cid_im_lsb) +show_prototype(d14_cid_im_msb) +show_store_prototype(d17_inhibit_freq_shift) +show_store_prototype(d17_freq) +show_store_prototype(c89_cid_sel_opt) +show_store_prototype(c89_cid_voltage_sel) +show_store_prototype(c89_cid_im_noise_threshold_lsb) +show_store_prototype(c89_cid_im_noise_threshold_msb) +show_store_prototype(c89_fnm_pixel_touch_mult) +show_store_prototype(c89_freq_scan_threshold_lsb) +show_store_prototype(c89_freq_scan_threshold_msb) +show_store_prototype(c89_quiet_im_threshold_lsb) +show_store_prototype(c89_quiet_im_threshold_msb) +show_store_prototype(c89_rail_sel_opt) +show_store_prototype(c89_rail_im_noise_threshold_lsb) +show_store_prototype(c89_rail_im_noise_threshold_msb) +show_store_prototype(c89_rail_im_quiet_threshold_lsb) +show_store_prototype(c89_rail_im_quiet_threshold_msb) +show_store_prototype(c93_freq_shift_noise_threshold_lsb) +show_store_prototype(c93_freq_shift_noise_threshold_msb) +show_store_prototype(c95_disable) +show_store_prototype(c95_filter_bw) +show_store_prototype(c95_first_burst_length_lsb) +show_store_prototype(c95_first_burst_length_msb) +show_store_prototype(c95_addl_burst_length_lsb) +show_store_prototype(c95_addl_burst_length_msb) +show_store_prototype(c95_i_stretch) +show_store_prototype(c95_r_stretch) +show_store_prototype(c95_noise_control1) +show_store_prototype(c95_noise_control2) +show_store_prototype(c95_noise_control3) +show_store_prototype(c95_noise_control4) +show_store_prototype(c99_int_dur_lsb) +show_store_prototype(c99_int_dur_msb) +show_store_prototype(c99_reset_dur) +show_store_prototype(c107_abs_int_dur) +show_store_prototype(c107_abs_reset_dur) +show_store_prototype(c107_abs_filter_bw) +show_store_prototype(c107_abs_rstretch) +show_store_prototype(c107_abs_burst_count_1) +show_store_prototype(c107_abs_burst_count_2) +show_store_prototype(c107_abs_stretch_dur) +show_store_prototype(c107_abs_adc_clock_div) +show_store_prototype(c107_abs_sub_burtst_size) +show_store_prototype(c107_abs_trigger_delay) +show_store_prototype(c113_en_hybrid_on_tx) +show_store_prototype(c113_en_hybrid_on_rx) +show_store_prototype(c113_tx_consistency_chk) +show_store_prototype(c113_rx_consistency_chk) +show_store_prototype(c113_en_gradient_baseline) +show_store_prototype(c113_consistency_thresh) +show_store_prototype(c113_tx_obj_thresh) +show_store_prototype(c113_rx_obj_thresh) +show_store_prototype(c113_tx_thermal_update_int_lsb) +show_store_prototype(c113_tx_thermal_update_int_msb) +show_store_prototype(c113_rx_thermal_update_int_lsb) +show_store_prototype(c113_rx_thermal_update_int_msb) +show_store_prototype(c113_tx_negative_thresh) +show_store_prototype(c113_rx_negative_thresh) +show_store_prototype(c116_im_thresh) +show_store_prototype(c116_debounce) +show_store_prototype(c116_quiet_im) +show_store_prototype(c116_filter) +show_store_prototype(c137_cmnr_adjust) +show_store_prototype(c145_bursts_per_cluster_rx) +show_store_prototype(c145_bursts_per_cluster_tx) +show_store_prototype(c146_int_dur_lsb) +show_store_prototype(c146_int_dur_msb) +show_store_prototype(c146_reset_dur) +show_store_prototype(c146_filter_bandwidth) +show_store_prototype(c146_stretch_dur) +show_store_prototype(c146_burst_count1) +show_store_prototype(c146_burst_count2) +show_store_prototype(c146_stretch_dur2) +show_store_prototype(c146_adc_clock_div) +show_store_prototype(c242_cbc_servo_disable) +show_store_prototype(c269_bsc_calibration_en) +show_prototype(f55_q2_has_single_layer_multitouch) +show_prototype(f55_c0_receivers_on_x) +show_prototype(f55_c8_pattern_type) + +static ssize_t synaptics_rmi4_f54_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct attribute *attrs[] = { + attrify(reset), + attrify(status), + attrify(report_size), + attrify(num_of_mapped_rx), + attrify(num_of_mapped_tx), + attrify(no_auto_cal), + attrify(report_type), + attrify(user_report_type1), + attrify(user_report_type2), + attrify(user_get_report1), + attrify(user_get_report2), + attrify(fifoindex), + attrify(get_report), + attrify(force_update), + attrify(force_cal), + attrify(enter_in_cell_test_mode), + attrify(num_of_rx_electrodes), + attrify(num_of_tx_electrodes), + attrify(has_image16), + attrify(has_image8), + attrify(has_baseline), + attrify(clock_rate), + attrify(touch_controller_family), + attrify(has_pixel_touch_threshold_adjustment), + attrify(has_sensor_assignment), + attrify(has_interference_metric), + attrify(has_sense_frequency_control), + attrify(has_firmware_noise_mitigation), + attrify(has_two_byte_report_rate), + attrify(has_one_byte_report_rate), + attrify(has_relaxation_control), + attrify(curve_compensation_mode), + attrify(has_iir_filter), + attrify(has_cmn_removal), + attrify(has_cmn_maximum), + attrify(has_touch_hysteresis), + attrify(has_edge_compensation), + attrify(has_per_frequency_noise_control), + attrify(has_enhanced_stretch), + attrify(has_force_fast_relaxation), + attrify(has_multi_metric_state_machine), + attrify(has_signal_clarity), + attrify(has_variance_metric), + attrify(has_0d_relaxation_control), + attrify(has_0d_acquisition_control), + attrify(has_status), + attrify(has_slew_metric), + attrify(has_h_blank), + attrify(has_v_blank), + attrify(has_long_h_blank), + attrify(has_startup_fast_relaxation), + attrify(has_esd_control), + attrify(has_noise_mitigation2), + attrify(has_noise_state), + attrify(has_energy_ratio_relaxation), + attrify(number_of_sensing_frequencies), + attrify(has_data17), + attrify(has_ctrl113), + attrify(has_ctrl145), + attrify(has_ctrl146), + attrify(q17_num_of_sense_freqs), + attrify(f55_q2_has_single_layer_multitouch), + NULL, +}; + +static struct attribute_group attr_group = GROUP(attrs); + +static struct attribute *attrs_reg_0[] = { + attrify(no_relax), + attrify(no_scan), + NULL, +}; + +static struct attribute *attrs_reg_1[] = { + attrify(bursts_per_cluster), + NULL, +}; + +static struct attribute *attrs_reg_2[] = { + attrify(saturation_cap), + NULL, +}; + +static struct attribute *attrs_reg_3[] = { + attrify(pixel_touch_threshold), + NULL, +}; + +static struct attribute *attrs_reg_4__6[] = { + attrify(rx_feedback_cap), + attrify(low_ref_cap), + attrify(low_ref_feedback_cap), + attrify(low_ref_polarity), + attrify(high_ref_cap), + attrify(high_ref_feedback_cap), + attrify(high_ref_polarity), + NULL, +}; + +static struct attribute *attrs_reg_7[] = { + attrify(cbc_cap), + attrify(cbc_polarity), + attrify(cbc_tx_carrier_selection), + NULL, +}; + +static struct attribute *attrs_reg_8__9[] = { + attrify(integration_duration), + attrify(reset_duration), + NULL, +}; + +static struct attribute *attrs_reg_10[] = { + attrify(noise_sensing_bursts_per_image), + NULL, +}; + +static struct attribute *attrs_reg_11[] = { + NULL, +}; + +static struct attribute *attrs_reg_12__13[] = { + attrify(slow_relaxation_rate), + attrify(fast_relaxation_rate), + NULL, +}; + +static struct attribute *attrs_reg_14__16[] = { + attrify(rxs_on_xaxis), + attrify(curve_comp_on_txs), + attrify(sensor_rx_assignment), + attrify(sensor_tx_assignment), + NULL, +}; + +static struct attribute *attrs_reg_17__19[] = { + attrify(burst_count), + attrify(disable), + attrify(filter_bandwidth), + attrify(stretch_duration), + NULL, +}; + +static struct attribute *attrs_reg_20[] = { + attrify(disable_noise_mitigation), + NULL, +}; + +static struct attribute *attrs_reg_21[] = { + attrify(freq_shift_noise_threshold), + NULL, +}; + +static struct attribute *attrs_reg_22__26[] = { + attrify(medium_noise_threshold), + attrify(high_noise_threshold), + attrify(noise_density), + attrify(frame_count), + NULL, +}; + +static struct attribute *attrs_reg_27[] = { + attrify(iir_filter_coef), + NULL, +}; + +static struct attribute *attrs_reg_28[] = { + attrify(quiet_threshold), + NULL, +}; + +static struct attribute *attrs_reg_29[] = { + attrify(cmn_filter_disable), + NULL, +}; + +static struct attribute *attrs_reg_30[] = { + attrify(cmn_filter_max), + NULL, +}; + +static struct attribute *attrs_reg_31[] = { + attrify(touch_hysteresis), + NULL, +}; + +static struct attribute *attrs_reg_32__35[] = { + attrify(rx_low_edge_comp), + attrify(rx_high_edge_comp), + attrify(tx_low_edge_comp), + attrify(tx_high_edge_comp), + NULL, +}; + +static struct attribute *attrs_reg_36[] = { + attrify(axis1_comp), + NULL, +}; + +static struct attribute *attrs_reg_37[] = { + attrify(axis2_comp), + NULL, +}; + +static struct attribute *attrs_reg_38__40[] = { + attrify(noise_control_1), + attrify(noise_control_2), + attrify(noise_control_3), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt0[] = { + attrify(c89_cid_sel_opt), + attrify(c89_cid_voltage_sel), + attrify(c89_cid_im_noise_threshold_lsb), + attrify(c89_cid_im_noise_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt1[] = { + attrify(c89_fnm_pixel_touch_mult), + attrify(c89_freq_scan_threshold_lsb), + attrify(c89_freq_scan_threshold_msb), + attrify(c89_quiet_im_threshold_lsb), + attrify(c89_quiet_im_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt2[] = { + attrify(c89_rail_sel_opt), + attrify(c89_rail_im_noise_threshold_lsb), + attrify(c89_rail_im_noise_threshold_msb), + attrify(c89_rail_im_quiet_threshold_lsb), + attrify(c89_rail_im_quiet_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_93[] = { + attrify(c93_freq_shift_noise_threshold_lsb), + attrify(c93_freq_shift_noise_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_95[] = { + attrify(c95_disable), + attrify(c95_filter_bw), + attrify(c95_first_burst_length_lsb), + attrify(c95_first_burst_length_msb), + attrify(c95_addl_burst_length_lsb), + attrify(c95_addl_burst_length_msb), + attrify(c95_i_stretch), + attrify(c95_r_stretch), + attrify(c95_noise_control1), + attrify(c95_noise_control2), + attrify(c95_noise_control3), + attrify(c95_noise_control4), + NULL, +}; + +static struct attribute *attrs_reg_99[] = { + attrify(c99_int_dur_lsb), + attrify(c99_int_dur_msb), + attrify(c99_reset_dur), + NULL, +}; + +static struct attribute *attrs_reg_107[] = { + attrify(c107_abs_int_dur), + attrify(c107_abs_reset_dur), + attrify(c107_abs_filter_bw), + attrify(c107_abs_rstretch), + attrify(c107_abs_burst_count_1), + attrify(c107_abs_burst_count_2), + attrify(c107_abs_stretch_dur), + attrify(c107_abs_adc_clock_div), + attrify(c107_abs_sub_burtst_size), + attrify(c107_abs_trigger_delay), + NULL, +}; + +static struct attribute *attrs_reg_113[] = { + attrify(c113_en_hybrid_on_tx), + attrify(c113_en_hybrid_on_rx), + attrify(c113_tx_consistency_chk), + attrify(c113_rx_consistency_chk), + attrify(c113_en_gradient_baseline), + attrify(c113_consistency_thresh), + attrify(c113_tx_obj_thresh), + attrify(c113_rx_obj_thresh), + attrify(c113_tx_thermal_update_int_lsb), + attrify(c113_tx_thermal_update_int_msb), + attrify(c113_rx_thermal_update_int_lsb), + attrify(c113_rx_thermal_update_int_msb), + attrify(c113_tx_negative_thresh), + attrify(c113_rx_negative_thresh), + NULL, +}; + +static struct attribute *attrs_reg_116[] = { + attrify(c116_im_thresh), + attrify(c116_debounce), + attrify(c116_quiet_im), + attrify(c116_filter), + NULL, +}; + +static struct attribute *attrs_reg_137[] = { + attrify(c137_cmnr_adjust), + NULL, +}; + +static struct attribute *attrs_reg_145[] = { + attrify(c145_bursts_per_cluster_rx), + attrify(c145_bursts_per_cluster_tx), + NULL, +}; + +static struct attribute *attrs_reg_146[] = { + attrify(c146_int_dur_lsb), + attrify(c146_int_dur_msb), + attrify(c146_reset_dur), + attrify(c146_filter_bandwidth), + attrify(c146_stretch_dur), + attrify(c146_burst_count1), + attrify(c146_burst_count2), + attrify(c146_stretch_dur2), + attrify(c146_adc_clock_div), + NULL, +}; + +static struct attribute *attrs_reg_242[] = { + attrify(c242_cbc_servo_disable), + NULL, +}; + +static struct attribute *attrs_reg_269[] = { + attrify(c269_bsc_calibration_en), + NULL, +}; + +static struct attribute *attrs_f55_c0[] = { + attrify(f55_c0_receivers_on_x), + NULL, +}; + +static struct attribute *attrs_f55_c8[] = { + attrify(f55_c8_pattern_type), + NULL, +}; + +static struct attribute_group attrs_ctrl_regs[] = { + GROUP(attrs_reg_0), + GROUP(attrs_reg_1), + GROUP(attrs_reg_2), + GROUP(attrs_reg_3), + GROUP(attrs_reg_4__6), + GROUP(attrs_reg_7), + GROUP(attrs_reg_8__9), + GROUP(attrs_reg_10), + GROUP(attrs_reg_11), + GROUP(attrs_reg_12__13), + GROUP(attrs_reg_14__16), + GROUP(attrs_reg_17__19), + GROUP(attrs_reg_20), + GROUP(attrs_reg_21), + GROUP(attrs_reg_22__26), + GROUP(attrs_reg_27), + GROUP(attrs_reg_28), + GROUP(attrs_reg_29), + GROUP(attrs_reg_30), + GROUP(attrs_reg_31), + GROUP(attrs_reg_32__35), + GROUP(attrs_reg_36), + GROUP(attrs_reg_37), + GROUP(attrs_reg_38__40), + GROUP(attrs_reg_89_subpkt0), + GROUP(attrs_reg_89_subpkt1), + GROUP(attrs_reg_89_subpkt2), + GROUP(attrs_reg_93), + GROUP(attrs_reg_95), + GROUP(attrs_reg_99), + GROUP(attrs_reg_107), + GROUP(attrs_reg_113), + GROUP(attrs_reg_116), + GROUP(attrs_reg_137), + GROUP(attrs_reg_145), + GROUP(attrs_reg_146), + GROUP(attrs_reg_242), + GROUP(attrs_reg_269), + GROUP(attrs_f55_c0), + GROUP(attrs_f55_c8), +}; + +static bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)]; + +static struct attribute *data_attrs_reg_4[] = { + attrify(d4_sense_freq_sel), + attrify(d4_baseline_sel), + attrify(d4_inhibit_freq_shift), + NULL, +}; + +static struct attribute *data_attrs_reg_6[] = { + attrify(d6_interference_metric_lsb), + attrify(d6_interference_metric_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_7_0[] = { + attrify(d7_current_report_rate_lsb), + NULL, +}; + +static struct attribute *data_attrs_reg_7_1[] = { + attrify(d7_current_report_rate_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_8[] = { + attrify(d8_variance_metric_lsb), + attrify(d8_variance_metric_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_9[] = { + attrify(d9_averaged_im_lsb), + attrify(d9_averaged_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_10[] = { + attrify(d10_noise_state), + NULL, +}; + +static struct attribute *data_attrs_reg_14[] = { + attrify(d14_cid_im_lsb), + attrify(d14_cid_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_16[] = { + attrify(d16_freq_scan_im_lsb), + attrify(d16_freq_scan_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_17[] = { + attrify(d17_freq), + attrify(d17_inhibit_freq_shift), + NULL, +}; + +static struct attribute_group attrs_data_regs[] = { + GROUP(data_attrs_reg_4), + GROUP(data_attrs_reg_6), + GROUP(data_attrs_reg_7_0), + GROUP(data_attrs_reg_7_1), + GROUP(data_attrs_reg_8), + GROUP(data_attrs_reg_9), + GROUP(data_attrs_reg_10), + GROUP(data_attrs_reg_14), + GROUP(data_attrs_reg_16), + GROUP(data_attrs_reg_17), +}; + +static bool attrs_data_regs_exist[ARRAY_SIZE(attrs_data_regs)]; + +static inline ssize_t synaptics_dsx_test_reporting_show_error( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Attempted to read from write-only attribute\n", __func__); + return -EPERM; +} + +static inline ssize_t synaptics_dsx_test_reporting_store_error( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Attempted to write to read-only attribute\n", __func__); + return -EPERM; +} + +static bool is_report_type_valid(enum f54_report_types report_type) +{ + switch (report_type) { + case F54_8BIT_IMAGE: + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TX_TO_TX_SHORT: + case F54_RX_TO_RX1: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_RX_OPENS1: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_TREX_OPENS: + case F54_TREX_TO_GND: + case F54_TREX_SHORTS: + case F54_HYB_ABS_RX_TX_DELTA: + case F54_HYB_ABS_RX_TX_RAW: + return true; + break; + default: + return false; + } +} + +static int set_user_report_type(struct synaptics_rmi4_f54_handle *f54, + enum f54_report_types *user_report_type, + const char *buf, size_t count) +{ + unsigned int report_type; + int retval = 0; + retval = kstrtouint(buf, 10, &report_type); + if (retval) + goto out; + + if (!is_report_type_valid((enum f54_report_types)report_type)) { + dev_err(&f54->rmi4_data->i2c_client->dev, + "%s: Report type not supported by driver\n", + __func__); + retval = -EINVAL; + } + +out: + if (retval == 0) { + *user_report_type = report_type; + retval = count; + } else + *user_report_type = INVALID_REPORT_TYPE; + + return retval; +} + +static ssize_t synaptics_rmi4_f54_user_report_type1_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + return set_user_report_type(f54, &f54->user_report_type1, buf, count); +} + +static ssize_t synaptics_rmi4_f54_user_report_type2_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + return set_user_report_type(f54, &f54->user_report_type2, buf, count); +} + +static void set_report_size(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char rx = f54->rmi4_data->num_of_rx; + unsigned char tx = f54->rmi4_data->num_of_tx; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + f54->report_size = rx * tx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + f54->report_size = 2 * rx * tx; + break; + case F54_HIGH_RESISTANCE: + f54->report_size = HIGH_RESISTANCE_DATA_SIZE; + break; + case F54_TX_TO_TX_SHORT: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + f54->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX1: + case F54_RX_OPENS1: + if (rx < tx) + f54->report_size = 2 * rx * rx; + else + f54->report_size = 2 * rx * tx; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + f54->report_size = FULL_RAW_CAP_MIN_MAX_DATA_SIZE; + break; + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + if (rx <= tx) + f54->report_size = 0; + else + f54->report_size = 2 * rx * (rx - tx); + break; + case F54_TREX_OPENS: + case F54_TREX_TO_GND: + case F54_TREX_SHORTS: + f54->report_size = TREX_DATA_SIZE; + break; + case F54_HYB_ABS_RX_TX_DELTA: + case F54_HYB_ABS_RX_TX_RAW: + f54->report_size = 4 * (rx + tx); + break; + default: + f54->report_size = 0; + } + pr_debug("report size %d\n", f54->report_size); + return; +} + +int set_interrupt(struct synaptics_rmi4_f54_handle *f54, bool set) +{ + int retval; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short f01_ctrl_reg; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + intr_mask = rmi4_data->intr_mask; + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (!set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + pr_debug("cleared intr_mask\n"); + } + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + pr_debug("cleared intr_mask\n"); + } else { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } + } + } + + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &f54->intr_mask, + 1); + if (retval < 0) + return retval; + pr_debug("set intr_mask = %d\n", f54->intr_mask); + } + + return 0; +} + +#ifdef WATCHDOG_HRTIMER +static void timeout_set_status(struct work_struct *work) +{ + int retval; + unsigned char command; + struct synaptics_rmi4_f54_handle *f54 = container_of(work, + struct synaptics_rmi4_f54_handle, timeout_work); + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + if (f54->status == STATUS_BUSY) { + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read F54_Cmd0=%d, rc = %d\n", + __func__, command, retval); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command register\n", + __func__); + } else if (command & COMMAND_GET_REPORT) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report type not supported by FW\n", + __func__); + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Get report not detected\n", + __func__); + } + f54->status = -ETIMEDOUT; + f54->report_type = INVALID_REPORT_TYPE; + f54->report_size = 0; + set_interrupt(f54, false); + __pm_relax(&f54->test_wake_lock); + } + mutex_unlock(&f54->status_mutex); + + return; +} + +static enum hrtimer_restart get_report_timeout(struct hrtimer *timer) +{ + struct synaptics_rmi4_f54_handle *f54 = container_of(timer, + struct synaptics_rmi4_f54_handle, watchdog); + + schedule_work(&(f54->timeout_work)); + + return HRTIMER_NORESTART; +} +#endif + +#ifdef RAW_HEX +static void print_raw_hex_report(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned int ii; + + pr_info("%s: Report data (raw hex)\n", __func__); + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + for (ii = 0; ii < f54->report_size; ii += 2) { + pr_info("%03d: 0x%02x%02x\n", + ii / 2, + f54->report_data[ii + 1], + f54->report_data[ii]); + } + break; + default: + for (ii = 0; ii < f54->report_size; ii++) + pr_info("%03d: 0x%02x\n", ii, f54->report_data[ii]); + break; + } + + return; +} +#endif + +#ifdef HUMAN_READABLE +static void print_image_report(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned int ii; + unsigned int jj; + short *report_data; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + pr_info("%s: Report data (image)\n", __func__); + + report_data = (short *)f54->report_data; + + for (ii = 0; ii < f54->rmi4_data->num_of_tx; ii++) { + for (jj = 0; jj < f54->rmi4_data->num_of_rx; jj++) { + if (*report_data < -64) + pr_cont("."); + else if (*report_data < 0) + pr_cont("-"); + else if (*report_data > 64) + pr_cont("*"); + else if (*report_data > 0) + pr_cont("+"); + else + pr_cont("0"); + + report_data++; + } + pr_info(""); + } + pr_info("%s: End of report\n", __func__); + break; + default: + pr_info("%s: Image not supported for report type %d\n", + __func__, f54->report_type); + } + + return; +} +#endif + +static void free_control_mem(struct synaptics_rmi4_f54_handle *f54) +{ + struct f54_control *control = &f54->control; + + kfree(control->reg_0); + kfree(control->reg_1); + kfree(control->reg_2); + kfree(control->reg_3); + kfree(control->reg_4__6); + kfree(control->reg_7); + kfree(control->reg_8__9); + kfree(control->reg_10); + kfree(control->reg_11); + kfree(control->reg_12__13); + kfree(control->reg_14); + if (control->reg_15) + kfree(control->reg_15->data); + kfree(control->reg_15); + if (control->reg_16) + kfree(control->reg_16->data); + kfree(control->reg_16); + if (control->reg_17) + kfree(control->reg_17->data); + kfree(control->reg_17); + if (control->reg_18) + kfree(control->reg_18->data); + kfree(control->reg_18); + if (control->reg_19) + kfree(control->reg_19->data); + kfree(control->reg_19); + kfree(control->reg_20); + kfree(control->reg_21); + kfree(control->reg_22__26); + kfree(control->reg_27); + kfree(control->reg_28); + kfree(control->reg_29); + kfree(control->reg_30); + kfree(control->reg_31); + kfree(control->reg_32__35); + if (control->reg_36) + kfree(control->reg_36->data); + kfree(control->reg_36); + if (control->reg_37) + kfree(control->reg_37->data); + kfree(control->reg_37); + if (control->reg_38) + kfree(control->reg_38->data); + kfree(control->reg_38); + if (control->reg_39) + kfree(control->reg_39->data); + kfree(control->reg_39); + if (control->reg_40) + kfree(control->reg_40->data); + kfree(control->reg_40); + if (control->reg_89) + kfree(control->reg_89->data); + kfree(control->reg_89); + kfree(control->reg_93); + if (control->reg_95) + kfree(control->reg_95->data); + kfree(control->reg_95); + kfree(control->reg_99); + kfree(control->reg_107); + kfree(control->reg_113); + kfree(control->reg_116); + kfree(control->reg_137); + kfree(control->reg_145); + kfree(control->reg_146); + kfree(control->reg_242); + kfree(control->reg_269); + + return; +} + +static void free_data_mem(struct synaptics_rmi4_f54_handle *f54) +{ + struct f54_data *data = &f54->data; + + kfree(data->reg_4); + kfree(data->reg_6); + kfree(data->reg_7_0); + kfree(data->reg_7_1); + kfree(data->reg_8); + kfree(data->reg_9); + kfree(data->reg_10); + kfree(data->reg_14); + kfree(data->reg_16); + kfree(data->reg_17); +} + +static void remove_sysfs(struct synaptics_rmi4_f54_handle *f54) +{ + int reg_num; + + sysfs_remove_bin_file(&f54->f54_kdata->kobj, f54->f54_kdata->dev_report_data); + + sysfs_remove_group(&f54->f54_kdata->kobj, &attr_group); + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) + sysfs_remove_group(&f54->f54_kdata->kobj, &attrs_ctrl_regs[reg_num]); + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_data_regs); reg_num++) + sysfs_remove_group(&f54->f54_kdata->kobj, &attrs_data_regs[reg_num]); + + kobject_put(&f54->f54_kdata->kobj); + wait_for_completion(&f54->f54_kdata->kobj_unregistered); + kfree(f54->f54_kdata->dev_report_data); + kfree(f54->f54_kdata); + + return; +} + +static int synaptics_rmi4_f54_reset(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + rmi4_data->irq_enable(rmi4_data, false); + + retval = rmi4_data->reset_device(rmi4_data); + + rmi4_data->irq_enable(rmi4_data, true); + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + f54->status = STATUS_IDLE; + + mutex_unlock(&f54->status_mutex); + + return 0; +} + +static ssize_t synaptics_rmi4_f54_reset_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned int reset; + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_f54_reset(f54); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t synaptics_rmi4_f54_status_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", f54->status); +} + +static ssize_t synaptics_rmi4_f54_report_size_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size); +} + +static ssize_t synaptics_rmi4_f54_num_of_mapped_rx_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rmi4_data->num_of_rx); +} + +static ssize_t synaptics_rmi4_f54_num_of_mapped_tx_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rmi4_data->num_of_tx); +} + +static ssize_t synaptics_rmi4_f54_no_auto_cal_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal); +} + +static ssize_t synaptics_rmi4_f54_no_auto_cal_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting > 1) + return -EINVAL; + + retval = f54->fn_ptr->read(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read control register\n", + __func__); + return retval; + } + + if ((data & NO_AUTO_CAL_MASK) == setting) + return count; + + data = (data & ~NO_AUTO_CAL_MASK) | (data & NO_AUTO_CAL_MASK); + + retval = f54->fn_ptr->write(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write control register\n", + __func__); + return retval; + } + + f54->no_auto_cal = (setting == 1); + + return count; +} + +static ssize_t synaptics_rmi4_f54_report_type_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", f54->report_type); +} + +static int synaptics_rmi4_f54_report_type_set( + struct synaptics_rmi4_f54_handle *f54, + enum f54_report_types report_type) +{ + unsigned char data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + int retval, report_type_valid = is_report_type_valid(report_type); + + if (!report_type_valid) { + f54->report_type = INVALID_REPORT_TYPE; + dev_err(&rmi4_data->i2c_client->dev, + "%s: Requested report type unsupported\n", __func__); + return -EINVAL; + } + + f54->report_type = report_type; + pr_debug("requested report type = %d\n", report_type); + + mutex_lock(&f54->status_mutex); + f54->report_type = report_type; + data = (unsigned char)report_type; + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr, + &data, + sizeof(data)); + mutex_unlock(&f54->status_mutex); + + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write report type\n", __func__); + return 0; +} + +static ssize_t synaptics_rmi4_f54_report_type_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (f54->status != STATUS_BUSY) { + retval = synaptics_rmi4_f54_report_type_set(f54, + (enum f54_report_types)setting); + + if (retval < 0) + return retval; + + return count; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Previous get report still ongoing\n", + __func__); + return -EINVAL; + } +} + +static ssize_t synaptics_rmi4_f54_user_report_type1_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type1); +} + +static ssize_t synaptics_rmi4_f54_user_report_type2_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type2); +} + +static ssize_t synaptics_rmi4_f54_fifoindex_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read data registers\n", + __func__); + return retval; + } + + batohs(&f54->fifoindex, data); + + return snprintf(buf, PAGE_SIZE, "%u\n", f54->fifoindex); +} +static ssize_t synaptics_rmi4_f54_fifoindex_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char data[2]; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + f54->fifoindex = setting; + + hstoba(data, (unsigned short)setting); + + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write data registers\n", + __func__); + return retval; + } + + return count; +} + +#if defined(REPORT_STATUS_POLLING) +static int test_wait_for_command_completion( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + unsigned char value; + unsigned char timeout_count; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + timeout_count = 0; + do { + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + &value, + sizeof(value)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command register\n", + __func__); + return retval; + } + + if (value == 0x00) + break; + + msleep(100); + timeout_count++; + } while (timeout_count < STATUS_WORK_INTERVAL); + + if (timeout_count == STATUS_WORK_INTERVAL) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Timed out waiting for command completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} +#endif + +ssize_t send_get_report_command(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + unsigned char command = (unsigned char)COMMAND_GET_REPORT; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + mutex_lock(&f54->status_mutex); + + if (f54->status != STATUS_IDLE) { + if (f54->status != STATUS_BUSY) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Invalid status (%d)\n", + __func__, f54->status); + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Previous get report still ongoing\n", + __func__); + } + mutex_unlock(&f54->status_mutex); + return -EBUSY; + } + + __pm_stay_awake(&f54->test_wake_lock); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, true); +#endif + f54->status = STATUS_BUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + + /* unlock status mutex to allow jumping to error_exit label */ + mutex_unlock(&f54->status_mutex); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: sent get report command\n", __func__); + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write get report command\n", + __func__); + goto error_exit; + } + +#if defined(REPORT_STATUS_POLLING) + /* if report status control method is polling */ + pr_debug("will be polling test report status\n"); + retval = test_wait_for_command_completion(f54); + if (retval < 0) + goto error_exit; + + /* invoke status work immediately */ + schedule_delayed_work(&f54->status_work, 0); +#else +#ifdef WATCHDOG_HRTIMER + hrtimer_start(&f54->watchdog, + ktime_set(WATCHDOG_TIMEOUT_S, 0), + HRTIMER_MODE_REL); +#endif +#endif +out: + return retval; + +error_exit: + mutex_lock(&f54->status_mutex); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, false); +#endif + __pm_relax(&f54->test_wake_lock); + f54->status = retval; + mutex_unlock(&f54->status_mutex); + + goto out; +} + +static ssize_t synaptics_rmi4_f54_get_report_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + if (!is_report_type_valid(f54->report_type)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Invalid report type\n", + __func__); + return -EINVAL; + } + + retval = send_get_report_command(f54); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t synaptics_rmi4_f54_force_update_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[1]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command 0 register\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + (data[0] & COMMAND_FORCE_UPDATE) == COMMAND_FORCE_UPDATE); +} + +static ssize_t synaptics_rmi4_f54_force_update_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_FORCE_UPDATE; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write force update command\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f54_enter_in_cell_test_mode_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[1]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command 0 register\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + (data[0] & COMMAND_ENTER_IN_CELL_TESTMODE) == + COMMAND_ENTER_IN_CELL_TESTMODE); +} + + +static ssize_t synaptics_rmi4_f54_enter_in_cell_test_mode_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_ENTER_IN_CELL_TESTMODE; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write Enter In-Cell Test Mode command\n", + __func__); + return retval; + } + + return count; +} + + +static ssize_t synaptics_rmi4_f54_force_cal_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_FORCE_CAL; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write force cal command\n", + __func__); + return retval; + } + + return count; +} + +simple_show_func_unsigned(query, num_of_rx_electrodes) +simple_show_func_unsigned(query, num_of_tx_electrodes) +simple_show_func_unsigned(query, has_image16) +simple_show_func_unsigned(query, has_image8) +simple_show_func_unsigned(query, has_baseline) +simple_show_func_unsigned(query, clock_rate) +simple_show_func_unsigned(query, touch_controller_family) +simple_show_func_unsigned(query, has_pixel_touch_threshold_adjustment) +simple_show_func_unsigned(query, has_sensor_assignment) +simple_show_func_unsigned(query, has_interference_metric) +simple_show_func_unsigned(query, has_sense_frequency_control) +simple_show_func_unsigned(query, has_firmware_noise_mitigation) +simple_show_func_unsigned(query, has_two_byte_report_rate) +simple_show_func_unsigned(query, has_one_byte_report_rate) +simple_show_func_unsigned(query, has_relaxation_control) +simple_show_func_unsigned(query, curve_compensation_mode) +simple_show_func_unsigned(query, has_iir_filter) +simple_show_func_unsigned(query, has_cmn_removal) +simple_show_func_unsigned(query, has_cmn_maximum) +simple_show_func_unsigned(query, has_touch_hysteresis) +simple_show_func_unsigned(query, has_edge_compensation) +simple_show_func_unsigned(query, has_per_frequency_noise_control) +simple_show_func_unsigned(query, has_enhanced_stretch) +simple_show_func_unsigned(query, has_force_fast_relaxation) +simple_show_func_unsigned(query, has_multi_metric_state_machine) +simple_show_func_unsigned(query, has_signal_clarity) +simple_show_func_unsigned(query, has_variance_metric) +simple_show_func_unsigned(query, has_0d_relaxation_control) +simple_show_func_unsigned(query, has_0d_acquisition_control) +simple_show_func_unsigned(query, has_status) +simple_show_func_unsigned(query, has_slew_metric) +simple_show_func_unsigned(query, has_h_blank) +simple_show_func_unsigned(query, has_v_blank) +simple_show_func_unsigned(query, has_long_h_blank) +simple_show_func_unsigned(query, has_startup_fast_relaxation) +simple_show_func_unsigned(query, has_esd_control) +simple_show_func_unsigned(query, has_noise_mitigation2) +simple_show_func_unsigned(query, has_noise_state) +simple_show_func_unsigned(query, has_energy_ratio_relaxation) +simple_show_func_unsigned(query12, number_of_sensing_frequencies) +simple_show_func_unsigned(query16, has_data17) +simple_show_func_unsigned(query27, has_ctrl113) +simple_show_func_unsigned(query36, has_ctrl145) +simple_show_func_unsigned(query36, has_ctrl146) +simple_show_func_unsigned(query17, q17_num_of_sense_freqs) +show_store_func_unsigned(data, reg_4, d4_inhibit_freq_shift) +show_store_func_unsigned(data, reg_4, d4_baseline_sel) +show_store_func_unsigned(data, reg_4, d4_sense_freq_sel) +show_store_func_unsigned(data, reg_16, d16_freq_scan_im_lsb) +show_store_func_unsigned(data, reg_16, d16_freq_scan_im_msb) +show_store_func_unsigned(data, reg_17, d17_inhibit_freq_shift) +show_store_func_unsigned(data, reg_17, d17_freq) +show_func_unsigned(data, reg_6, d6_interference_metric_lsb) +show_func_unsigned(data, reg_6, d6_interference_metric_msb) +show_func_unsigned(data, reg_7_0, d7_current_report_rate_lsb) +show_func_unsigned(data, reg_7_1, d7_current_report_rate_msb) +show_func_unsigned(data, reg_8, d8_variance_metric_lsb) +show_func_unsigned(data, reg_8, d8_variance_metric_msb) +show_func_unsigned(data, reg_9, d9_averaged_im_lsb) +show_func_unsigned(data, reg_9, d9_averaged_im_msb) +show_func_unsigned(data, reg_10, d10_noise_state) +show_func_unsigned(data, reg_14, d14_cid_im_lsb) +show_func_unsigned(data, reg_14, d14_cid_im_msb) + +show_store_func_unsigned(control, reg_0, no_relax) +show_store_func_unsigned(control, reg_0, no_scan) +show_store_func_unsigned(control, reg_1, bursts_per_cluster) +show_store_func_unsigned(control, reg_2, saturation_cap) +show_store_func_unsigned(control, reg_3, pixel_touch_threshold) +show_store_func_unsigned(control, reg_4__6, rx_feedback_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_feedback_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_polarity) +show_store_func_unsigned(control, reg_4__6, high_ref_cap) +show_store_func_unsigned(control, reg_4__6, high_ref_feedback_cap) +show_store_func_unsigned(control, reg_4__6, high_ref_polarity) +show_store_func_unsigned(control, reg_7, cbc_cap) +show_store_func_unsigned(control, reg_7, cbc_polarity) +show_store_func_unsigned(control, reg_7, cbc_tx_carrier_selection) +show_store_func_unsigned(control, reg_8__9, integration_duration) +show_store_func_unsigned(control, reg_8__9, reset_duration) +show_store_func_unsigned(control, reg_10, noise_sensing_bursts_per_image) +show_store_func_unsigned(control, reg_12__13, slow_relaxation_rate) +show_store_func_unsigned(control, reg_12__13, fast_relaxation_rate) +show_store_func_unsigned(control, reg_14, rxs_on_xaxis) +show_store_func_unsigned(control, reg_14, curve_comp_on_txs) +show_store_func_unsigned(control, reg_20, disable_noise_mitigation) +show_store_func_unsigned(control, reg_21, freq_shift_noise_threshold) +show_store_func_unsigned(control, reg_22__26, medium_noise_threshold) +show_store_func_unsigned(control, reg_22__26, high_noise_threshold) +show_store_func_unsigned(control, reg_22__26, noise_density) +show_store_func_unsigned(control, reg_22__26, frame_count) +show_store_func_unsigned(control, reg_27, iir_filter_coef) +show_store_func_unsigned(control, reg_28, quiet_threshold) +show_store_func_unsigned(control, reg_29, cmn_filter_disable) +show_store_func_unsigned(control, reg_30, cmn_filter_max) +show_store_func_unsigned(control, reg_31, touch_hysteresis) +show_store_func_unsigned(control, reg_32__35, rx_low_edge_comp) +show_store_func_unsigned(control, reg_32__35, rx_high_edge_comp) +show_store_func_unsigned(control, reg_32__35, tx_low_edge_comp) +show_store_func_unsigned(control, reg_32__35, tx_high_edge_comp) + +show_replicated_func_unsigned(control, reg_15, sensor_rx_assignment) +show_replicated_func_unsigned(control, reg_16, sensor_tx_assignment) +show_replicated_func_unsigned(control, reg_17, disable) +show_replicated_func_unsigned(control, reg_17, filter_bandwidth) +show_replicated_func_unsigned(control, reg_19, stretch_duration) +show_replicated_func_unsigned(control, reg_38, noise_control_1) +show_replicated_func_unsigned(control, reg_39, noise_control_2) +show_replicated_func_unsigned(control, reg_40, noise_control_3) + +show_store_replicated_func_unsigned(control, reg_36, axis1_comp) +show_store_replicated_func_unsigned(control, reg_37, axis2_comp) + +show_store_subpkt_func_unsigned(control, reg_89, 0, c89_cid_sel_opt) +show_store_subpkt_func_unsigned(control, reg_89, 0, c89_cid_voltage_sel) +show_store_subpkt_func_unsigned(control, reg_89, 0, + c89_cid_im_noise_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 0, + c89_cid_im_noise_threshold_msb) + +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_fnm_pixel_touch_mult) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_freq_scan_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_freq_scan_threshold_msb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_quiet_im_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_quiet_im_threshold_msb) + +show_store_subpkt_func_unsigned(control, reg_89, 2, c89_rail_sel_opt); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_noise_threshold_lsb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_noise_threshold_msb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_quiet_threshold_lsb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_quiet_threshold_msb); + +show_store_func_unsigned(control, reg_93, c93_freq_shift_noise_threshold_lsb) +show_store_func_unsigned(control, reg_93, c93_freq_shift_noise_threshold_msb) + +show_store_replicated_func_unsigned(control, reg_95, c95_disable) +show_store_replicated_func_unsigned(control, reg_95, c95_filter_bw) +show_store_replicated_func_unsigned(control, reg_95, c95_first_burst_length_lsb) +show_store_replicated_func_unsigned(control, reg_95, c95_first_burst_length_msb) +show_store_replicated_func_unsigned(control, reg_95, c95_addl_burst_length_lsb) +show_store_replicated_func_unsigned(control, reg_95, c95_addl_burst_length_msb) +show_store_replicated_func_unsigned(control, reg_95, c95_i_stretch) +show_store_replicated_func_unsigned(control, reg_95, c95_r_stretch) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control1) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control2) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control3) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control4) + +show_store_func_unsigned(control, reg_99, c99_int_dur_lsb) +show_store_func_unsigned(control, reg_99, c99_int_dur_msb) +show_store_func_unsigned(control, reg_99, c99_reset_dur) + +show_store_func_unsigned(control, reg_107, c107_abs_int_dur) +show_store_func_unsigned(control, reg_107, c107_abs_reset_dur) +show_store_func_unsigned(control, reg_107, c107_abs_filter_bw) +show_store_func_unsigned(control, reg_107, c107_abs_rstretch) +show_store_func_unsigned(control, reg_107, c107_abs_burst_count_1) +show_store_func_unsigned(control, reg_107, c107_abs_burst_count_2) +show_store_func_unsigned(control, reg_107, c107_abs_stretch_dur) +show_store_func_unsigned(control, reg_107, c107_abs_adc_clock_div) +show_store_func_unsigned(control, reg_107, c107_abs_sub_burtst_size) +show_store_func_unsigned(control, reg_107, c107_abs_trigger_delay) + +show_store_func_unsigned(control, reg_113, c113_en_hybrid_on_tx) +show_store_func_unsigned(control, reg_113, c113_en_hybrid_on_rx) +show_store_func_unsigned(control, reg_113, c113_tx_consistency_chk) +show_store_func_unsigned(control, reg_113, c113_rx_consistency_chk) +show_store_func_unsigned(control, reg_113, c113_en_gradient_baseline) +show_store_func_unsigned(control, reg_113, c113_consistency_thresh) +show_store_func_unsigned(control, reg_113, c113_tx_obj_thresh) +show_store_func_unsigned(control, reg_113, c113_rx_obj_thresh) +show_store_func_unsigned(control, reg_113, c113_tx_thermal_update_int_lsb) +show_store_func_unsigned(control, reg_113, c113_tx_thermal_update_int_msb) +show_store_func_unsigned(control, reg_113, c113_rx_thermal_update_int_lsb) +show_store_func_unsigned(control, reg_113, c113_rx_thermal_update_int_msb) +show_store_func_unsigned(control, reg_113, c113_tx_negative_thresh) +show_store_func_unsigned(control, reg_113, c113_rx_negative_thresh) + +show_store_func_unsigned(control, reg_116, c116_im_thresh) +show_store_func_unsigned(control, reg_116, c116_debounce) +show_store_func_unsigned(control, reg_116, c116_quiet_im) +show_store_func_unsigned(control, reg_116, c116_filter) + +show_store_func_unsigned(control, reg_137, c137_cmnr_adjust) + +show_store_func_unsigned(control, reg_145, c145_bursts_per_cluster_rx) +show_store_func_unsigned(control, reg_145, c145_bursts_per_cluster_tx) + +show_store_func_unsigned(control, reg_146, c146_int_dur_lsb) +show_store_func_unsigned(control, reg_146, c146_int_dur_msb) +show_store_func_unsigned(control, reg_146, c146_reset_dur) +show_store_func_unsigned(control, reg_146, c146_filter_bandwidth) +show_store_func_unsigned(control, reg_146, c146_stretch_dur) +show_store_func_unsigned(control, reg_146, c146_burst_count1) +show_store_func_unsigned(control, reg_146, c146_burst_count2) +show_store_func_unsigned(control, reg_146, c146_stretch_dur2) +show_store_func_unsigned(control, reg_146, c146_adc_clock_div) + +show_store_func_unsigned(control, reg_242, c242_cbc_servo_disable) +show_store_func_unsigned(control, reg_269, c269_bsc_calibration_en) + +simple_show_func_unsigned(query_f55_0_2, f55_q2_has_single_layer_multitouch) +show_func_unsigned(control, reg_0_f55, f55_c0_receivers_on_x) +show_func_unsigned(control, reg_8_f55, f55_c8_pattern_type) + +static ssize_t synaptics_rmi4_f54_burst_count_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + int size = 0; + unsigned char ii; + unsigned char *temp; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->control_mutex); + + retval = f54->fn_ptr->read(rmi4_data, + f54->control.reg_17->address, + (unsigned char *)f54->control.reg_17->data, + f54->control.reg_17->length); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read control reg_17\n", + __func__); + } + + retval = f54->fn_ptr->read(rmi4_data, + f54->control.reg_18->address, + (unsigned char *)f54->control.reg_18->data, + f54->control.reg_18->length); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read control reg_18\n", + __func__); + } + + mutex_unlock(&f54->control_mutex); + + temp = buf; + + for (ii = 0; ii < f54->control.reg_17->length; ii++) { + retval = snprintf(temp, PAGE_SIZE - size, "%u ", (1 << 8) * + f54->control.reg_17->data[ii].burst_count_b8__10 + + f54->control.reg_18->data[ii].burst_count_b0__7); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Faild to write output\n", + __func__); + return retval; + } + size += retval; + temp += retval; + } + + retval = snprintf(temp, PAGE_SIZE - size, "\n"); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Faild to write null terminator\n", + __func__); + return retval; + } + + return size + retval; +} + +static ssize_t synaptics_rmi4_f54_user_get_report_show( + struct synaptics_rmi4_f54_handle *f54, + char *buf, enum f54_report_types report_type) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + if (f54->status != STATUS_IDLE) { + if (f54->status == STATUS_BUSY) + dev_err(&rmi4_data->i2c_client->dev, + "%s: WARNING: Resetting in busy state\n", + __func__); + + retval = synaptics_rmi4_f54_reset(f54); + if (retval < 0) + goto out; + } + + retval = synaptics_rmi4_f54_report_type_set(f54, report_type); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set report type\n", __func__); + goto out; + } + + retval = send_get_report_command(f54); + +out: + retval = scnprintf(buf, PAGE_SIZE, "%d\n", retval); + return retval; +} + +static ssize_t synaptics_rmi4_f54_user_get_report1_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return synaptics_rmi4_f54_user_get_report_show(f54, + buf, f54->user_report_type1); +} + +static ssize_t synaptics_rmi4_f54_user_get_report2_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return synaptics_rmi4_f54_user_get_report_show(f54, + buf, f54->user_report_type2); +} + +static ssize_t synaptics_rmi4_f54_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct synaptics_rmi4_f54_handle *f54 = attributes->private; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->data_mutex); + + if (!f54->report_data || !f54->report_size) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report type %d data not available\n", + __func__, f54->report_type); + mutex_unlock(&f54->data_mutex); + return -EINVAL; + } + if (pos > f54->report_size || pos < 0) { + mutex_unlock(&f54->data_mutex); + return 0; + } + + count = min_t(loff_t, count, f54->report_size - pos); + memcpy(buf, f54->report_data + pos, count); + mutex_unlock(&f54->data_mutex); + pr_debug("copied %zu bytes to the caller's buffer\n", count); + return count; +} + +static int synaptics_rmi4_f54_set_sysfs(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + int reg_num; + int dreg_num; + struct bin_attribute *dev_report_data; + struct f54_kobj_data *f54_kobj_data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + f54_kobj_data = kzalloc(sizeof(*f54_kobj_data), GFP_KERNEL); + if (!f54_kobj_data) + goto exit; + + dev_report_data = kzalloc(sizeof(struct bin_attribute), GFP_KERNEL); + if (!dev_report_data) + goto exit_1; + + dev_report_data->attr.name = "report_data"; + dev_report_data->attr.mode = S_IRUGO; + dev_report_data->read = synaptics_rmi4_f54_data_read; + dev_report_data->private = (void *)f54; + + f54_kobj_data->f54 = f54; + f54_kobj_data->dev_report_data = dev_report_data; + + init_completion(&f54_kobj_data->kobj_unregistered); + f54->f54_kdata = f54_kobj_data; + + retval = kobject_init_and_add(&f54_kobj_data->kobj, &f54_ktype, + &rmi4_data->i2c_client->dev.kobj, "f54"); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs directory\n", + __func__); + goto exit_1; + } + + retval = sysfs_create_bin_file(&f54_kobj_data->kobj, dev_report_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_2; + } + + retval = sysfs_create_group(&f54_kobj_data->kobj, &attr_group); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_3; + } + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) { + if (attrs_ctrl_regs_exist[reg_num]) { + retval = sysfs_create_group(&f54_kobj_data->kobj, + &attrs_ctrl_regs[reg_num]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_4; + } + } + } + + for (dreg_num = 0; dreg_num < ARRAY_SIZE(attrs_data_regs); dreg_num++) { + if (attrs_data_regs_exist[dreg_num]) { + retval = sysfs_create_group(&f54_kobj_data->kobj, + &attrs_data_regs[dreg_num]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes for data register %d\n", + __func__, dreg_num); + goto exit_5; + } + } + } + + return 0; + +exit_5: + for (dreg_num--; dreg_num >= 0; dreg_num--) + sysfs_remove_group(&f54_kobj_data->kobj, &attrs_data_regs[dreg_num]); + +exit_4: + for (reg_num--; reg_num >= 0; reg_num--) + sysfs_remove_group(&f54_kobj_data->kobj, &attrs_ctrl_regs[reg_num]); + + sysfs_remove_group(&f54_kobj_data->kobj, &attr_group); + +exit_3: + sysfs_remove_bin_file(&f54_kobj_data->kobj, dev_report_data); + +exit_2: + kobject_put(&f54_kobj_data->kobj); + +exit_1: + kfree(dev_report_data); + kfree(f54->f54_kdata); +exit: + return -ENODEV; +} + +/* + * Fill in base register address and offset of F54 + * control registers to allow run time patching + */ +static int synaptics_rmi4_scan_f54_ctrl_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_ctrl_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + int ii, error = f54_ctrl_regs->nr_regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + + f54_ctrl_regs->base_addr = f54->control_base_addr; + for (ii = 0; ii < f54_ctrl_regs->nr_regs; ii++) { + reg = &f54_ctrl_regs->regs[ii]; + subpkt = ®->subpkt[0]; + if (reg->r_number == 0 && f54->control.reg_0) { + data = kzalloc(sizeof(f54->control.reg_0->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_0->address - + f54->control_base_addr; + reg->size = sizeof(f54->control.reg_0->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 2 && f54->control.reg_2) { + data = kzalloc(sizeof(f54->control.reg_2->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_2->address - + f54->control_base_addr; + reg->size = 2; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 95 && f54->control.reg_95) { + int jj, num_of_subpkts; + data = kzalloc(f54->control.reg_95->length, GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_95->address - + f54->control_base_addr; + reg->size = f54->control.reg_95->length; + reg->data = data; + /* not going over the number of predefined subpackets */ + num_of_subpkts = min((int)reg->nr_subpkts, + (int)(f54->control.reg_95->length / + subpkt->size)); + for (jj = 0; jj < num_of_subpkts; jj++) { + subpkt = ®->subpkt[jj]; + subpkt->present = true; + subpkt->offset = jj * subpkt->size; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: subpkt[%d], off = %d, sz = %d\n", __func__, + jj, subpkt->offset, subpkt->size); + } + error--; + } + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->control_base_addr+reg->offset, + reg->offset, reg->size); + } + dev_dbg(&rmi4_data->i2c_client->dev, "%s: rc = %d\n", __func__, error); + return error; +} + +/* + * Fill in base register address and offset of F54 + * command register to allow run time patching + */ +static int synaptics_rmi4_scan_f54_cmd_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_cmd_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg = f54_cmd_regs->regs; + struct synaptics_rmi4_subpkt *subpkt = ®->subpkt[0]; + + f54_cmd_regs->base_addr = f54->command_base_addr; + data = kzalloc(1, GFP_KERNEL); + if (!data) + return -ENOMEM; + reg->offset = 0; + reg->size = 1; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->command_base_addr+reg->offset, + reg->offset, reg->size); + return 0; +} + +/* + * Fill in base register address and offset of F54 query + * register 12 to allow run time access + */ +static int synaptics_rmi4_scan_f54_query_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_query_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + struct synaptics_rmi4_packet_reg *reg = f54_query_regs->regs; + struct synaptics_rmi4_subpkt *subpkt = ®->subpkt[0]; + unsigned char *data = kzalloc(sizeof(f54->query12.data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + f54_query_regs->base_addr = f54->query_base_addr; + /* need an offset off of base address here */ + reg->offset = 12; + reg->size = sizeof(f54->query12.data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->query_base_addr+reg->offset, + reg->offset, reg->size); + return 0; +} + +/* + * Fill in base register address and offset of F54 data + * registers 10 & 17 to allow run time access + */ +static int synaptics_rmi4_scan_f54_data_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_data_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + int ii, error = f54_data_regs->nr_regs; + unsigned char *data; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_rmi4_packet_reg *reg; + + f54_data_regs->base_addr = f54->data_base_addr; + for (ii = 0; ii < f54_data_regs->nr_regs; ii++) { + reg = &f54_data_regs->regs[ii]; + if (reg->r_number == 6 && f54->data.reg_6) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_6->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_6->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_6->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 10 && f54->data.reg_10) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_10->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_10->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_10->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 14 && f54->data.reg_14) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_14->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_14->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_14->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 16 && f54->data.reg_16) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_16->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_16->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_16->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 17 && f54->data.reg_17) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_17->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_17->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_17->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->data_base_addr+reg->offset, + reg->offset, reg->size); + } + dev_dbg(&rmi4_data->i2c_client->dev, "%s: rc = %d\n", __func__, error); + return error ? -ENOSYS : error; +} + +static int synaptics_rmi4_f54_set_ctrl( + struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char length; + unsigned char reg_num = 0; + unsigned short reg_addr = f54->control_base_addr; + struct f54_control *control = &f54->control; + struct f54_query *query = &f54->query; + struct f54_query12 *query12 = &f54->query12; + struct f54_query13 *query13 = &f54->query13; + struct f54_query15 *query15 = &f54->query15; + struct f54_query16 *query16 = &f54->query16; + struct f54_query17 *query17 = &f54->query17; + struct f54_query21 *query21 = &f54->query21; + struct f54_query22 *query22 = &f54->query22; + struct f54_query23 *query23 = &f54->query23; + struct f54_query25 *query25 = &f54->query25; + struct f54_query27 *query27 = &f54->query27; + struct f54_query29 *query29 = &f54->query29; + struct f54_query30 *query30 = &f54->query30; + struct f54_query32 *query32 = &f54->query32; + struct f54_query33 *query33 = &f54->query33; + struct f54_query35 *query35 = &f54->query35; + struct f54_query36 *query36 = &f54->query36; + struct f54_query38 *query38 = &f54->query38; + struct f54_query39 *query39 = &f54->query39; + struct f54_query40 *query40 = &f54->query40; +// struct f54_query41 *query41 = &f54->query41; +// struct f54_query42 *query42 = &f54->query42; + struct f54_query43 *query43 = &f54->query43; + struct f54_query46 *query46 = &f54->query46; + /* BSC control access */ + struct f54_query47 *query47 = &f54->query47; + struct f54_query49 *query49 = &f54->query49; + struct f54_query50 *query50 = &f54->query50; + struct f54_query51 *query51 = &f54->query51; + struct f54_query55 *query55 = &f54->query55; + struct f54_query57 *query57 = &f54->query57; + struct f54_query58 *query58 = &f54->query58; + struct f54_query61 *query61 = &f54->query61; + struct f54_query64 *query64 = &f54->query64; + struct f54_query65 *query65 = &f54->query65; + struct f54_query67 *query67 = &f54->query67; + struct f54_query68 *query68 = &f54->query68; + struct f54_query69 *query69 = &f54->query69; + struct f54_query70 *query70 = &f54->query70; +// struct f54_query71 *query71 = &f54->query71; +// struct f54_query72 *query72 = &f54->query72; +// struct f54_query73 *query73 = &f54->query73; +// struct f54_query74 *query74 = &f54->query74; + struct f54_query75 *query75 = &f54->query75; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + unsigned char num_of_sensing_freqs; + + num_of_sensing_freqs = f54->query12.number_of_sensing_frequencies; + + CTRL_REG_ADD(0, 1, true); + CTRL_REG_ADD(1, 1, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(2, 2, true); + CTRL_REG_ADD(3, 1, (f54->query.has_pixel_touch_threshold_adjustment == 1)); + CTRL_REG_ADD(4__6, 3, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(7, 1, (f54->query.touch_controller_family == 1)); + CTRL_REG_ADD(8__9, 3, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(10, 1, f54->query.has_interference_metric); + CTRL_REG_ADD(11, 2, f54->query.has_ctrl11); + CTRL_REG_ADD(12__13, 2, f54->query.has_relaxation_control); + + /* controls 14 15 16 */ + if (f54->query.has_sensor_assignment == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + control->reg_14 = kzalloc(sizeof(*(control->reg_14)), + GFP_KERNEL); + if (!control->reg_14) + goto exit_no_mem; + control->reg_14->address = reg_addr; + reg_addr += sizeof(control->reg_14->data); + + control->reg_15 = kzalloc(sizeof(*(control->reg_15)), + GFP_KERNEL); + if (!control->reg_15) + goto exit_no_mem; + control->reg_15->length = f54->query.num_of_rx_electrodes; + control->reg_15->data = kzalloc(control->reg_15->length * + sizeof(*(control->reg_15->data)), GFP_KERNEL); + if (!control->reg_15->data) + goto exit_no_mem; + control->reg_15->address = reg_addr; + reg_addr += control->reg_15->length; + + control->reg_16 = kzalloc(sizeof(*(control->reg_16)), + GFP_KERNEL); + if (!control->reg_16) + goto exit_no_mem; + control->reg_16->length = f54->query.num_of_tx_electrodes; + control->reg_16->data = kzalloc(control->reg_16->length * + sizeof(*(control->reg_16->data)), GFP_KERNEL); + if (!control->reg_16->data) + goto exit_no_mem; + control->reg_16->address = reg_addr; + reg_addr += control->reg_16->length; + } + reg_num++; + + /* controls 17 18 19 */ + if (f54->query.has_sense_frequency_control == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + control->reg_17 = kzalloc(sizeof(*(control->reg_17)), + GFP_KERNEL); + if (!control->reg_17) + goto exit_no_mem; + control->reg_17->length = num_of_sensing_freqs; + control->reg_17->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_17->data)), GFP_KERNEL); + if (!control->reg_17->data) + goto exit_no_mem; + control->reg_17->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_18 = kzalloc(sizeof(*(control->reg_18)), + GFP_KERNEL); + if (!control->reg_18) + goto exit_no_mem; + control->reg_18->length = num_of_sensing_freqs; + control->reg_18->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_18->data)), GFP_KERNEL); + if (!control->reg_18->data) + goto exit_no_mem; + control->reg_18->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_19 = kzalloc(sizeof(*(control->reg_19)), + GFP_KERNEL); + if (!control->reg_19) + goto exit_no_mem; + control->reg_19->length = num_of_sensing_freqs; + control->reg_19->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_19->data)), GFP_KERNEL); + if (!control->reg_19->data) + goto exit_no_mem; + control->reg_19->address = reg_addr; + reg_addr += num_of_sensing_freqs; + } + reg_num++; + + CTRL_REG_ADD(20, 1, true); + CTRL_REG_ADD(21, 2, f54->query.has_sense_frequency_control); + CTRL_REG_ADD(22__26, 7, f54->query.has_firmware_noise_mitigation); + CTRL_REG_ADD(27, 1, f54->query.has_iir_filter); + CTRL_REG_ADD(28, 2, f54->query.has_firmware_noise_mitigation); + CTRL_REG_ADD(29, 1, f54->query.has_cmn_removal); + CTRL_REG_ADD(30, 1, f54->query.has_cmn_maximum); + CTRL_REG_ADD(31, 1, f54->query.has_touch_hysteresis); + CTRL_REG_ADD(32__35, 8, f54->query.has_edge_compensation); + + /* control 36 */ + if ((f54->query.curve_compensation_mode == 1) || + (f54->query.curve_compensation_mode == 2)) { + attrs_ctrl_regs_exist[reg_num] = true; + + if (f54->query.curve_compensation_mode == 1) { + length = max(f54->query.num_of_rx_electrodes, + f54->query.num_of_tx_electrodes); + } else if (f54->query.curve_compensation_mode == 2) { + length = f54->query.num_of_rx_electrodes; + } + + control->reg_36 = kzalloc(sizeof(*(control->reg_36)), + GFP_KERNEL); + if (!control->reg_36) + goto exit_no_mem; + control->reg_36->length = length; + control->reg_36->data = kzalloc(length * + sizeof(*(control->reg_36->data)), GFP_KERNEL); + if (!control->reg_36->data) + goto exit_no_mem; + control->reg_36->address = reg_addr; + reg_addr += length; + } + reg_num++; + + /* control 37 */ + if (f54->query.curve_compensation_mode == 2) { + attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_37 = kzalloc(sizeof(*(control->reg_37)), + GFP_KERNEL); + if (!control->reg_37) + goto exit_no_mem; + control->reg_37->length = f54->query.num_of_tx_electrodes; + control->reg_37->data = kzalloc(control->reg_37->length * + sizeof(*(control->reg_37->data)), GFP_KERNEL); + if (!control->reg_37->data) + goto exit_no_mem; + + control->reg_37->address = reg_addr; + reg_addr += control->reg_37->length; + } + reg_num++; + + /* controls 38 39 40 */ + if (f54->query.has_per_frequency_noise_control == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_38 = kzalloc(sizeof(*(control->reg_38)), + GFP_KERNEL); + if (!control->reg_38) + goto exit_no_mem; + control->reg_38->length = num_of_sensing_freqs; + + control->reg_38->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_38->data)), GFP_KERNEL); + if (!control->reg_38->data) + goto exit_no_mem; + control->reg_38->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_39 = kzalloc(sizeof(*(control->reg_39)), + GFP_KERNEL); + if (!control->reg_39) + goto exit_no_mem; + control->reg_39->length = num_of_sensing_freqs; + + control->reg_39->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_39->data)), GFP_KERNEL); + if (!control->reg_39->data) + goto exit_no_mem; + control->reg_39->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_40 = kzalloc(sizeof(*(control->reg_40)), + GFP_KERNEL); + if (!control->reg_40) + goto exit_no_mem; + control->reg_40->length = num_of_sensing_freqs; + + control->reg_40->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_40->data)), GFP_KERNEL); + if (!control->reg_40->data) + goto exit_no_mem; + control->reg_40->address = reg_addr; + reg_addr += num_of_sensing_freqs; + } + reg_num++; + + CTRL_REG_PRESENCE(41, 1, query->has_signal_clarity); + CTRL_REG_PRESENCE(42, 2, query->has_variance_metric); + CTRL_REG_PRESENCE(43, 2, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(44, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(45, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(46, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(47, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(48, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(49, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(50, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(51, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(52, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(53, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(53, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(54, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(55, 1, query->has_0d_relaxation_control); + CTRL_REG_PRESENCE(56, 1, query->has_0d_relaxation_control); + CTRL_REG_PRESENCE(57, 1, query->has_0d_acquisition_control); + CTRL_REG_PRESENCE(58, 1, query->has_0d_acquisition_control); + CTRL_REG_PRESENCE(59, 2, query->has_h_blank); + CTRL_REG_PRESENCE(60, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(61, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(62, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(63, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank || + query->has_slew_metric || + query->has_slew_option || + query->has_noise_mitigation2); + + if (query->has_h_blank) { + CTRL_REG_PRESENCE(64, 7, 1); + CTRL_REG_PRESENCE(65, 7, 1); + } else if (query->has_v_blank || query->has_long_h_blank) { + CTRL_REG_PRESENCE(64, 1, 1); + CTRL_REG_PRESENCE(65, 1, 1); + } + + if (query->has_h_blank || query->has_v_blank || + query->has_long_h_blank) { + CTRL_REG_PRESENCE(66, 1, 1); + CTRL_REG_PRESENCE(67, 1, 1); + CTRL_REG_PRESENCE(68, 1, 1); + CTRL_REG_PRESENCE(69, 1, 1); + CTRL_REG_PRESENCE(70, 1, 1); + CTRL_REG_PRESENCE(71, 1, 1); + CTRL_REG_PRESENCE(72, 2, 1); + CTRL_REG_PRESENCE(73, 2, 1); + } + CTRL_REG_PRESENCE(74, 2, query->has_slew_metric); + CTRL_REG_PRESENCE(75, query12->number_of_sensing_frequencies, + query->has_enhanced_stretch && + query->has_sense_frequency_control); + CTRL_REG_PRESENCE(76, 1, query->has_startup_fast_relaxation); + CTRL_REG_PRESENCE(77, 1, query->has_esd_control); + CTRL_REG_PRESENCE(78, 1, query->has_esd_control); + CTRL_REG_PRESENCE(79, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(80, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(81, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(82, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(83, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(84, 1, query->has_energy_ratio_relaxation); + CTRL_REG_PRESENCE(85, 1, query->has_energy_ratio_relaxation); + CTRL_REG_PRESENCE(86, 1, query13->has_ctrl86); + CTRL_REG_PRESENCE(87, 1, query13->has_ctrl87); + CTRL_REG_PRESENCE(88, 1, query->has_ctrl88); + +/* + CTRL_REG_ADD(89, 1, query13->has_cid_im || + query13->has_noise_mitigation_enh || + query13->has_rail_im); +*/ + CTRL_PKT_REG_START(89, + CTRL_SUBPKT_SZ(89, 0) + + CTRL_SUBPKT_SZ(89, 1) + + CTRL_SUBPKT_SZ(89, 2)); + + CTRL_SUBPKT_ADD(89, 0, query13->has_cid_im); + CTRL_SUBPKT_ADD(89, 1, query13->has_noise_mitigation_enh); + CTRL_SUBPKT_ADD(89, 2, query13->has_rail_im); + CTRL_PKT_REG_END(89, 1); + + CTRL_REG_PRESENCE(90, 1, query15->has_ctrl90); + CTRL_REG_PRESENCE(91, 1, query21->has_ctrl91); + CTRL_REG_PRESENCE(92, 1, query16->has_ctrl92); + + CTRL_REG_ADD(93, 1, query16->has_ctrl93); + + CTRL_REG_PRESENCE(94, 1, query16->has_ctrl94_query18); + + CTRL_REG_ADD_EXT(95, 1, query16->has_ctrl95_query19, + sizeof(struct f54_control_95n) * + query17->q17_num_of_sense_freqs); + + CTRL_REG_PRESENCE(96, 1, query21->has_ctrl96); + CTRL_REG_PRESENCE(97, 1, query21->has_ctrl97); + CTRL_REG_PRESENCE(98, 1, query21->has_ctrl98); + + CTRL_REG_ADD(99, 1, (query16->has_ctrl99 || + ((f54->query.touch_controller_family == 2) || + (f54->query.touch_controller_family == 4)))); + + CTRL_REG_PRESENCE(100, 1, query16->has_ctrl100); + CTRL_REG_PRESENCE(101, 1, query22->has_ctrl101); + CTRL_REG_PRESENCE(102, 1, query23->has_ctrl102); + CTRL_REG_PRESENCE(103, 1, query22->has_ctrl103_query26); + CTRL_REG_PRESENCE(104, 1, query22->has_ctrl104); + CTRL_REG_PRESENCE(105, 1, query22->has_ctrl105); + CTRL_REG_PRESENCE(106, 1, query25->has_ctrl106); + + CTRL_REG_ADD(107, 1, query25->has_ctrl107); + + CTRL_REG_PRESENCE(108, 1, query25->has_ctrl108); + CTRL_REG_PRESENCE(109, 1, query25->has_ctrl109); + CTRL_REG_PRESENCE(110, 1, query27->has_ctrl110); + CTRL_REG_PRESENCE(111, 1, query27->has_ctrl111); + CTRL_REG_PRESENCE(112, 1, query27->has_ctrl112); + + CTRL_REG_ADD(113, 1, query27->has_ctrl113); + + CTRL_REG_PRESENCE(114, 1, query27->has_ctrl114); + CTRL_REG_PRESENCE(115, 1, query29->has_ctrl115); + + CTRL_REG_ADD(116, 1, query29->has_ctrl116); + + CTRL_REG_PRESENCE(117, 1, query29->has_ctrl117); + CTRL_REG_PRESENCE(118, 1, query30->has_ctrl118); + CTRL_REG_PRESENCE(119, 1, query30->has_ctrl119); + CTRL_REG_PRESENCE(120, 1, query30->has_ctrl120); + CTRL_REG_PRESENCE(121, 1, query30->has_ctrl121); + CTRL_REG_PRESENCE(122, 1, query30->has_ctrl122_query31); + CTRL_REG_PRESENCE(123, 1, query30->has_ctrl123); + CTRL_REG_PRESENCE(124, 1, query30->has_ctrl124); + CTRL_REG_PRESENCE(125, 1, query32->has_ctrl125); + CTRL_REG_PRESENCE(126, 1, query32->has_ctrl126); + CTRL_REG_PRESENCE(127, 1, query32->has_ctrl127); + CTRL_REG_PRESENCE(128, 1, query33->has_ctrl128); + CTRL_REG_PRESENCE(129, 1, query33->has_ctrl129); + CTRL_REG_PRESENCE(130, 1, query33->has_ctrl130); + CTRL_REG_PRESENCE(131, 1, query33->has_ctrl131); + CTRL_REG_PRESENCE(132, 1, query33->has_ctrl132); + CTRL_REG_PRESENCE(133, 1, query33->has_ctrl133); + CTRL_REG_PRESENCE(134, 1, query33->has_ctrl134); + CTRL_REG_PRESENCE(135, 1, query35->has_ctrl135); + CTRL_REG_PRESENCE(136, 1, query35->has_ctrl136); + + CTRL_REG_ADD(137, 1, query35->has_ctrl137); + + CTRL_REG_PRESENCE(138, 1, query35->has_ctrl138); + CTRL_REG_PRESENCE(139, 1, query35->has_ctrl139); + CTRL_REG_PRESENCE(140, 1, query35->has_ctrl140); + CTRL_REG_PRESENCE(141, 1, query36->has_ctrl141); + CTRL_REG_PRESENCE(142, 1, query36->has_ctrl142); + CTRL_REG_PRESENCE(143, 1, query36->has_ctrl143); + CTRL_REG_PRESENCE(144, 1, query36->has_ctrl144); + + CTRL_REG_ADD(145, 1, query36->has_ctrl145); + CTRL_REG_ADD(146, 1, query36->has_ctrl146); + + CTRL_REG_PRESENCE(147, 1, query38->has_ctrl147); + CTRL_REG_PRESENCE(148, 1, query38->has_ctrl148); + CTRL_REG_PRESENCE(149, 1, query38->has_ctrl149); + CTRL_REG_PRESENCE(150, 1, query38->has_ctrl150); + CTRL_REG_PRESENCE(151, 1, query38->has_ctrl151); + CTRL_REG_PRESENCE(152, 1, query38->has_ctrl152); + CTRL_REG_PRESENCE(153, 1, query38->has_ctrl153); + CTRL_REG_PRESENCE(154, 1, query39->has_ctrl154); + CTRL_REG_PRESENCE(155, 1, query39->has_ctrl155); + CTRL_REG_PRESENCE(156, 1, query39->has_ctrl156); + CTRL_REG_PRESENCE(157, 1, query39->has_ctrl157_158); + CTRL_REG_PRESENCE(158, 1, query39->has_ctrl157_158); +// CTRL_REG_RESERVED_PRESENCE(159, 1, 0); + CTRL_REG_PRESENCE(160, 1, query39->has_ctrl160); +// CTRL_REG_RESERVED_PRESENCE(161, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(162, 1, 0); + CTRL_REG_PRESENCE(163, 1, query40->has_ctrl163_query41); +// CTRL_REG_RESERVED_PRESENCE(164, 1, 0); + CTRL_REG_PRESENCE(165, 1, query40->has_ctrl165_query42); + CTRL_REG_PRESENCE(166, 1, query40->has_ctrl166); + CTRL_REG_PRESENCE(167, 1, query40->has_ctrl167); + CTRL_REG_PRESENCE(168, 1, query40->has_ctrl168); + CTRL_REG_PRESENCE(169, 1, query40->has_ctrl169); +// CTRL_REG_RESERVED_PRESENCE(170, 1, 0); + CTRL_REG_PRESENCE(171, 1, query43->has_ctrl171); + CTRL_REG_PRESENCE(172, 1, query43->has_ctrl172_query44_query45); + CTRL_REG_PRESENCE(173, 1, query43->has_ctrl173); + CTRL_REG_PRESENCE(174, 1, query43->has_ctrl174); + CTRL_REG_PRESENCE(175, 1, query43->has_ctrl175); + CTRL_REG_PRESENCE(176, 1, query46->has_ctrl176); + CTRL_REG_PRESENCE(177, 1, query46->has_ctrl177_178); + CTRL_REG_PRESENCE(178, 1, query46->has_ctrl177_178); + CTRL_REG_PRESENCE(179, 1, query46->has_ctrl179); + /* BSC control access */ +// CTRL_REG_RESERVED_PRESENCE(180, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(181, 1, 0); + CTRL_REG_PRESENCE(182, 1, query47->has_ctrl182); + CTRL_REG_PRESENCE(183, 1, query47->has_ctrl183); +// CTRL_REG_RESERVED_PRESENCE(184, 1, 0); + CTRL_REG_PRESENCE(185, 1, query47->has_ctrl185); + CTRL_REG_PRESENCE(186, 1, query47->has_ctrl186); + CTRL_REG_PRESENCE(187, 1, query47->has_ctrl187); + CTRL_REG_PRESENCE(188, 1, query49->has_ctrl188); +// CTRL_REG_RESERVED_PRESENCE(189, 1, 0); + CTRL_REG_PRESENCE(190, 1, query49->has_ctrl190); +// CTRL_REG_RESERVED_PRESENCE(191, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(192, 1, 0); + + CTRL_REG_PRESENCE(193, 1, query50->has_ctrl193); +// CTRL_REG_RESERVED_PRESENCE(194, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(195, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(196, 1, 0); + CTRL_REG_PRESENCE(197, 1, query51->has_ctrl197); + CTRL_REG_PRESENCE(198, 1, query51->has_query53_query54_ctrl198); + CTRL_REG_PRESENCE(199, 1, query51->has_ctrl199); + CTRL_REG_PRESENCE(200, 1, query55->has_ctrl200); + CTRL_REG_PRESENCE(201, 1, query55->has_ctrl201_ctrl202); + CTRL_REG_PRESENCE(202, 1, query55->has_ctrl201_ctrl202); + CTRL_REG_PRESENCE(203, 1, query55->has_ctrl203); + CTRL_REG_PRESENCE(204, 1, query55->has_ctrl204); + CTRL_REG_PRESENCE(205, 1, query57->has_ctrl205); + CTRL_REG_PRESENCE(206, 1, query57->has_ctrl206); + CTRL_REG_PRESENCE(207, 1, query57->has_ctrl207); + CTRL_REG_PRESENCE(208, 1, query57->has_ctrl208); + CTRL_REG_PRESENCE(209, 1, query57->has_ctrl209); + CTRL_REG_PRESENCE(210, 1, query57->has_ctrl210); + CTRL_REG_PRESENCE(211, 1, query58->has_ctrl211); + CTRL_REG_PRESENCE(212, 1, query58->has_ctrl212); + CTRL_REG_PRESENCE(213, 1, query58->has_ctrl213); + CTRL_REG_PRESENCE(214, 1, query61->has_ctrl214); + CTRL_REG_PRESENCE(215, 1, query61->has_ctrl215_query62_query63); + CTRL_REG_PRESENCE(216, 1, query61->has_ctrl216); + CTRL_REG_PRESENCE(217, 1, query61->has_ctrl217); + CTRL_REG_PRESENCE(218, 1, query61->has_misc_host_ctrl); + /* might have subpacket */ + CTRL_REG_PRESENCE(219, 1, query61->has_ctrl219); + CTRL_REG_PRESENCE(220, 1, query64->has_ctrl220); + CTRL_REG_PRESENCE(221, 1, query64->has_ctrl221); + CTRL_REG_PRESENCE(222, 1, query64->has_ctrl222); + CTRL_REG_PRESENCE(223, 1, query68->is_tddi_hic); + CTRL_REG_PRESENCE(224, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(225, 1, query67->has_abs_doze_spatial_filter_en); + CTRL_REG_PRESENCE(226, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(227, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(228, 1, (f54->query.touch_controller_family == 4)); +// CTRL_REG_RESERVED_PRESENCE(229, 1, 0); + CTRL_REG_PRESENCE(230, 1, query65->has_ctrl230); + CTRL_REG_PRESENCE(231, 1, query65->has_query66_ctrl231); + CTRL_REG_PRESENCE(232, 1, query65->has_ctrl232); +// CTRL_REG_RESERVED_PRESENCE(233, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(234, 1, 0); + CTRL_REG_PRESENCE(235, 1, query67->has_ctrl235_ctrl236); + CTRL_REG_PRESENCE(236, 1, query67->has_ctrl235_ctrl236); + CTRL_REG_PRESENCE(237, 1, query68->has_ctrl237); + CTRL_REG_PRESENCE(238, 1, query68->has_ctrl238); + CTRL_REG_PRESENCE(239, 1, query68->has_ctrl239); + /* might have up to 4 subpackets */ + CTRL_REG_PRESENCE(240, 1, query69->data[0] & 0x0f); + CTRL_REG_ADD(242, 1, query70->has_ctrl242); + CTRL_REG_ADD(269, 1, query75->has_ctrl269); + + /* add F55 control registers */ + reg_addr = f54->fn55->control_base_addr; + + CTRL_REG_ADD(0_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + + CTRL_REG_PRESENCE(1_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + CTRL_REG_PRESENCE(2_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + CTRL_REG_PRESENCE(3_f55, 1, f54->query_f55_0_2.has_edge_compensation); + CTRL_REG_PRESENCE(4_f55, 1, f54->query_f55_0_2.curve_compensation_mode); + CTRL_REG_PRESENCE(5_f55, 1, f54->query_f55_0_2.curve_compensation_mode); + CTRL_REG_PRESENCE(6_f55, 1, f54->query_f55_0_2.has_ctrl6); + CTRL_REG_PRESENCE(7_f55, 1, + f54->query_f55_0_2.has_alternate_tx_assignment); + + CTRL_REG_ADD(8_f55, 1, + (f54->query_f55_0_2.f55_q2_has_single_layer_multitouch && + f54->query_f55_3.f55_q3_has_ctrl8)); + return 0; + +exit_no_mem: + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for control registers\n", + __func__); + return -ENOMEM; +} + +static int synaptics_rmi4_f54_set_data( + struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char reg_num = 0; + unsigned short reg_addr = f54->data_base_addr; + struct f54_data *data = &f54->data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + /* data 0 - data 3: skip mandatory registers */ + reg_addr += 4; + + /* data 4 */ + DATA_REG_ADD(4, 1, f54->query.has_sense_frequency_control); + + /* F54_ANALOG_Data5 (reserved) is not present */ + /* - do not increment */ + + /* data 6 */ + DATA_REG_ADD(6, 2, f54->query.has_interference_metric); + /* data 7.0 */ + DATA_REG_ADD(7_0, 1, (f54->query.has_one_byte_report_rate || + f54->query.has_two_byte_report_rate)); + /* data 7.1 */ + DATA_REG_ADD(7_1, 1, f54->query.has_two_byte_report_rate); + /* data 8 */ + DATA_REG_ADD(8, 2, f54->query.has_variance_metric); + /* data 9 */ + DATA_REG_ADD(9, 2, f54->query.has_multi_metric_state_machine); + /* data 10 */ + DATA_REG_ADD(10, 1, (f54->query.has_multi_metric_state_machine || + f54->query.has_noise_state)); + + /* data 11 */ + DATA_REG_PRESENCE(11, 1, f54->query.has_status); + /* data 12 */ + DATA_REG_PRESENCE(12, 2, f54->query.has_slew_metric); + /* data 13 */ + DATA_REG_PRESENCE(13, 2, f54->query.has_multi_metric_state_machine); + + /* data 14 */ + DATA_REG_ADD(14, 1, f54->query13.has_cid_im); + + /* data 15 */ + DATA_REG_PRESENCE(15, 1, f54->query13.has_rail_im); + + /* data 16 */ + DATA_REG_ADD(16, 1, f54->query13.has_noise_mitigation_enh); + /* data 17 */ + DATA_REG_ADD(17, 1, f54->query16.has_data17); + + return 0; + +exit_no_mem: + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data registers\n", + __func__); + return -ENOMEM; +} + +static int synaptics_rmi4_f55_read_query( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + uint16_t reg_addr; + + retval = f54->fn_ptr->read(rmi4_data, + f54->fn55->query_base_addr, + f54->query_f55_0_2.data, + sizeof(f54->query_f55_0_2.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read F55 query registers 0-2\n", + __func__); + goto err; + } + + reg_addr = f54->fn55->query_base_addr; + + QUERY_REG_READ(_f55_0_2, 1); + QUERY_REG_READ(_f55_3, + f54->query_f55_0_2.f55_q2_has_single_layer_multitouch); +return 0; + +err: + return retval; +} + +static void synaptics_rmi4_f54_sensor_mapping( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + struct f54_control *control = &f54->control; + unsigned int rx_len; + unsigned int tx_len; + unsigned char *buffer; + unsigned char *rx_buffer; + unsigned char *tx_buffer; + unsigned short offset; + int i; + + rmi4_data->num_of_rx = 0; + rmi4_data->num_of_tx = 0; + + if (f54->query.has_sensor_assignment == 1) { + rx_len = control->reg_15->length; + tx_len = control->reg_16->length; + offset = control->reg_15->address; + + } else if (f54->fn55) { + if (f54->query_f55_0_2.has_sensor_assignment) { + rx_len = f54->query_f55_0_2.num_of_rx_electrodes; + tx_len = f54->query_f55_0_2.num_of_tx_electrodes; + + offset = f54->fn55->control_base_addr + 1; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Has sensor assignment is not set in F55\n", + __func__); + return; + } + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Cannot find sensor mapping\n", + __func__); + return; + } + + buffer = kzalloc((rx_len + tx_len) * sizeof(unsigned char), + GFP_KERNEL); + + retval = f54->fn_ptr->read(rmi4_data, offset, buffer, rx_len + tx_len); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read control registers\n", + __func__); + goto exit; + } + rx_buffer = buffer; + tx_buffer = buffer + rx_len; + + for (i = 0; i < rx_len; i++) { + if (rx_buffer[i] != 0xFF) + rmi4_data->num_of_rx++; + } + for (i = 0; i < tx_len; i++) { + if (tx_buffer[i] != 0xFF) + rmi4_data->num_of_tx++; + } + + dev_info(&rmi4_data->i2c_client->dev, + "RxTx mapped from F$%s Rx mapped count %d(%d); Tx mapped count %d(%d)\n", + f54->fn55 ? "55" : "54", rmi4_data->num_of_rx, + rx_len, rmi4_data->num_of_tx, tx_len); +exit: + kfree(buffer); +} + +static void synaptics_rmi4_f54_status_work(struct work_struct *work) +{ + int retval; + unsigned char report_index[2]; + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct synaptics_rmi4_f54_handle *f54 = container_of(dw, + struct synaptics_rmi4_f54_handle, status_work); + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + pr_debug("enter; status = %d\n", f54->status); + if (f54->status != STATUS_BUSY) { + retval = STATUS_ERROR; + goto error_exit; + } + + set_report_size(f54); + pr_debug("report_size = %d\n", f54->report_size); + if (f54->report_size == 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report data size = 0\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + if (f54->data_buffer_size < f54->report_size) { + mutex_lock(&f54->data_mutex); + if (f54->data_buffer_size) + kfree(f54->report_data); + f54->report_data = kzalloc(f54->report_size, GFP_KERNEL); + if (!f54->report_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + f54->data_buffer_size = 0; + mutex_unlock(&f54->data_mutex); + retval = -ENOMEM; + goto error_exit; + } + f54->data_buffer_size = f54->report_size; + mutex_unlock(&f54->data_mutex); + } + + report_index[0] = 0; + report_index[1] = 0; + + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + report_index, + sizeof(report_index)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write report data index\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + retval = f54->fn_ptr->read(rmi4_data, + f54->data_base_addr + DATA_REPORT_DATA_OFFSET, + f54->report_data, + f54->report_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read report data\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + pr_debug("retrieved %d bytes of test report data\n", retval); + retval = STATUS_IDLE; + +#ifdef RAW_HEX + print_raw_hex_report(f54); +#endif + +#ifdef HUMAN_READABLE + print_image_report(f54); +#endif + +error_exit: + mutex_lock(&f54->status_mutex); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, false); +#endif + __pm_relax(&f54->test_wake_lock); + f54->status = retval; + mutex_unlock(&f54->status_mutex); + pr_debug("status set to %d\n", f54->status); + + return; +} + +static void synaptics_rmi4_f54_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + pr_debug("called f54_int_mask %x, int_mask %x\n", f54->intr_mask, intr_mask); + if (f54->intr_mask & intr_mask) { + schedule_delayed_work(&f54->status_work, + msecs_to_jiffies(STATUS_WORK_INTERVAL)); + } + + return; +} + +int synaptics_rmi4_f54_read_query( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + uint16_t reg_addr; + + retval = f54->fn_ptr->read(rmi4_data, + f54->query_base_addr, + f54->query.data, + sizeof(f54->query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + goto err; + } + + reg_addr = f54->query_base_addr + sizeof(f54->query.data); + + QUERY_REG_READ(12, f54->query.has_sense_frequency_control); + QUERY_REG_READ(13, f54->query.has_query13); + QUERY_REG_READ(14, f54->query13.has_ctrl87); + QUERY_REG_READ(15, f54->query.has_query15); + QUERY_REG_READ(16, f54->query15.has_query16); + QUERY_REG_READ(17, f54->query16.has_query17); + QUERY_REG_READ(18, f54->query16.has_ctrl94_query18); + QUERY_REG_READ(19, f54->query16.has_ctrl95_query19); + QUERY_REG_READ(20, f54->query15.has_query20); + QUERY_REG_READ(21, f54->query15.has_query21); + QUERY_REG_READ(22, f54->query15.has_query22); + QUERY_REG_READ(23, f54->query22.has_query23); + QUERY_REG_READ(24, f54->query21.has_query24_data18); + QUERY_REG_READ(25, f54->query15.has_query25); + QUERY_REG_READ(26, f54->query22.has_ctrl103_query26); + QUERY_REG_READ(27, f54->query25.has_query27); + QUERY_REG_READ(28, f54->query22.has_query28); + QUERY_REG_READ(29, f54->query27.has_query29); + QUERY_REG_READ(30, f54->query29.has_query30); + QUERY_REG_READ(31, f54->query30.has_ctrl122_query31); + QUERY_REG_READ(32, f54->query30.has_query32); + QUERY_REG_READ(33, f54->query32.has_query33); + QUERY_REG_READ(34, f54->query32.has_query34); + QUERY_REG_READ(35, f54->query32.has_query35); + QUERY_REG_READ(36, f54->query33.has_query36); + if (f54->query36.has_query37) + reg_addr += 1; + QUERY_REG_READ(38, f54->query36.has_query38); + QUERY_REG_READ(39, f54->query38.has_query39); + QUERY_REG_READ(40, f54->query39.has_query40); + QUERY_REG_READ(41, f54->query40.has_ctrl163_query41); + QUERY_REG_READ(42, f54->query40.has_ctrl165_query42); + QUERY_REG_READ(43, f54->query40.has_query43); +#if 0 + reg_addr += 1; /* query44 is reserved - always present */ + reg_addr += 1; /* query45 is reserved - always present */ +#endif + QUERY_REG_READ(46, f54->query43.has_query46); + QUERY_REG_READ(47, f54->query46.has_query47); + QUERY_REG_READ(49, f54->query47.has_query49); + QUERY_REG_READ(50, f54->query49.has_query50); + QUERY_REG_READ(51, f54->query50.has_query51); + if (f54->query50.has_query52) + reg_addr += 1; + QUERY_REG_READ(55, f54->query51.has_query55); + QUERY_REG_READ(57, f54->query55.has_query57); + QUERY_REG_READ(58, f54->query57.has_query58); + QUERY_REG_READ(61, f54->query58.has_query61); + QUERY_REG_READ(64, f54->query61.has_query64); + QUERY_REG_READ(65, f54->query64.has_query65); + QUERY_REG_READ(67, f54->query65.has_query67); + QUERY_REG_READ(68, f54->query67.has_query68); + QUERY_REG_READ(69, f54->query68.has_query69); + QUERY_REG_READ(70, f54->query69.has_query70); + QUERY_REG_READ(71, f54->query70.has_query71); + QUERY_REG_READ(72, f54->query71.has_query72); + QUERY_REG_READ(73, f54->query72.has_query73); + QUERY_REG_READ(74, f54->query73.has_query74); + QUERY_REG_READ(75, f54->query74.has_query75); + + return 0; + +err: + return retval; +} + +static int synaptics_rmi4_f54_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + bool hasF54 = false; + bool hasF55 = false; + unsigned short ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_offset; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + f54->fn_ptr = kzalloc(sizeof(*(f54->fn_ptr)), GFP_KERNEL); + if (!f54->fn_ptr) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto exit_free_f54; + } + + f54->rmi4_data = rmi4_data; + f54->fn_ptr->read = rmi4_data->i2c_read; + f54->fn_ptr->write = rmi4_data->i2c_write; + f54->fn_ptr->enable = rmi4_data->irq_enable; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (ii = PDT_START; ii > PDT_END; ii -= PDT_ENTRY_SIZE) { + ii |= (page << 8); + + retval = f54->fn_ptr->read(rmi4_data, + ii, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + goto exit_free_mem; + + if (!rmi_fd.fn_number) + break; + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F54) { + hasF54 = true; + f54->query_base_addr = + rmi_fd.query_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: query_base_addr 0x%04x\n", + __func__, f54->query_base_addr); + f54->control_base_addr = + rmi_fd.ctrl_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: ctrl_base_addr 0x%04x\n", + __func__, f54->control_base_addr); + f54->data_base_addr = + rmi_fd.data_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: data_base_addr 0x%04x\n", + __func__, f54->data_base_addr); + f54->command_base_addr = + rmi_fd.cmd_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: command_base_addr 0x%04x\n", + __func__, f54->command_base_addr); + } else if (rmi_fd.fn_number == SYNAPTICS_RMI4_F55) { + hasF55 = true; + f54->fn55 = kmalloc(sizeof(*f54->fn55), + GFP_KERNEL); + f54->fn55->query_base_addr = + rmi_fd.query_base_addr | (page << 8); + f54->fn55->control_base_addr = + rmi_fd.ctrl_base_addr | (page << 8); + } + + if (hasF54 && hasF55) + goto found; + + if (!hasF54) + intr_count += + (rmi_fd.intr_src_count & MASK_3BIT); + } + } + + if (!hasF54) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F$54 is not available\n", + __func__); + goto exit; + } +found: + f54->intr_reg_num = (intr_count + 7) / 8; + if (f54->intr_reg_num != 0) + f54->intr_reg_num -= 1; + + f54->intr_mask = 0; + intr_offset = intr_count % 8; + for (ii = intr_offset; + ii < ((rmi_fd.intr_src_count & MASK_3BIT) + + intr_offset); + ii++) { + f54->intr_mask |= 1 << ii; + } + + retval = synaptics_rmi4_f54_read_query(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + goto exit_free_mem; + } + + retval = synaptics_rmi4_f55_read_query(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up F55 query registers\n", + __func__); + goto exit_free_control; + } + + retval = synaptics_rmi4_f54_set_ctrl(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up control registers\n", + __func__); + goto exit_free_control; + } + + synaptics_rmi4_f54_sensor_mapping(f54); + + retval = synaptics_rmi4_f54_set_data(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up data registers\n", + __func__); + goto exit_free_data; + } + + mutex_init(&f54->status_mutex); + mutex_init(&f54->data_mutex); + mutex_init(&f54->control_mutex); + + retval = synaptics_rmi4_f54_set_sysfs(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs entries\n", + __func__); + goto exit_sysfs; + } + + INIT_DELAYED_WORK(&f54->status_work, + synaptics_rmi4_f54_status_work); + + f54->user_report_type1 = F54_16BIT_IMAGE; + f54->user_report_type2 = F54_RAW_16BIT_IMAGE; + + wakeup_source_init(&f54->test_wake_lock, "synaptics_test_report"); + +#ifdef WATCHDOG_HRTIMER + /* Watchdog timer to catch unanswered get report commands */ + hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + f54->watchdog.function = get_report_timeout; + + /* Work function to do actual cleaning up */ + INIT_WORK(&f54->timeout_work, timeout_set_status); +#endif + + f54->init_done = true; + + return 0; + +exit_sysfs: +exit_free_data: + free_data_mem(f54); + +exit_free_control: + free_control_mem(f54); + +exit_free_mem: + kfree(f54->fn_ptr); + +exit_free_f54: + kfree(f54); + rmi4_data->f54_data = NULL; +exit: + return retval; +} + +static void synaptics_rmi4_f54_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + if (!f54->init_done) { + dev_dbg(&rmi4_data->i2c_client->dev, "%s: no cleanup necessary!!!\n", __func__); + goto exit_and_complete; + } + +#ifdef WATCHDOG_HRTIMER + hrtimer_cancel(&f54->watchdog); +#endif + + remove_sysfs(f54); + + free_data_mem(f54); + free_control_mem(f54); + + kfree(f54->report_data); + kfree(f54->fn55); + /* cancel delayed work first then free fn_ptr!!! */ + cancel_delayed_work_sync(&f54->status_work); + kfree(f54->fn_ptr); + + wakeup_source_trash(&f54->test_wake_lock); +exit_and_complete: + complete(&f54->remove_complete); + + return; +} + +static int __init rmi4_f54_module_init(void) +{ + struct synaptics_rmi4_f54_handle *f54; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + f54 = kzalloc(sizeof(*f54), GFP_KERNEL); + next->f54_data = (void *)f54; + next->scan_f54_ctrl_regs = synaptics_rmi4_scan_f54_ctrl_reg_info; + next->scan_f54_cmd_regs = synaptics_rmi4_scan_f54_cmd_reg_info; + next->scan_f54_query_regs = synaptics_rmi4_scan_f54_query_reg_info; + next->scan_f54_data_regs = synaptics_rmi4_scan_f54_data_reg_info; + dev_dbg(&next->i2c_client->dev, + "%s: f54 ptr = %p, rmi4_data_ptr %p\n", + __func__, f54, dev_get_drvdata(&next->i2c_client->dev)); + synaptics_rmi4_new_function(next, RMI_F54, true, + synaptics_rmi4_f54_init, + synaptics_rmi4_f54_remove, + synaptics_rmi4_f54_attn, + NULL, IC_MODE_UI); + } + return 0; +} + +static void __exit rmi4_f54_module_exit(void) +{ + struct synaptics_rmi4_f54_handle *f54; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + f54 = (struct synaptics_rmi4_f54_handle *)next->f54_data; + init_completion(&f54->remove_complete); + synaptics_rmi4_new_function(next, RMI_F54, false, + synaptics_rmi4_f54_init, + synaptics_rmi4_f54_remove, + synaptics_rmi4_f54_attn, + NULL, IC_MODE_UI); + wait_for_completion(&f54->remove_complete); + kfree(f54); + next->scan_f54_ctrl_regs = NULL; + next->scan_f54_cmd_regs = NULL; + next->scan_f54_query_regs = NULL; + next->scan_f54_data_regs = NULL; + next->f54_data = NULL; + } + return; +} + +module_init(rmi4_f54_module_init); +module_exit(rmi4_f54_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Test Reporting Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class.c new file mode 100644 index 000000000000..6540666f7382 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class.c @@ -0,0 +1,234 @@ +/* + * Synaptics DSX touchscreen driver MMI class extention + * + * Copyright (C) 2019 Motorola Mobility + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include + +#include "synaptics_dsx_i2c.h" + +#define ASSERT_PTR(p) if (p == NULL) { \ + dev_err(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } + +static int synaptics_mmi_get_class_entry_name(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", ts->class_entry_name); +} + +static int synaptics_mmi_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "synaptics"); +} + +static int synaptics_mmi_get_productinfo(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", + ts->rmi4_mod_info.product_id_string); +} + +static int synaptics_mmi_get_build_id(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + unsigned int firmware_id; + struct synaptics_rmi4_device_info *rmi; + + ASSERT_PTR(ts); + rmi = &(ts->rmi4_mod_info); + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%x\n", firmware_id); +} + +static int synaptics_mmi_get_config_id(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + unsigned int config_id; + struct synaptics_rmi4_device_info *rmi; + + ASSERT_PTR(ts); + rmi = &(ts->rmi4_mod_info); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%08x\n", config_id); +} + +static int synaptics_mmi_get_bus_type(struct device *dev, void *idata) { + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int synaptics_mmi_get_irq_status(struct device *dev, void *idata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = gpio_get_value(ts->board.irq_gpio); + + return 0; +} + +static int synaptics_mmi_get_drv_irq(struct device *dev, void *idata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = ts->irq_enabled ? 1 : 0; + + return 0; +} + +static int synaptics_mmi_get_poweron(struct device *dev, void *idata) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = (atomic_read(&ts->touch_stopped) == 0 && ts->flash_enabled) ? 1 : 0; + + return 0; +} + +static int synaptics_mmi_get_flashprog(struct device *dev, void *idata) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = ts->in_bootloader ? 1 : 0; + + return 0; +} + +static int synaptics_mmi_drv_irq(struct device *dev, int state) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + switch (state) { + case 0: /* Disable irq */ + synaptics_rmi4_irq_enable(ts, false); + break; + case 1: /* Enable irq */ + synaptics_rmi4_irq_enable(ts, true); + break; + default: + dev_err(dev, "%s: invalid value\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int synaptics_mmi_reset(struct device *dev, int type) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + int ret; + + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + ret = synaptics_dsx_ic_reset(ts, type); + if (ret > 0) + dev_dbg(dev,"%s: successful reset took %dms\n", __func__, ret); + else + dev_warn(dev, "%s: timed out waiting for idle\n", __func__); + + return 0; +} + +static int synaptics_mmi_pre_suspend(struct device *dev) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + synaptics_dsx_sensor_state(ts, STATE_SUSPEND); + return 0; +} + +static int synaptics_mmi_post_resume(struct device *dev) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + synaptics_dsx_sensor_ready_state(ts, false); + ts->flash_enabled = true; + return 0; +} + +static int synaptics_mmi_fw_update(struct device *dev, char *fwname) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu; + const char *name = fwname; + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + fwu = ts->fwu_data; + return (fwu && fwu->firmware_update) ? fwu->firmware_update(dev, name) : -ENOSYS; +} + +static int synaptics_mmi_fw_erase(struct device *dev) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu; + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + fwu = ts->fwu_data; + return (fwu && fwu->firmware_erase) ? fwu->firmware_erase(dev) : -ENOSYS; +} + +static int synaptics_mmi_charger_mode(struct device *dev, int mode) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + synaptics_dsx_charger_mode(ts, mode); + return 0; +} + +static struct ts_mmi_methods synaptics_mmi_methods = { + .get_vendor = synaptics_mmi_get_vendor, + .get_productinfo = synaptics_mmi_get_productinfo, + .get_build_id = synaptics_mmi_get_build_id, + .get_config_id = synaptics_mmi_get_config_id, + .get_bus_type = synaptics_mmi_get_bus_type, + .get_irq_status = synaptics_mmi_get_irq_status, + .get_drv_irq = synaptics_mmi_get_drv_irq, + .get_poweron = synaptics_mmi_get_poweron, + .get_flashprog = synaptics_mmi_get_flashprog, + .get_class_entry_name = synaptics_mmi_get_class_entry_name, + /* SET methods */ + .reset = synaptics_mmi_reset, + .drv_irq = synaptics_mmi_drv_irq, + .charger_mode = synaptics_mmi_charger_mode, + /* Firmware */ + .firmware_update = synaptics_mmi_fw_update, + .firmware_erase = synaptics_mmi_fw_erase, + /* vendor specific attribute group */ + /* PM callback */ + .post_resume = synaptics_mmi_post_resume, + .pre_suspend = synaptics_mmi_pre_suspend, +}; + +int synaptics_mmi_data_init(struct synaptics_rmi4_data *ts, bool enable) +{ + int ret = 0; + if (enable) { + ret = ts_mmi_dev_register(&ts->i2c_client->dev, &synaptics_mmi_methods); + if (ret) + dev_err(&ts->i2c_client->dev, "Failed to register ts mmi\n"); + + } else + ts_mmi_dev_unregister(&ts->i2c_client->dev); + + return ret; +}