/* * Synaptics DSX touchscreen driver * * Copyright (C) 2012-2016 Synaptics Incorporated. All rights reserved. * * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. * 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. * * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS. * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS' * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S. * DOLLARS. */ #include #include #include #include #include #include #include #include #include "synaptics_dsx_core.h" #define GESTURE_PHYS_NAME "synaptics_dsx/gesture" #define TUNING_SYSFS_DIR_NAME "tuning" #define STORE_GESTURES #ifdef STORE_GESTURES #define GESTURES_TO_STORE 10 #endif #define CTRL23_FINGER_REPORT_ENABLE_BIT 0 #define CTRL27_UDG_ENABLE_BIT 4 #define WAKEUP_GESTURE_MODE 0x02 static ssize_t udg_sysfs_engine_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_detection_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_detection_score_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_detection_index_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_registration_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_registration_begin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_registration_status_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_size_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_max_index_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_detection_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_index_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_template_valid_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_valid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_template_clear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_trace_size_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_data_show(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count); static ssize_t udg_sysfs_template_data_store(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count); static ssize_t udg_sysfs_trace_data_show(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count); static ssize_t udg_sysfs_template_displacement_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_template_displacement_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_scale_invariance_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_scale_invariance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_threshold_factor_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_threshold_factor_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev, struct device_attribute *attr, char *buf); static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); static int udg_read_tuning_params(void); static int udg_write_tuning_params(void); static int udg_detection_enable(bool enable); static int udg_engine_enable(bool enable); static int udg_set_index(unsigned char index); #ifdef STORE_GESTURES static int udg_read_valid_data(void); static int udg_write_valid_data(void); static int udg_read_template_data(unsigned char index); static int udg_write_template_data(void); #endif enum gesture_type { DETECTION = 0x0f, REGISTRATION = 0x10, }; struct udg_tuning { union { struct { unsigned char maximum_number_of_templates; unsigned char template_size; unsigned char template_disp_lsb; unsigned char template_disp_msb; unsigned char rotation_inv_lsb; unsigned char rotation_inv_msb; unsigned char scale_inv_lsb; unsigned char scale_inv_msb; unsigned char thres_factor_lsb; unsigned char thres_factor_msb; unsigned char metric_thres_lsb; unsigned char metric_thres_msb; unsigned char inter_stroke_lsb; unsigned char inter_stroke_msb; } __packed; unsigned char data[14]; }; }; struct udg_addr { unsigned short data_4; unsigned short ctrl_18; unsigned short ctrl_20; unsigned short ctrl_23; unsigned short ctrl_27; unsigned short ctrl_41; unsigned short trace_x; unsigned short trace_y; unsigned short trace_segment; unsigned short template_helper; unsigned short template_data; unsigned short template_flags; }; struct synaptics_rmi4_f12_query_0 { union { struct { struct { unsigned char has_register_descriptors:1; unsigned char has_closed_cover:1; unsigned char has_fast_glove_detect:1; unsigned char has_dribble:1; unsigned char has_4p4_jitter_filter_strength:1; unsigned char f12_query0_s0_b5__7:3; } __packed; struct { unsigned char max_num_templates:4; unsigned char f12_query0_s1_b4__7:4; unsigned char template_size_lsb; unsigned char template_size_msb; } __packed; }; unsigned char data[4]; }; }; struct synaptics_rmi4_f12_query_5 { union { struct { unsigned char size_of_query6; struct { unsigned char ctrl0_is_present:1; unsigned char ctrl1_is_present:1; unsigned char ctrl2_is_present:1; unsigned char ctrl3_is_present:1; unsigned char ctrl4_is_present:1; unsigned char ctrl5_is_present:1; unsigned char ctrl6_is_present:1; unsigned char ctrl7_is_present:1; } __packed; struct { unsigned char ctrl8_is_present:1; unsigned char ctrl9_is_present:1; unsigned char ctrl10_is_present:1; unsigned char ctrl11_is_present:1; unsigned char ctrl12_is_present:1; unsigned char ctrl13_is_present:1; unsigned char ctrl14_is_present:1; unsigned char ctrl15_is_present:1; } __packed; struct { unsigned char ctrl16_is_present:1; unsigned char ctrl17_is_present:1; unsigned char ctrl18_is_present:1; unsigned char ctrl19_is_present:1; unsigned char ctrl20_is_present:1; unsigned char ctrl21_is_present:1; unsigned char ctrl22_is_present:1; unsigned char ctrl23_is_present:1; } __packed; struct { unsigned char ctrl24_is_present:1; unsigned char ctrl25_is_present:1; unsigned char ctrl26_is_present:1; unsigned char ctrl27_is_present:1; unsigned char ctrl28_is_present:1; unsigned char ctrl29_is_present:1; unsigned char ctrl30_is_present:1; unsigned char ctrl31_is_present:1; } __packed; struct { unsigned char ctrl32_is_present:1; unsigned char ctrl33_is_present:1; unsigned char ctrl34_is_present:1; unsigned char ctrl35_is_present:1; unsigned char ctrl36_is_present:1; unsigned char ctrl37_is_present:1; unsigned char ctrl38_is_present:1; unsigned char ctrl39_is_present:1; } __packed; struct { unsigned char ctrl40_is_present:1; unsigned char ctrl41_is_present:1; unsigned char ctrl42_is_present:1; unsigned char ctrl43_is_present:1; unsigned char ctrl44_is_present:1; unsigned char ctrl45_is_present:1; unsigned char ctrl46_is_present:1; unsigned char ctrl47_is_present:1; } __packed; }; unsigned char data[7]; }; }; struct synaptics_rmi4_f12_query_8 { union { struct { unsigned char size_of_query9; struct { unsigned char data0_is_present:1; unsigned char data1_is_present:1; unsigned char data2_is_present:1; unsigned char data3_is_present:1; unsigned char data4_is_present:1; unsigned char data5_is_present:1; unsigned char data6_is_present:1; unsigned char data7_is_present:1; } __packed; struct { unsigned char data8_is_present:1; unsigned char data9_is_present:1; unsigned char data10_is_present:1; unsigned char data11_is_present:1; unsigned char data12_is_present:1; unsigned char data13_is_present:1; unsigned char data14_is_present:1; unsigned char data15_is_present:1; } __packed; struct { unsigned char data16_is_present:1; unsigned char data17_is_present:1; unsigned char data18_is_present:1; unsigned char data19_is_present:1; unsigned char data20_is_present:1; unsigned char data21_is_present:1; unsigned char data22_is_present:1; unsigned char data23_is_present:1; } __packed; }; unsigned char data[4]; }; }; struct synaptics_rmi4_f12_control_41 { union { struct { unsigned char enable_registration:1; unsigned char template_index:4; unsigned char begin:1; unsigned char f12_ctrl41_b6__7:2; } __packed; unsigned char data[1]; }; }; struct synaptics_rmi4_udg_handle { atomic_t attn_event; unsigned char intr_mask; unsigned char report_flags; unsigned char object_type_enable1; unsigned char object_type_enable2; unsigned char trace_size; unsigned char template_index; unsigned char max_num_templates; unsigned char detection_score; unsigned char detection_index; unsigned char detection_status; unsigned char registration_status; unsigned char *ctrl_buf; unsigned char *trace_data_buf; unsigned char *template_data_buf; #ifdef STORE_GESTURES unsigned char gestures_to_store; unsigned char *storage_buf; unsigned char valid_buf[2]; #endif unsigned short trace_data_buf_size; unsigned short template_size; unsigned short template_data_size; unsigned short query_base_addr; unsigned short control_base_addr; unsigned short data_base_addr; unsigned short command_base_addr; unsigned short ctrl_18_sub10_off; unsigned short ctrl_20_sub1_off; unsigned short ctrl_23_sub3_off; unsigned short ctrl_27_sub5_off; struct input_dev *udg_dev; struct kobject *tuning_dir; struct udg_addr addr; struct udg_tuning tuning; struct synaptics_rmi4_data *rmi4_data; }; static struct device_attribute attrs[] = { __ATTR(engine_enable, 0220, synaptics_rmi4_show_error, udg_sysfs_engine_enable_store), __ATTR(detection_enable, 0220, synaptics_rmi4_show_error, udg_sysfs_detection_enable_store), __ATTR(detection_score, 0444, udg_sysfs_detection_score_show, synaptics_rmi4_store_error), __ATTR(detection_index, 0444, udg_sysfs_detection_index_show, synaptics_rmi4_store_error), __ATTR(registration_enable, 0220, synaptics_rmi4_show_error, udg_sysfs_registration_enable_store), __ATTR(registration_begin, 0220, synaptics_rmi4_show_error, udg_sysfs_registration_begin_store), __ATTR(registration_status, 0444, udg_sysfs_registration_status_show, synaptics_rmi4_store_error), __ATTR(template_size, 0444, udg_sysfs_template_size_show, synaptics_rmi4_store_error), __ATTR(template_max_index, 0444, udg_sysfs_template_max_index_show, synaptics_rmi4_store_error), __ATTR(template_detection, 0444, udg_sysfs_template_detection_show, synaptics_rmi4_store_error), __ATTR(template_index, 0220, synaptics_rmi4_show_error, udg_sysfs_template_index_store), __ATTR(template_valid, 0664, udg_sysfs_template_valid_show, udg_sysfs_template_valid_store), __ATTR(template_clear, 0220, synaptics_rmi4_show_error, udg_sysfs_template_clear_store), __ATTR(trace_size, 0444, udg_sysfs_trace_size_show, synaptics_rmi4_store_error), }; static struct bin_attribute template_data = { .attr = { .name = "template_data", .mode = 0664, }, .size = 0, .read = udg_sysfs_template_data_show, .write = udg_sysfs_template_data_store, }; static struct bin_attribute trace_data = { .attr = { .name = "trace_data", .mode = 0444, }, .size = 0, .read = udg_sysfs_trace_data_show, .write = NULL, }; static struct device_attribute params[] = { __ATTR(template_displacement, 0664, udg_sysfs_template_displacement_show, udg_sysfs_template_displacement_store), __ATTR(rotation_invariance, 0664, udg_sysfs_rotation_invariance_show, udg_sysfs_rotation_invariance_store), __ATTR(scale_invariance, 0664, udg_sysfs_scale_invariance_show, udg_sysfs_scale_invariance_store), __ATTR(threshold_factor, 0664, udg_sysfs_threshold_factor_show, udg_sysfs_threshold_factor_store), __ATTR(match_metric_threshold, 0664, udg_sysfs_match_metric_threshold_show, udg_sysfs_match_metric_threshold_store), __ATTR(max_inter_stroke_time, 0664, udg_sysfs_max_inter_stroke_time_show, udg_sysfs_max_inter_stroke_time_store), }; static struct synaptics_rmi4_udg_handle *udg; static unsigned char ctrl_18_sub_size[] = {10, 10, 10, 2, 3, 4, 3, 3, 1, 1}; static unsigned char ctrl_20_sub_size[] = {2}; static unsigned char ctrl_23_sub_size[] = {1, 1, 1}; static unsigned char ctrl_27_sub_size[] = {1, 5, 2, 1, 7}; DECLARE_COMPLETION(udg_remove_complete); static ssize_t udg_sysfs_engine_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; bool enable; unsigned int input; if (kstrtouint(buf, 10, &input) != 1) return -EINVAL; if (input == 1) enable = true; else if (input == 0) enable = false; else return -EINVAL; retval = udg_engine_enable(enable); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_detection_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; bool enable; unsigned int input; if (kstrtouint(buf, 10, &input) != 1) return -EINVAL; if (input == 1) enable = true; else if (input == 0) enable = false; else return -EINVAL; udg->detection_status = 0; retval = udg_detection_enable(enable); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_detection_score_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_score); } static ssize_t udg_sysfs_detection_index_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", udg->detection_index); } static ssize_t udg_sysfs_registration_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; bool enable; unsigned int input; struct synaptics_rmi4_f12_control_41 control_41; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; if (kstrtouint(buf, 10, &input) != 1) return -EINVAL; if (input == 1) enable = true; else if (input == 0) enable = false; else return -EINVAL; if (enable) { retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_23, udg->ctrl_buf, udg->ctrl_23_sub3_off + 1); if (retval < 0) return retval; udg->ctrl_buf[0] = 0; udg->ctrl_buf[0] |= (1 << CTRL23_FINGER_REPORT_ENABLE_BIT); if (udg->ctrl_23_sub3_off) udg->ctrl_buf[udg->ctrl_23_sub3_off] = 0; retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_23, udg->ctrl_buf, udg->ctrl_23_sub3_off + 1); if (retval < 0) return retval; } else { retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_23, udg->ctrl_buf, udg->ctrl_23_sub3_off + 1); if (retval < 0) return retval; udg->ctrl_buf[0] = udg->object_type_enable1; if (udg->ctrl_23_sub3_off) { udg->ctrl_buf[udg->ctrl_23_sub3_off] = udg->object_type_enable2; } retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_23, udg->ctrl_buf, udg->ctrl_23_sub3_off + 1); if (retval < 0) return retval; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_41, control_41.data, sizeof(control_41.data)); if (retval < 0) return retval; control_41.enable_registration = enable ? 1 : 0; retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_41, control_41.data, sizeof(control_41.data)); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_registration_begin_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; bool begin; unsigned int input; struct synaptics_rmi4_f12_control_41 control_41; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; if (kstrtouint(buf, 10, &input) != 1) return -EINVAL; if (input == 1) begin = true; else if (input == 0) begin = false; else return -EINVAL; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_41, control_41.data, sizeof(control_41.data)); if (retval < 0) return retval; control_41.begin = begin ? 1 : 0; retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_41, control_41.data, sizeof(control_41.data)); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_registration_status_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "0x%02x\n", udg->registration_status); } static ssize_t udg_sysfs_template_size_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", udg->template_size); } static ssize_t udg_sysfs_template_max_index_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", udg->max_num_templates - 1); } static ssize_t udg_sysfs_template_detection_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; int attn_event; unsigned char detection_status; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; attn_event = atomic_read(&udg->attn_event); atomic_set(&udg->attn_event, 0); if (attn_event == 0) return snprintf(buf, PAGE_SIZE, "0\n"); if (udg->detection_status == 0) { retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.data_4, rmi4_data->gesture_detection, sizeof(rmi4_data->gesture_detection)); if (retval < 0) return retval; udg->detection_status = rmi4_data->gesture_detection[0]; } detection_status = udg->detection_status; udg->detection_status = 0; switch (detection_status) { case DETECTION: udg->detection_score = rmi4_data->gesture_detection[1]; udg->detection_index = rmi4_data->gesture_detection[4]; udg->trace_size = rmi4_data->gesture_detection[3]; break; case REGISTRATION: udg->registration_status = rmi4_data->gesture_detection[1]; udg->trace_size = rmi4_data->gesture_detection[3]; break; default: return snprintf(buf, PAGE_SIZE, "0\n"); } return snprintf(buf, PAGE_SIZE, "0x%02x\n", detection_status); } static ssize_t udg_sysfs_template_index_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long index; retval = sstrtoul(buf, 10, &index); if (retval) return retval; retval = udg_set_index((unsigned char)index); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_template_valid_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned char valid; unsigned char offset; unsigned char byte_num; unsigned char template_flags[2]; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; byte_num = udg->template_index / 8; offset = udg->template_index % 8; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.template_flags, template_flags, sizeof(template_flags)); if (retval < 0) return retval; valid = (template_flags[byte_num] & (1 << offset)) >> offset; return snprintf(buf, PAGE_SIZE, "%u\n", valid); } static ssize_t udg_sysfs_template_valid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long valid; unsigned char offset; unsigned char byte_num; unsigned char template_flags[2]; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = sstrtoul(buf, 10, &valid); if (retval) return retval; if (valid > 0) valid = 1; byte_num = udg->template_index / 8; offset = udg->template_index % 8; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.template_flags, template_flags, sizeof(template_flags)); if (retval < 0) return retval; if (valid) template_flags[byte_num] |= (1 << offset); else template_flags[byte_num] &= ~(1 << offset); retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.template_flags, template_flags, sizeof(template_flags)); if (retval < 0) return retval; #ifdef STORE_GESTURES udg_read_valid_data(); #endif return count; } static ssize_t udg_sysfs_template_clear_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned int input; const char cmd[] = {'0', 0}; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; if (kstrtouint(buf, 10, &input) != 1) return -EINVAL; if (input != 1) return -EINVAL; memset(udg->template_data_buf, 0x00, udg->template_data_size); retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.template_data, udg->template_data_buf, udg->template_data_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to clear template data\n", __func__); return retval; } retval = udg_sysfs_template_valid_store(dev, attr, cmd, 1); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to clear valid bit\n", __func__); return retval; } #ifdef STORE_GESTURES udg_read_template_data(udg->template_index); udg_read_valid_data(); #endif return count; } static ssize_t udg_sysfs_trace_size_show(struct device *dev, struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", udg->trace_size); } static ssize_t udg_sysfs_trace_data_show(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count) { int retval; unsigned short index = 0; unsigned short trace_data_size; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; trace_data_size = udg->trace_size * 5; if (trace_data_size == 0) return -EINVAL; if (count < trace_data_size) { dev_err(rmi4_data->pdev->dev.parent, "%s: Not enough space (%d bytes) in buffer\n", __func__, (unsigned int)count); return -EINVAL; } if (udg->trace_data_buf_size < trace_data_size) { if (udg->trace_data_buf_size) kfree(udg->trace_data_buf); udg->trace_data_buf = kzalloc(trace_data_size, GFP_KERNEL); if (!udg->trace_data_buf) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to alloc mem for trace data buffer\n", __func__); udg->trace_data_buf_size = 0; return -ENOMEM; } udg->trace_data_buf_size = trace_data_size; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.trace_x, &udg->trace_data_buf[index], udg->trace_size * 2); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to read trace X data\n", __func__); return retval; } index += udg->trace_size * 2; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.trace_y, &udg->trace_data_buf[index], udg->trace_size * 2); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to read trace Y data\n", __func__); return retval; } index += udg->trace_size * 2; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.trace_segment, &udg->trace_data_buf[index], udg->trace_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to read trace segment data\n", __func__); return retval; } retval = secure_memcpy(buf, count, udg->trace_data_buf, udg->trace_data_buf_size, trace_data_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to copy trace data\n", __func__); return retval; } return trace_data_size; } static ssize_t udg_sysfs_template_data_show(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; if (count < udg->template_data_size) { dev_err(rmi4_data->pdev->dev.parent, "%s: Not enough space (%d bytes) in buffer\n", __func__, (unsigned int)count); return -EINVAL; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.template_data, udg->template_data_buf, udg->template_data_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to read template data\n", __func__); return retval; } retval = secure_memcpy(buf, count, udg->template_data_buf, udg->template_data_size, udg->template_data_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to copy template data\n", __func__); return retval; } #ifdef STORE_GESTURES udg_read_template_data(udg->template_index); udg_read_valid_data(); #endif return udg->template_data_size; } static ssize_t udg_sysfs_template_data_store(struct file *data_file, struct kobject *kobj, struct bin_attribute *attributes, char *buf, loff_t pos, size_t count) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = secure_memcpy(udg->template_data_buf, udg->template_data_size, buf, count, count); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to copy template data\n", __func__); return retval; } retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.template_data, udg->template_data_buf, count); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to write template data\n", __func__); return retval; } #ifdef STORE_GESTURES udg_read_template_data(udg->template_index); udg_read_valid_data(); #endif return count; } static ssize_t udg_sysfs_template_displacement_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned short template_displacement; retval = udg_read_tuning_params(); if (retval < 0) return retval; template_displacement = ((unsigned short)udg->tuning.template_disp_lsb << 0) | ((unsigned short)udg->tuning.template_disp_msb << 8); return snprintf(buf, PAGE_SIZE, "%u\n", template_displacement); } static ssize_t udg_sysfs_template_displacement_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long input; retval = sstrtoul(buf, 10, &input); if (retval) return retval; retval = udg_read_tuning_params(); if (retval < 0) return retval; udg->tuning.template_disp_lsb = (unsigned char)(input >> 0); udg->tuning.template_disp_msb = (unsigned char)(input >> 8); retval = udg_write_tuning_params(); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_rotation_invariance_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned short rotation_invariance; retval = udg_read_tuning_params(); if (retval < 0) return retval; rotation_invariance = ((unsigned short)udg->tuning.rotation_inv_lsb << 0) | ((unsigned short)udg->tuning.rotation_inv_msb << 8); return snprintf(buf, PAGE_SIZE, "%u\n", rotation_invariance); } static ssize_t udg_sysfs_rotation_invariance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long input; retval = sstrtoul(buf, 10, &input); if (retval) return retval; retval = udg_read_tuning_params(); if (retval < 0) return retval; udg->tuning.rotation_inv_lsb = (unsigned char)(input >> 0); udg->tuning.rotation_inv_msb = (unsigned char)(input >> 8); retval = udg_write_tuning_params(); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_scale_invariance_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned short scale_invariance; retval = udg_read_tuning_params(); if (retval < 0) return retval; scale_invariance = ((unsigned short)udg->tuning.scale_inv_lsb << 0) | ((unsigned short)udg->tuning.scale_inv_msb << 8); return snprintf(buf, PAGE_SIZE, "%u\n", scale_invariance); } static ssize_t udg_sysfs_scale_invariance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long input; retval = sstrtoul(buf, 10, &input); if (retval) return retval; retval = udg_read_tuning_params(); if (retval < 0) return retval; udg->tuning.scale_inv_lsb = (unsigned char)(input >> 0); udg->tuning.scale_inv_msb = (unsigned char)(input >> 8); retval = udg_write_tuning_params(); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_threshold_factor_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned short threshold_factor; retval = udg_read_tuning_params(); if (retval < 0) return retval; threshold_factor = ((unsigned short)udg->tuning.thres_factor_lsb << 0) | ((unsigned short)udg->tuning.thres_factor_msb << 8); return snprintf(buf, PAGE_SIZE, "%u\n", threshold_factor); } static ssize_t udg_sysfs_threshold_factor_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long input; retval = sstrtoul(buf, 10, &input); if (retval) return retval; retval = udg_read_tuning_params(); if (retval < 0) return retval; udg->tuning.thres_factor_lsb = (unsigned char)(input >> 0); udg->tuning.thres_factor_msb = (unsigned char)(input >> 8); retval = udg_write_tuning_params(); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_match_metric_threshold_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned short match_metric_threshold; retval = udg_read_tuning_params(); if (retval < 0) return retval; match_metric_threshold = ((unsigned short)udg->tuning.metric_thres_lsb << 0) | ((unsigned short)udg->tuning.metric_thres_msb << 8); return snprintf(buf, PAGE_SIZE, "%u\n", match_metric_threshold); } static ssize_t udg_sysfs_match_metric_threshold_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long input; retval = sstrtoul(buf, 10, &input); if (retval) return retval; retval = udg_read_tuning_params(); if (retval < 0) return retval; udg->tuning.metric_thres_lsb = (unsigned char)(input >> 0); udg->tuning.metric_thres_msb = (unsigned char)(input >> 8); retval = udg_write_tuning_params(); if (retval < 0) return retval; return count; } static ssize_t udg_sysfs_max_inter_stroke_time_show(struct device *dev, struct device_attribute *attr, char *buf) { int retval; unsigned short max_inter_stroke_time; retval = udg_read_tuning_params(); if (retval < 0) return retval; max_inter_stroke_time = ((unsigned short)udg->tuning.inter_stroke_lsb << 0) | ((unsigned short)udg->tuning.inter_stroke_msb << 8); return snprintf(buf, PAGE_SIZE, "%u\n", max_inter_stroke_time); } static ssize_t udg_sysfs_max_inter_stroke_time_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int retval; unsigned long input; retval = sstrtoul(buf, 10, &input); if (retval) return retval; retval = udg_read_tuning_params(); if (retval < 0) return retval; udg->tuning.inter_stroke_lsb = (unsigned char)(input >> 0); udg->tuning.inter_stroke_msb = (unsigned char)(input >> 8); retval = udg_write_tuning_params(); if (retval < 0) return retval; return count; } static int udg_ctrl_subpacket(unsigned char ctrlreg, unsigned char subpacket, struct synaptics_rmi4_f12_query_5 *query_5) { int retval; unsigned char cnt; unsigned char regnum; unsigned char bitnum; unsigned char q5_index; unsigned char q6_index; unsigned char offset; unsigned char max_ctrlreg; unsigned char *query_6; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; max_ctrlreg = (sizeof(query_5->data) - 1) * 8 - 1; if (ctrlreg > max_ctrlreg) { dev_err(rmi4_data->pdev->dev.parent, "%s: Control register number (%d) over limit\n", __func__, ctrlreg); return -EINVAL; } q5_index = ctrlreg / 8 + 1; bitnum = ctrlreg % 8; if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) { dev_err(rmi4_data->pdev->dev.parent, "%s: Control %d is not present\n", __func__, ctrlreg); return -EINVAL; } query_6 = kmalloc(query_5->size_of_query6, GFP_KERNEL); if (!query_6) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to alloc mem for query 6\n", __func__); return -ENOMEM; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->query_base_addr + 6, query_6, query_5->size_of_query6); if (retval < 0) goto exit; q6_index = 0; for (regnum = 0; regnum < ctrlreg; regnum++) { q5_index = regnum / 8 + 1; bitnum = regnum % 8; if ((query_5->data[q5_index] & (1 << bitnum)) == 0x00) continue; if (query_6[q6_index] == 0x00) q6_index += 3; else q6_index++; while (query_6[q6_index] & ~MASK_7BIT) q6_index++; q6_index++; } cnt = 0; q6_index++; offset = subpacket / 7; bitnum = subpacket % 7; do { if (cnt == offset) { if (query_6[q6_index + cnt] & (1 << bitnum)) retval = 1; else retval = 0; goto exit; } cnt++; } while (query_6[q6_index + cnt - 1] & ~MASK_7BIT); retval = 0; exit: kfree(query_6); return retval; } static int udg_read_tuning_params(void) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_18, udg->ctrl_buf, udg->ctrl_18_sub10_off + sizeof(struct udg_tuning)); if (retval < 0) return retval; secure_memcpy(udg->tuning.data, sizeof(udg->tuning.data), (unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off], sizeof(struct udg_tuning), sizeof(struct udg_tuning)); return 0; } static int udg_write_tuning_params(void) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; secure_memcpy((unsigned char *)&udg->ctrl_buf[udg->ctrl_18_sub10_off], sizeof(struct udg_tuning), udg->tuning.data, sizeof(udg->tuning.data), sizeof(struct udg_tuning)); retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_18, udg->ctrl_buf, udg->ctrl_18_sub10_off + sizeof(struct udg_tuning)); if (retval < 0) return retval; return 0; } static int udg_detection_enable(bool enable) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_20, udg->ctrl_buf, udg->ctrl_20_sub1_off + 1); if (retval < 0) return retval; if (enable) udg->ctrl_buf[udg->ctrl_20_sub1_off] = WAKEUP_GESTURE_MODE; else udg->ctrl_buf[udg->ctrl_20_sub1_off] = udg->report_flags; retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_20, udg->ctrl_buf, udg->ctrl_20_sub1_off + 1); if (retval < 0) return retval; return 0; } static int udg_engine_enable(bool enable) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; if (enable) { retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_27, udg->ctrl_buf, udg->ctrl_27_sub5_off + 1); if (retval < 0) return retval; udg->ctrl_buf[udg->ctrl_27_sub5_off] |= (1 << CTRL27_UDG_ENABLE_BIT); retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_27, udg->ctrl_buf, udg->ctrl_27_sub5_off + 1); if (retval < 0) return retval; } else { retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_27, udg->ctrl_buf, udg->ctrl_27_sub5_off + 1); if (retval < 0) return retval; udg->ctrl_buf[udg->ctrl_27_sub5_off] &= ~(1 << CTRL27_UDG_ENABLE_BIT); retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_27, udg->ctrl_buf, udg->ctrl_27_sub5_off + 1); if (retval < 0) return retval; } return 0; } static void udg_report(void) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; atomic_set(&udg->attn_event, 1); if (rmi4_data->suspend) { if (rmi4_data->gesture_detection[0] == 0) { retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.data_4, rmi4_data->gesture_detection, sizeof(rmi4_data->gesture_detection)); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to read gesture detection\n", __func__); return; } } udg->detection_status = rmi4_data->gesture_detection[0]; rmi4_data->gesture_detection[0] = 0; if (udg->detection_status == DETECTION) { input_report_key(udg->udg_dev, KEY_WAKEUP, 1); input_sync(udg->udg_dev); input_report_key(udg->udg_dev, KEY_WAKEUP, 0); input_sync(udg->udg_dev); rmi4_data->suspend = false; } } return; } static int udg_set_index(unsigned char index) { int retval; struct synaptics_rmi4_f12_control_41 control_41; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; if (index >= udg->max_num_templates) return -EINVAL; udg->template_index = index; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_41, control_41.data, sizeof(control_41.data)); if (retval < 0) return retval; control_41.template_index = udg->template_index; retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.ctrl_41, control_41.data, sizeof(control_41.data)); if (retval < 0) return retval; return 0; } #ifdef STORE_GESTURES static int udg_read_valid_data(void) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.template_flags, udg->valid_buf, sizeof(udg->valid_buf)); if (retval < 0) return retval; return 0; } static int udg_write_valid_data(void) { int retval; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.template_flags, udg->valid_buf, sizeof(udg->valid_buf)); if (retval < 0) return retval; return 0; } static int udg_read_template_data(unsigned char index) { int retval; unsigned char *storage; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; udg_set_index(index); storage = &(udg->storage_buf[index * udg->template_data_size]); retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.template_data, storage, udg->template_data_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to read template data\n", __func__); return retval; } return 0; } static int udg_write_template_data(void) { int retval; unsigned char ii; unsigned char *storage; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; for (ii = 0; ii < udg->gestures_to_store; ii++) { udg_set_index(ii); storage = &(udg->storage_buf[ii * udg->template_data_size]); retval = synaptics_rmi4_reg_write(rmi4_data, udg->addr.template_data, storage, udg->template_data_size); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to write template data\n", __func__); return retval; } } return 0; } #endif static int udg_reg_init(void) { int retval; unsigned char ii; unsigned char data_offset; unsigned char size_of_query; unsigned char ctrl_18_offset; unsigned char ctrl_20_offset; unsigned char ctrl_23_offset; unsigned char ctrl_27_offset; unsigned char ctrl_41_offset; struct synaptics_rmi4_f12_query_0 query_0; struct synaptics_rmi4_f12_query_5 query_5; struct synaptics_rmi4_f12_query_8 query_8; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; retval = synaptics_rmi4_reg_read(rmi4_data, udg->query_base_addr + 7, &size_of_query, sizeof(size_of_query)); if (retval < 0) return retval; if (size_of_query < 4) { dev_err(rmi4_data->pdev->dev.parent, "%s: User defined gesture support unavailable (missing data registers)\n", __func__); retval = -ENODEV; return retval; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->query_base_addr + 8, query_8.data, sizeof(query_8.data)); if (retval < 0) return retval; if ((query_8.data16_is_present) && (query_8.data17_is_present) && (query_8.data18_is_present) && (query_8.data19_is_present) && (query_8.data20_is_present) && (query_8.data21_is_present)) { data_offset = query_8.data0_is_present + query_8.data1_is_present + query_8.data2_is_present + query_8.data3_is_present; udg->addr.data_4 = udg->data_base_addr + data_offset; data_offset = data_offset + query_8.data4_is_present + query_8.data5_is_present + query_8.data6_is_present + query_8.data7_is_present + query_8.data8_is_present + query_8.data9_is_present + query_8.data10_is_present + query_8.data11_is_present + query_8.data12_is_present + query_8.data13_is_present + query_8.data14_is_present + query_8.data15_is_present; udg->addr.trace_x = udg->data_base_addr + data_offset; udg->addr.trace_y = udg->addr.trace_x + 1; udg->addr.trace_segment = udg->addr.trace_y + 1; udg->addr.template_helper = udg->addr.trace_segment + 1; udg->addr.template_data = udg->addr.template_helper + 1; udg->addr.template_flags = udg->addr.template_data + 1; } else { dev_err(rmi4_data->pdev->dev.parent, "%s: User defined gesture support unavailable (missing data registers)\n", __func__); retval = -ENODEV; return retval; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->query_base_addr + 4, &size_of_query, sizeof(size_of_query)); if (retval < 0) return retval; if (size_of_query < 7) { dev_err(rmi4_data->pdev->dev.parent, "%s: User defined gesture support unavailable (missing control registers)\n", __func__); retval = -ENODEV; return retval; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->query_base_addr + 5, query_5.data, sizeof(query_5.data)); if (retval < 0) return retval; ctrl_18_offset = query_5.ctrl0_is_present + query_5.ctrl1_is_present + query_5.ctrl2_is_present + query_5.ctrl3_is_present + query_5.ctrl4_is_present + query_5.ctrl5_is_present + query_5.ctrl6_is_present + query_5.ctrl7_is_present + query_5.ctrl8_is_present + query_5.ctrl9_is_present + query_5.ctrl10_is_present + query_5.ctrl11_is_present + query_5.ctrl12_is_present + query_5.ctrl13_is_present + query_5.ctrl14_is_present + query_5.ctrl15_is_present + query_5.ctrl16_is_present + query_5.ctrl17_is_present; ctrl_20_offset = ctrl_18_offset + query_5.ctrl18_is_present + query_5.ctrl19_is_present; ctrl_23_offset = ctrl_20_offset + query_5.ctrl20_is_present + query_5.ctrl21_is_present + query_5.ctrl22_is_present; ctrl_27_offset = ctrl_23_offset+ query_5.ctrl23_is_present + query_5.ctrl24_is_present + query_5.ctrl25_is_present + query_5.ctrl26_is_present; ctrl_41_offset = ctrl_27_offset+ query_5.ctrl27_is_present + query_5.ctrl28_is_present + query_5.ctrl29_is_present + query_5.ctrl30_is_present + query_5.ctrl31_is_present + query_5.ctrl32_is_present + query_5.ctrl33_is_present + query_5.ctrl34_is_present + query_5.ctrl35_is_present + query_5.ctrl36_is_present + query_5.ctrl37_is_present + query_5.ctrl38_is_present + query_5.ctrl39_is_present + query_5.ctrl40_is_present; udg->addr.ctrl_18 = udg->control_base_addr + ctrl_18_offset; udg->addr.ctrl_20 = udg->control_base_addr + ctrl_20_offset; udg->addr.ctrl_23 = udg->control_base_addr + ctrl_23_offset; udg->addr.ctrl_27 = udg->control_base_addr + ctrl_27_offset; udg->addr.ctrl_41 = udg->control_base_addr + ctrl_41_offset; udg->ctrl_18_sub10_off = 0; for (ii = 0; ii < 10; ii++) { retval = udg_ctrl_subpacket(18, ii, &query_5); if (retval == 1) udg->ctrl_18_sub10_off += ctrl_18_sub_size[ii]; else if (retval < 0) return retval; } udg->ctrl_20_sub1_off = 0; for (ii = 0; ii < 1; ii++) { retval = udg_ctrl_subpacket(20, ii, &query_5); if (retval == 1) udg->ctrl_20_sub1_off += ctrl_20_sub_size[ii]; else if (retval < 0) return retval; } udg->ctrl_23_sub3_off = 0; for (ii = 0; ii < 3; ii++) { retval = udg_ctrl_subpacket(23, ii, &query_5); if (retval == 1) udg->ctrl_23_sub3_off += ctrl_23_sub_size[ii]; else if (retval < 0) return retval; } retval = udg_ctrl_subpacket(23, 3, &query_5); if (retval == 0) udg->ctrl_23_sub3_off = 0; else if (retval < 0) return retval; udg->ctrl_27_sub5_off = 0; for (ii = 0; ii < 5; ii++) { retval = udg_ctrl_subpacket(27, ii, &query_5); if (retval == 1) udg->ctrl_27_sub5_off += ctrl_27_sub_size[ii]; else if (retval < 0) return retval; } retval = synaptics_rmi4_reg_read(rmi4_data, udg->query_base_addr + 0, query_0.data, sizeof(query_0.data)); if (retval < 0) return retval; udg->max_num_templates = query_0.max_num_templates; udg->template_size = ((unsigned short)query_0.template_size_lsb << 0) | ((unsigned short)query_0.template_size_msb << 8); udg->template_data_size = udg->template_size * 4 * 2 + 4 + 1; #ifdef STORE_GESTURES udg->gestures_to_store = udg->max_num_templates; if (GESTURES_TO_STORE < udg->gestures_to_store) udg->gestures_to_store = GESTURES_TO_STORE; #endif retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_20, udg->ctrl_buf, udg->ctrl_20_sub1_off + 1); if (retval < 0) return retval; udg->report_flags = udg->ctrl_buf[udg->ctrl_20_sub1_off]; retval = synaptics_rmi4_reg_read(rmi4_data, udg->addr.ctrl_23, udg->ctrl_buf, udg->ctrl_23_sub3_off + 1); if (retval < 0) return retval; udg->object_type_enable1 = udg->ctrl_buf[0]; if (udg->ctrl_23_sub3_off) udg->object_type_enable2 = udg->ctrl_buf[udg->ctrl_23_sub3_off]; return retval; } static int udg_scan_pdt(void) { int retval; unsigned char ii; unsigned char page; unsigned char intr_count = 0; unsigned char intr_off; unsigned char intr_src; unsigned short addr; struct synaptics_rmi4_fn_desc fd; struct synaptics_rmi4_data *rmi4_data = udg->rmi4_data; for (page = 0; page < PAGES_TO_SERVICE; page++) { for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { addr |= (page << 8); retval = synaptics_rmi4_reg_read(rmi4_data, addr, (unsigned char *)&fd, sizeof(fd)); if (retval < 0) return retval; addr &= ~(MASK_8BIT << 8); if (fd.fn_number) { dev_dbg(rmi4_data->pdev->dev.parent, "%s: Found F%02x\n", __func__, fd.fn_number); switch (fd.fn_number) { case SYNAPTICS_RMI4_F12: goto f12_found; break; } } else { break; } intr_count += fd.intr_src_count; } } dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to find F12\n", __func__); return -EINVAL; f12_found: udg->query_base_addr = fd.query_base_addr | (page << 8); udg->control_base_addr = fd.ctrl_base_addr | (page << 8); udg->data_base_addr = fd.data_base_addr | (page << 8); udg->command_base_addr = fd.cmd_base_addr | (page << 8); retval = udg_reg_init(); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to initialize user defined gesture registers\n", __func__); return retval; } udg->intr_mask = 0; intr_src = fd.intr_src_count; intr_off = intr_count % 8; for (ii = intr_off; ii < (intr_src + intr_off); ii++) { udg->intr_mask |= 1 << ii; } rmi4_data->intr_mask[0] |= udg->intr_mask; addr = rmi4_data->f01_ctrl_base_addr + 1; retval = synaptics_rmi4_reg_write(rmi4_data, addr, &rmi4_data->intr_mask[0], sizeof(rmi4_data->intr_mask[0])); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to set interrupt enable bit\n", __func__); return retval; } return 0; } static void synaptics_rmi4_udg_attn(struct synaptics_rmi4_data *rmi4_data, unsigned char intr_mask) { if (!udg) return; if (udg->intr_mask & intr_mask) udg_report(); return; } static int synaptics_rmi4_udg_init(struct synaptics_rmi4_data *rmi4_data) { int retval; unsigned char ii; unsigned char size; unsigned char attr_count; unsigned char param_count; if (udg) { dev_dbg(rmi4_data->pdev->dev.parent, "%s: Handle already exists\n", __func__); return 0; } udg = kzalloc(sizeof(*udg), GFP_KERNEL); if (!udg) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to alloc mem for udg\n", __func__); retval = -ENOMEM; goto exit; } size = 0; for (ii = 0; ii < sizeof(ctrl_18_sub_size); ii++) size += ctrl_18_sub_size[ii]; size += sizeof(struct udg_tuning); udg->ctrl_buf = kzalloc(size, GFP_KERNEL); if (!udg->ctrl_buf) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to alloc mem for ctrl_buf\n", __func__); retval = -ENOMEM; goto exit_free_udg; } udg->rmi4_data = rmi4_data; retval = udg_scan_pdt(); if (retval < 0) goto exit_free_ctrl_buf; udg->template_data_buf = kzalloc(udg->template_data_size, GFP_KERNEL); if (!udg->template_data_buf) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to alloc mem for template_data_buf\n", __func__); retval = -ENOMEM; goto exit_free_ctrl_buf; } #ifdef STORE_GESTURES udg->storage_buf = kzalloc( udg->template_data_size * udg->gestures_to_store, GFP_KERNEL); if (!udg->storage_buf) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to alloc mem for storage_buf\n", __func__); kfree(udg->template_data_buf); retval = -ENOMEM; goto exit_free_ctrl_buf; } #endif udg->udg_dev = input_allocate_device(); if (udg->udg_dev == NULL) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to allocate gesture device\n", __func__); retval = -ENOMEM; goto exit_free_template_data_buf; } udg->udg_dev->name = GESTURE_DRIVER_NAME; udg->udg_dev->phys = GESTURE_PHYS_NAME; udg->udg_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT; udg->udg_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION; udg->udg_dev->dev.parent = rmi4_data->pdev->dev.parent; input_set_drvdata(udg->udg_dev, rmi4_data); set_bit(EV_KEY, udg->udg_dev->evbit); set_bit(KEY_WAKEUP, udg->udg_dev->keybit); input_set_capability(udg->udg_dev, EV_KEY, KEY_WAKEUP); retval = input_register_device(udg->udg_dev); if (retval) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to register gesture device\n", __func__); input_free_device(udg->udg_dev); goto exit_free_template_data_buf; } udg->tuning_dir = kobject_create_and_add(TUNING_SYSFS_DIR_NAME, &udg->udg_dev->dev.kobj); if (!udg->tuning_dir) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to create tuning sysfs directory\n", __func__); goto exit_unregister_input_device; } retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &template_data); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to create template data bin file\n", __func__); goto exit_remove_sysfs_directory; } retval = sysfs_create_bin_file(&udg->udg_dev->dev.kobj, &trace_data); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to create trace data bin file\n", __func__); goto exit_remove_bin_file; } for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { retval = sysfs_create_file(&udg->udg_dev->dev.kobj, &attrs[attr_count].attr); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to create sysfs attributes\n", __func__); retval = -ENODEV; goto exit_remove_attrs; } } for (param_count = 0; param_count < ARRAY_SIZE(params); param_count++) { retval = sysfs_create_file(udg->tuning_dir, ¶ms[param_count].attr); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to create tuning parameters\n", __func__); retval = -ENODEV; goto exit_remove_params; } } retval = udg_engine_enable(true); if (retval < 0) { dev_err(rmi4_data->pdev->dev.parent, "%s: Failed to enable gesture engine\n", __func__); goto exit_remove_params; } return 0; exit_remove_params: for (param_count--; param_count >= 0; param_count--) { sysfs_remove_file(udg->tuning_dir, ¶ms[param_count].attr); } exit_remove_attrs: for (attr_count--; attr_count >= 0; attr_count--) { sysfs_remove_file(&udg->udg_dev->dev.kobj, &attrs[attr_count].attr); } sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data); exit_remove_bin_file: sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data); exit_remove_sysfs_directory: kobject_put(udg->tuning_dir); exit_unregister_input_device: input_unregister_device(udg->udg_dev); exit_free_template_data_buf: #ifdef STORE_GESTURES kfree(udg->storage_buf); #endif kfree(udg->template_data_buf); exit_free_ctrl_buf: kfree(udg->ctrl_buf); exit_free_udg: kfree(udg); udg = NULL; exit: return retval; } static void synaptics_rmi4_udg_remove(struct synaptics_rmi4_data *rmi4_data) { unsigned char count; if (!udg) goto exit; for (count = 0; count < ARRAY_SIZE(params); count++) { sysfs_remove_file(udg->tuning_dir, ¶ms[count].attr); } for (count = 0; count < ARRAY_SIZE(attrs); count++) { sysfs_remove_file(&udg->udg_dev->dev.kobj, &attrs[count].attr); } sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &trace_data); sysfs_remove_bin_file(&udg->udg_dev->dev.kobj, &template_data); kobject_put(udg->tuning_dir); input_unregister_device(udg->udg_dev); #ifdef STORE_GESTURES kfree(udg->storage_buf); #endif kfree(udg->template_data_buf); kfree(udg->trace_data_buf); kfree(udg->ctrl_buf); kfree(udg); udg = NULL; exit: complete(&udg_remove_complete); } static void synaptics_rmi4_udg_reset(struct synaptics_rmi4_data *rmi4_data) { if (!udg) { synaptics_rmi4_udg_init(rmi4_data); return; } udg_scan_pdt(); udg_engine_enable(true); #ifdef STORE_GESTURES udg_write_template_data(); udg_write_valid_data(); #endif } static void synaptics_rmi4_udg_reinit(struct synaptics_rmi4_data *rmi4_data) { if (!udg) return; udg_engine_enable(true); #ifdef STORE_GESTURES udg_write_template_data(); udg_write_valid_data(); #endif } static void synaptics_rmi4_udg_e_suspend(struct synaptics_rmi4_data *rmi4_data) { if (!udg) return; rmi4_data->sleep_enable(rmi4_data, false); rmi4_data->irq_enable(rmi4_data, true, false); enable_irq_wake(rmi4_data->irq); udg_engine_enable(true); udg_detection_enable(true); } static void synaptics_rmi4_udg_suspend(struct synaptics_rmi4_data *rmi4_data) { if (!udg) return; rmi4_data->sleep_enable(rmi4_data, false); rmi4_data->irq_enable(rmi4_data, true, false); enable_irq_wake(rmi4_data->irq); udg_engine_enable(true); udg_detection_enable(true); } static void synaptics_rmi4_udg_resume(struct synaptics_rmi4_data *rmi4_data) { if (!udg) return; disable_irq_wake(rmi4_data->irq); udg_detection_enable(false); } static void synaptics_rmi4_udg_l_resume(struct synaptics_rmi4_data *rmi4_data) { if (!udg) return; disable_irq_wake(rmi4_data->irq); udg_detection_enable(false); } static struct synaptics_rmi4_exp_fn gesture_module = { .fn_type = RMI_GESTURE, .init = synaptics_rmi4_udg_init, .remove = synaptics_rmi4_udg_remove, .reset = synaptics_rmi4_udg_reset, .reinit = synaptics_rmi4_udg_reinit, .early_suspend = synaptics_rmi4_udg_e_suspend, .suspend = synaptics_rmi4_udg_suspend, .resume = synaptics_rmi4_udg_resume, .late_resume = synaptics_rmi4_udg_l_resume, .attn = synaptics_rmi4_udg_attn, }; static int __init rmi4_gesture_module_init(void) { synaptics_rmi4_new_function(&gesture_module, true); return 0; } static void __exit rmi4_gesture_module_exit(void) { synaptics_rmi4_new_function(&gesture_module, false); wait_for_completion(&udg_remove_complete); } module_init(rmi4_gesture_module_init); module_exit(rmi4_gesture_module_exit); MODULE_AUTHOR("Synaptics, Inc."); MODULE_DESCRIPTION("Synaptics DSX User Defined Gesture Module"); MODULE_LICENSE("GPL v2");