| /* |
| * Synaptics DSX touchscreen driver |
| * |
| * Copyright (C) 2012 Synaptics Incorporated |
| * |
| * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com> |
| * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com> |
| * |
| * 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 <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/i2c.h> |
| #include <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/input.h> |
| #include <linux/ctype.h> |
| #include <linux/hrtimer.h> |
| #include <linux/wakelock.h> |
| #include <linux/input/synaptics_rmi_dsx.h> |
| #include "synaptics_dsx_i2c.h" |
| |
| #define WATCHDOG_HRTIMER |
| #define WATCHDOG_TIMEOUT_S 1 |
| #define STATUS_WORK_INTERVAL 20 /* ms */ |
| |
| /* |
| #define RAW_HEX |
| #define HUMAN_READABLE |
| */ |
| |
| #define STATUS_IDLE 0 |
| #define STATUS_BUSY 1 |
| |
| #define DATA_REPORT_INDEX_OFFSET 1 |
| #define DATA_REPORT_DATA_OFFSET 3 |
| |
| #define COMMAND_GET_REPORT 1 |
| #define COMMAND_FORCE_CAL 2 |
| |
| #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 |
| |
| #define concat(a, b) a##b |
| |
| #define GROUP(_attrs) {\ |
| .attrs = _attrs,\ |
| } |
| |
| #define attrify(propname) (&dev_attr_##propname.attr) |
| |
| #define show_prototype_ext(propname, perm)\ |
| static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ |
| struct device *dev,\ |
| struct device_attribute *attr,\ |
| char *buf);\ |
| \ |
| struct device_attribute dev_attr_##propname =\ |
| __ATTR(propname, (perm),\ |
| concat(synaptics_rmi4_f54, _##propname##_show),\ |
| synaptics_rmi4_store_error); |
| |
| #define show_prototype(propname)\ |
| show_prototype_ext(propname, S_IRUSR | S_IRGRP) |
| |
| #define store_prototype(propname)\ |
| static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ |
| struct device *dev,\ |
| struct device_attribute *attr,\ |
| const char *buf, size_t count);\ |
| \ |
| struct device_attribute dev_attr_##propname =\ |
| __ATTR(propname, S_IWUSR | S_IWGRP,\ |
| synaptics_rmi4_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 device *dev,\ |
| struct device_attribute *attr,\ |
| char *buf);\ |
| \ |
| static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ |
| struct device *dev,\ |
| struct device_attribute *attr,\ |
| const char *buf, size_t count);\ |
| \ |
| struct device_attribute dev_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_IWUSR | S_IWGRP) |
| |
| #define simple_show_func(rtype, propname, fmt)\ |
| static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ |
| struct device *dev,\ |
| struct device_attribute *attr,\ |
| char *buf)\ |
| {\ |
| 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 device *dev,\ |
| struct device_attribute *attr,\ |
| char *buf)\ |
| {\ |
| int retval;\ |
| struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ |
| \ |
| 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_store_func(rtype, rgrp, propname, fmt)\ |
| show_func(rtype, rgrp, propname, fmt)\ |
| \ |
| static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ |
| struct device *dev,\ |
| struct device_attribute *attr,\ |
| const char *buf, size_t count)\ |
| {\ |
| int retval;\ |
| unsigned long setting;\ |
| unsigned long o_setting;\ |
| struct synaptics_rmi4_data *rmi4_data = f54->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;\ |
| } \ |
| \ |
| 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_replicated_func(rtype, rgrp, propname, fmt)\ |
| static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ |
| struct device *dev,\ |
| struct device_attribute *attr,\ |
| char *buf)\ |
| {\ |
| int retval;\ |
| int size = 0;\ |
| unsigned char ii;\ |
| unsigned char length;\ |
| unsigned char *temp;\ |
| struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ |
| \ |
| mutex_lock(&f54->rtype##_mutex);\ |
| \ |
| length = f54->rtype.rgrp->length;\ |
| \ |
| 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 < length; 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: 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;\ |
| } \ |
| |
| #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 device *dev,\ |
| struct device_attribute *attr,\ |
| const char *buf, size_t count)\ |
| {\ |
| int retval;\ |
| unsigned int setting;\ |
| unsigned char ii;\ |
| unsigned char length;\ |
| const unsigned char *temp;\ |
| struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ |
| \ |
| mutex_lock(&f54->rtype##_mutex);\ |
| \ |
| length = f54->rtype.rgrp->length;\ |
| \ |
| 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 < length; ii++) {\ |
| if (sscanf(temp, fmt, &setting) == 1) {\ |
| f54->rtype.rgrp->data[ii].propname = setting;\ |
| } else {\ |
| retval = f54->fn_ptr->read(rmi4_data,\ |
| f54->rtype.rgrp->address,\ |
| (unsigned char *)f54->rtype.rgrp->data,\ |
| length);\ |
| 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") |
| |
| 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, |
| 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; |
| |
| /* queries 9 10 11 */ |
| unsigned char f54_query9; |
| unsigned char f54_query10; |
| unsigned char f54_query11; |
| |
| /* query 12 */ |
| unsigned char number_of_sensing_frequencies:4; |
| unsigned char f54_query12_b4__7:4; |
| } __packed; |
| unsigned char data[14]; |
| }; |
| }; |
| |
| 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 { |
| 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 f55_query { |
| 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 reserved:4; |
| } __packed; |
| unsigned char data[3]; |
| }; |
| }; |
| |
| |
| struct synaptics_rmi4_fn55_desc { |
| unsigned short query_base_addr; |
| unsigned short control_base_addr; |
| }; |
| |
| struct synaptics_rmi4_f54_handle { |
| 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_control control; |
| struct kobject *attr_dir; |
| 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 wake_lock test_wake_lock; |
| }; |
| |
| 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) |
| store_prototype(force_cal) |
| 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(number_of_sensing_frequencies) |
| |
| 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) |
| |
| 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_cal), |
| 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(number_of_sensing_frequencies), |
| 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_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), |
| }; |
| |
| static bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)]; |
| |
| static struct bin_attribute dev_report_data = { |
| .attr = { |
| .name = "report_data", |
| .mode = S_IRUGO, |
| }, |
| .size = 0, |
| .read = synaptics_rmi4_f54_data_read, |
| }; |
| |
| static struct synaptics_rmi4_f54_handle *f54; |
| |
| static struct completion remove_complete; |
| |
| 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: |
| return true; |
| break; |
| default: |
| return false; |
| } |
| } |
| |
| static int set_user_report_type(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 device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| return set_user_report_type(&f54->user_report_type1, buf, count); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_user_report_type2_store(struct device *dev, |
| struct device_attribute *attr, const char *buf, size_t count) |
| { |
| return set_user_report_type(&f54->user_report_type2, buf, count); |
| } |
| |
| static void set_report_size(void) |
| { |
| 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; |
| default: |
| f54->report_size = 0; |
| } |
| |
| return; |
| } |
| |
| static int set_interrupt(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; |
| } |
| |
| 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; |
| } 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; |
| } |
| |
| return 0; |
| } |
| |
| #ifdef WATCHDOG_HRTIMER |
| static void timeout_set_status(struct work_struct *work) |
| { |
| int retval; |
| unsigned char command; |
| 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)); |
| 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(false); |
| wake_unlock(&f54->test_wake_lock); |
| } |
| mutex_unlock(&f54->status_mutex); |
| |
| return; |
| } |
| |
| static enum hrtimer_restart get_report_timeout(struct hrtimer *timer) |
| { |
| schedule_work(&(f54->timeout_work)); |
| |
| return HRTIMER_NORESTART; |
| } |
| #endif |
| |
| #ifdef RAW_HEX |
| static void print_raw_hex_report(void) |
| { |
| 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(void) |
| { |
| 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(void) |
| { |
| 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); |
| kfree(control.reg_15); |
| kfree(control.reg_16); |
| kfree(control.reg_17); |
| kfree(control.reg_18); |
| 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); |
| kfree(control.reg_36); |
| kfree(control.reg_37); |
| kfree(control.reg_38); |
| kfree(control.reg_39); |
| kfree(control.reg_40); |
| |
| return; |
| } |
| |
| static void remove_sysfs(void) |
| { |
| int reg_num; |
| |
| sysfs_remove_bin_file(f54->attr_dir, &dev_report_data); |
| |
| sysfs_remove_group(f54->attr_dir, &attr_group); |
| |
| for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) |
| sysfs_remove_group(f54->attr_dir, &attrs_ctrl_regs[reg_num]); |
| |
| kobject_put(f54->attr_dir); |
| |
| return; |
| } |
| |
| static int synaptics_rmi4_f54_reset(void) |
| { |
| 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, NULL); |
| |
| 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 device *dev, |
| struct device_attribute *attr, 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(); |
| if (retval < 0) |
| return retval; |
| |
| return count; |
| } |
| |
| static ssize_t synaptics_rmi4_f54_status_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%d\n", f54->status); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_report_size_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_num_of_mapped_rx_show(struct device *dev, |
| struct device_attribute *attr, 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 device *dev, |
| struct device_attribute *attr, 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 device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_no_auto_cal_store(struct device *dev, |
| struct device_attribute *attr, 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 device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%d\n", f54->report_type); |
| } |
| |
| static int synaptics_rmi4_f54_report_type_set(enum f54_report_types report_type) |
| { |
| int retval = -EINVAL; |
| unsigned char data; |
| struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; |
| int report_type_valid = is_report_type_valid(report_type); |
| |
| if (!report_type_valid) |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Report type not supported by driver\n", __func__); |
| |
| if (report_type_valid) { |
| 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); |
| } else |
| f54->report_type = INVALID_REPORT_TYPE; |
| |
| if (retval < 0) |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Failed to write data register\n", __func__); |
| |
| return retval; |
| } |
| |
| static ssize_t synaptics_rmi4_f54_report_type_store(struct device *dev, |
| struct device_attribute *attr, 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( |
| (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 device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type1); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_user_report_type2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type2); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_fifoindex_show(struct device *dev, |
| struct device_attribute *attr, 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 device *dev, |
| struct device_attribute *attr, 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; |
| } |
| |
| ssize_t send_get_report_command(void) |
| { |
| 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; |
| } |
| |
| wake_lock(&f54->test_wake_lock); |
| set_interrupt(true); |
| f54->status = STATUS_BUSY; |
| |
| retval = f54->fn_ptr->write(rmi4_data, |
| f54->command_base_addr, |
| &command, |
| sizeof(command)); |
| |
| mutex_unlock(&f54->status_mutex); |
| |
| if (retval < 0) { |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Failed to write get report command\n", |
| __func__); |
| goto error_exit; |
| } |
| |
| #ifdef WATCHDOG_HRTIMER |
| hrtimer_start(&f54->watchdog, |
| ktime_set(WATCHDOG_TIMEOUT_S, 0), |
| HRTIMER_MODE_REL); |
| #endif |
| out: |
| return retval; |
| |
| error_exit: |
| mutex_lock(&f54->status_mutex); |
| set_interrupt(false); |
| wake_unlock(&f54->test_wake_lock); |
| f54->status = retval; |
| mutex_unlock(&f54->status_mutex); |
| |
| goto out; |
| } |
| |
| static ssize_t synaptics_rmi4_f54_get_report_store(struct device *dev, |
| struct device_attribute *attr, 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(); |
| if (retval < 0) |
| return retval; |
| |
| return count; |
| } |
| |
| static ssize_t synaptics_rmi4_f54_force_cal_store(struct device *dev, |
| struct device_attribute *attr, 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, number_of_sensing_frequencies) |
| |
| 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) |
| |
| static ssize_t synaptics_rmi4_f54_burst_count_show(struct device *dev, |
| struct device_attribute *attr, 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( |
| 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(); |
| if (retval < 0) |
| goto out; |
| } |
| |
| retval = synaptics_rmi4_f54_report_type_set(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(); |
| |
| out: |
| retval = scnprintf(buf, PAGE_SIZE, "%d\n", retval); |
| return retval; |
| } |
| |
| static ssize_t synaptics_rmi4_f54_user_get_report1_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return synaptics_rmi4_f54_user_get_report_show( |
| buf, f54->user_report_type1); |
| } |
| |
| static ssize_t synaptics_rmi4_f54_user_get_report2_show(struct device *dev, |
| struct device_attribute *attr, char *buf) |
| { |
| return synaptics_rmi4_f54_user_get_report_show( |
| 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_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); |
| return count; |
| } |
| |
| static int synaptics_rmi4_f54_set_sysfs(void) |
| { |
| int retval; |
| int reg_num; |
| struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; |
| |
| f54->attr_dir = kobject_create_and_add("f54", |
| &rmi4_data->i2c_client->dev.kobj); |
| if (!f54->attr_dir) { |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Failed to create sysfs directory\n", |
| __func__); |
| goto exit_1; |
| } |
| |
| retval = sysfs_create_bin_file(f54->attr_dir, &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->attr_dir, &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->attr_dir, |
| &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; |
| } |
| } |
| } |
| |
| return 0; |
| |
| exit_4: |
| for (reg_num--; reg_num >= 0; reg_num--) |
| sysfs_remove_group(f54->attr_dir, &attrs_ctrl_regs[reg_num]); |
| |
| sysfs_remove_group(f54->attr_dir, &attr_group); |
| |
| exit_3: |
| sysfs_remove_bin_file(f54->attr_dir, &dev_report_data); |
| |
| exit_2: |
| kobject_put(f54->attr_dir); |
| |
| exit_1: |
| return -ENODEV; |
| } |
| |
| static int synaptics_rmi4_f54_set_ctrl(void) |
| { |
| unsigned char length; |
| unsigned char reg_num = 0; |
| unsigned char num_of_sensing_freqs; |
| unsigned short reg_addr = f54->control_base_addr; |
| struct f54_control *control = &f54->control; |
| struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; |
| |
| num_of_sensing_freqs = f54->query.number_of_sensing_frequencies; |
| |
| /* control 0 */ |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_0 = kzalloc(sizeof(*(control->reg_0)), |
| GFP_KERNEL); |
| if (!control->reg_0) |
| goto exit_no_mem; |
| control->reg_0->address = reg_addr; |
| reg_addr += sizeof(control->reg_0->data); |
| reg_num++; |
| |
| /* control 1 */ |
| if ((f54->query.touch_controller_family == 0) || |
| (f54->query.touch_controller_family == 1)) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_1 = kzalloc(sizeof(*(control->reg_1)), |
| GFP_KERNEL); |
| if (!control->reg_1) |
| goto exit_no_mem; |
| control->reg_1->address = reg_addr; |
| reg_addr += sizeof(control->reg_1->data); |
| } |
| reg_num++; |
| |
| /* control 2 */ |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_2 = kzalloc(sizeof(*(control->reg_2)), |
| GFP_KERNEL); |
| if (!control->reg_2) |
| goto exit_no_mem; |
| control->reg_2->address = reg_addr; |
| reg_addr += sizeof(control->reg_2->data); |
| reg_num++; |
| |
| /* control 3 */ |
| if (f54->query.has_pixel_touch_threshold_adjustment == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_3 = kzalloc(sizeof(*(control->reg_3)), |
| GFP_KERNEL); |
| if (!control->reg_3) |
| goto exit_no_mem; |
| control->reg_3->address = reg_addr; |
| reg_addr += sizeof(control->reg_3->data); |
| } |
| reg_num++; |
| |
| /* controls 4 5 6 */ |
| if ((f54->query.touch_controller_family == 0) || |
| (f54->query.touch_controller_family == 1)) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_4__6 = kzalloc(sizeof(*(control->reg_4__6)), |
| GFP_KERNEL); |
| if (!control->reg_4__6) |
| goto exit_no_mem; |
| control->reg_4__6->address = reg_addr; |
| reg_addr += sizeof(control->reg_4__6->data); |
| } |
| reg_num++; |
| |
| /* control 7 */ |
| if (f54->query.touch_controller_family == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_7 = kzalloc(sizeof(*(control->reg_7)), |
| GFP_KERNEL); |
| if (!control->reg_7) |
| goto exit_no_mem; |
| control->reg_7->address = reg_addr; |
| reg_addr += sizeof(control->reg_7->data); |
| } |
| reg_num++; |
| |
| /* controls 8 9 */ |
| if ((f54->query.touch_controller_family == 0) || |
| (f54->query.touch_controller_family == 1)) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_8__9 = kzalloc(sizeof(*(control->reg_8__9)), |
| GFP_KERNEL); |
| if (!control->reg_8__9) |
| goto exit_no_mem; |
| control->reg_8__9->address = reg_addr; |
| reg_addr += sizeof(control->reg_8__9->data); |
| } |
| reg_num++; |
| |
| /* control 10 */ |
| if (f54->query.has_interference_metric == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_10 = kzalloc(sizeof(*(control->reg_10)), |
| GFP_KERNEL); |
| if (!control->reg_10) |
| goto exit_no_mem; |
| control->reg_10->address = reg_addr; |
| reg_addr += sizeof(control->reg_10->data); |
| } |
| reg_num++; |
| |
| /* control 11 */ |
| if (f54->query.has_ctrl11 == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_11 = kzalloc(sizeof(*(control->reg_11)), |
| GFP_KERNEL); |
| if (!control->reg_11) |
| goto exit_no_mem; |
| control->reg_11->address = reg_addr; |
| reg_addr += sizeof(control->reg_11->data); |
| } |
| reg_num++; |
| |
| /* controls 12 13 */ |
| if (f54->query.has_relaxation_control == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_12__13 = kzalloc(sizeof(*(control->reg_12__13)), |
| GFP_KERNEL); |
| if (!control->reg_12__13) |
| goto exit_no_mem; |
| control->reg_12__13->address = reg_addr; |
| reg_addr += sizeof(control->reg_12__13->data); |
| } |
| reg_num++; |
| |
| /* 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; |
| |
| length = num_of_sensing_freqs; |
| |
| control->reg_17 = kzalloc(sizeof(*(control->reg_17)), |
| GFP_KERNEL); |
| if (!control->reg_17) |
| goto exit_no_mem; |
| control->reg_17->length = length; |
| control->reg_17->data = kzalloc(length * |
| sizeof(*(control->reg_17->data)), GFP_KERNEL); |
| if (!control->reg_17->data) |
| goto exit_no_mem; |
| control->reg_17->address = reg_addr; |
| reg_addr += length; |
| |
| control->reg_18 = kzalloc(sizeof(*(control->reg_18)), |
| GFP_KERNEL); |
| if (!control->reg_18) |
| goto exit_no_mem; |
| control->reg_18->length = length; |
| control->reg_18->data = kzalloc(length * |
| sizeof(*(control->reg_18->data)), GFP_KERNEL); |
| if (!control->reg_18->data) |
| goto exit_no_mem; |
| control->reg_18->address = reg_addr; |
| reg_addr += length; |
| |
| control->reg_19 = kzalloc(sizeof(*(control->reg_19)), |
| GFP_KERNEL); |
| if (!control->reg_19) |
| goto exit_no_mem; |
| control->reg_19->length = length; |
| control->reg_19->data = kzalloc(length * |
| sizeof(*(control->reg_19->data)), GFP_KERNEL); |
| if (!control->reg_19->data) |
| goto exit_no_mem; |
| control->reg_19->address = reg_addr; |
| reg_addr += length; |
| } |
| reg_num++; |
| |
| /* control 20 */ |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_20 = kzalloc(sizeof(*(control->reg_20)), |
| GFP_KERNEL); |
| if (!control->reg_20) |
| goto exit_no_mem; |
| control->reg_20->address = reg_addr; |
| reg_addr += sizeof(control->reg_20->data); |
| reg_num++; |
| |
| /* control 21 */ |
| if (f54->query.has_sense_frequency_control == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_21 = kzalloc(sizeof(*(control->reg_21)), |
| GFP_KERNEL); |
| if (!control->reg_21) |
| goto exit_no_mem; |
| control->reg_21->address = reg_addr; |
| reg_addr += sizeof(control->reg_21->data); |
| } |
| reg_num++; |
| |
| /* controls 22 23 24 25 26 */ |
| if (f54->query.has_firmware_noise_mitigation == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_22__26 = kzalloc(sizeof(*(control->reg_22__26)), |
| GFP_KERNEL); |
| if (!control->reg_22__26) |
| goto exit_no_mem; |
| control->reg_22__26->address = reg_addr; |
| reg_addr += sizeof(control->reg_22__26->data); |
| } |
| reg_num++; |
| |
| /* control 27 */ |
| if (f54->query.has_iir_filter == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_27 = kzalloc(sizeof(*(control->reg_27)), |
| GFP_KERNEL); |
| if (!control->reg_27) |
| goto exit_no_mem; |
| control->reg_27->address = reg_addr; |
| reg_addr += sizeof(control->reg_27->data); |
| } |
| reg_num++; |
| |
| /* control 28 */ |
| if (f54->query.has_firmware_noise_mitigation == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_28 = kzalloc(sizeof(*(control->reg_28)), |
| GFP_KERNEL); |
| if (!control->reg_28) |
| goto exit_no_mem; |
| control->reg_28->address = reg_addr; |
| reg_addr += sizeof(control->reg_28->data); |
| } |
| reg_num++; |
| |
| /* control 29 */ |
| if (f54->query.has_cmn_removal == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_29 = kzalloc(sizeof(*(control->reg_29)), |
| GFP_KERNEL); |
| if (!control->reg_29) |
| goto exit_no_mem; |
| control->reg_29->address = reg_addr; |
| reg_addr += sizeof(control->reg_29->data); |
| } |
| reg_num++; |
| |
| /* control 30 */ |
| if (f54->query.has_cmn_maximum == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_30 = kzalloc(sizeof(*(control->reg_30)), |
| GFP_KERNEL); |
| if (!control->reg_30) |
| goto exit_no_mem; |
| control->reg_30->address = reg_addr; |
| reg_addr += sizeof(control->reg_30->data); |
| } |
| reg_num++; |
| |
| /* control 31 */ |
| if (f54->query.has_touch_hysteresis == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_31 = kzalloc(sizeof(*(control->reg_31)), |
| GFP_KERNEL); |
| if (!control->reg_31) |
| goto exit_no_mem; |
| control->reg_31->address = reg_addr; |
| reg_addr += sizeof(control->reg_31->data); |
| } |
| reg_num++; |
| |
| /* controls 32 33 34 35 */ |
| if (f54->query.has_edge_compensation == 1) { |
| attrs_ctrl_regs_exist[reg_num] = true; |
| control->reg_32__35 = kzalloc(sizeof(*(control->reg_32__35)), |
| GFP_KERNEL); |
| if (!control->reg_32__35) |
| goto exit_no_mem; |
| control->reg_32__35->address = reg_addr; |
| reg_addr += sizeof(control->reg_32__35->data); |
| } |
| reg_num++; |
| |
| /* 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(control->reg_38->length * |
| sizeof(*(control->reg_38->data)), GFP_KERNEL); |
| if (!control->reg_38->data) |
| goto exit_no_mem; |
| control->reg_38->address = reg_addr; |
| reg_addr += control->reg_38->length; |
| |
| 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(control->reg_39->length * |
| sizeof(*(control->reg_39->data)), GFP_KERNEL); |
| if (!control->reg_39->data) |
| goto exit_no_mem; |
| control->reg_39->address = reg_addr; |
| reg_addr += control->reg_39->length; |
| |
| 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(control->reg_40->length * |
| sizeof(*(control->reg_40->data)), GFP_KERNEL); |
| if (!control->reg_40->data) |
| goto exit_no_mem; |
| control->reg_40->address = reg_addr; |
| reg_addr += control->reg_40->length; |
| } |
| reg_num++; |
| |
| 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 void synaptics_rmi4_f54_sensor_mapping(void) |
| { |
| 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; |
| struct f55_query query; |
| 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) { |
| retval = f54->fn_ptr->read(rmi4_data, |
| f54->fn55->query_base_addr, |
| query.data, |
| sizeof(query.data)); |
| if (retval < 0) { |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Failed to read query registers\n", |
| __func__); |
| return; |
| } |
| |
| if (query.has_sensor_assignment) { |
| rx_len = query.num_of_rx_electrodes; |
| tx_len = query.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\n" \ |
| "\n\tRx mapped count %d(%d)\n\tTx \ |
| mapped count %d(%d)\n\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 synaptics_rmi4_data *rmi4_data = f54->rmi4_data; |
| |
| if (f54->status != STATUS_BUSY) |
| return; |
| |
| set_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; |
| } |
| |
| retval = STATUS_IDLE; |
| |
| #ifdef RAW_HEX |
| print_raw_hex_report(); |
| #endif |
| |
| #ifdef HUMAN_READABLE |
| print_image_report(); |
| #endif |
| |
| error_exit: |
| mutex_lock(&f54->status_mutex); |
| set_interrupt(false); |
| wake_unlock(&f54->test_wake_lock); |
| f54->status = retval; |
| mutex_unlock(&f54->status_mutex); |
| |
| return; |
| } |
| |
| static void synaptics_rmi4_f54_attn(struct synaptics_rmi4_data *rmi4_data, |
| unsigned char intr_mask) |
| { |
| if (f54->intr_mask & intr_mask) { |
| queue_delayed_work(f54->status_workqueue, |
| &f54->status_work, |
| msecs_to_jiffies(STATUS_WORK_INTERVAL)); |
| } |
| |
| return; |
| } |
| |
| 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; |
| |
| f54 = kzalloc(sizeof(*f54), GFP_KERNEL); |
| if (!f54) { |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Failed to alloc mem for f54\n", |
| __func__); |
| retval = -ENOMEM; |
| goto exit; |
| } |
| |
| 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); |
| f54->control_base_addr = |
| rmi_fd.ctrl_base_addr | (page << 8); |
| f54->data_base_addr = |
| rmi_fd.data_base_addr | (page << 8); |
| f54->command_base_addr = |
| rmi_fd.cmd_base_addr | (page << 8); |
| } 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 = 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 exit_free_mem; |
| } |
| |
| retval = synaptics_rmi4_f54_set_ctrl(); |
| 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(); |
| |
| mutex_init(&f54->status_mutex); |
| mutex_init(&f54->data_mutex); |
| mutex_init(&f54->control_mutex); |
| |
| retval = synaptics_rmi4_f54_set_sysfs(); |
| if (retval < 0) { |
| dev_err(&rmi4_data->i2c_client->dev, |
| "%s: Failed to create sysfs entries\n", |
| __func__); |
| goto exit_sysfs; |
| } |
| |
| f54->status_workqueue = |
| create_singlethread_workqueue("f54_status_workqueue"); |
| 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; |
| |
| wake_lock_init(&f54->test_wake_lock, WAKE_LOCK_SUSPEND, |
| "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 |
| |
| return 0; |
| |
| exit_sysfs: |
| exit_free_control: |
| free_control_mem(); |
| |
| exit_free_mem: |
| kfree(f54->fn_ptr); |
| |
| exit_free_f54: |
| kfree(f54); |
| |
| exit: |
| return retval; |
| } |
| |
| static void synaptics_rmi4_f54_remove(struct synaptics_rmi4_data *rmi4_data) |
| { |
| #ifdef WATCHDOG_HRTIMER |
| hrtimer_cancel(&f54->watchdog); |
| #endif |
| |
| cancel_delayed_work_sync(&f54->status_work); |
| flush_workqueue(f54->status_workqueue); |
| destroy_workqueue(f54->status_workqueue); |
| |
| remove_sysfs(); |
| |
| free_control_mem(); |
| |
| kfree(f54->report_data); |
| kfree(f54->fn55); |
| kfree(f54->fn_ptr); |
| kfree(f54); |
| |
| complete(&remove_complete); |
| |
| return; |
| } |
| |
| static int __init rmi4_f54_module_init(void) |
| { |
| synaptics_rmi4_new_function(RMI_F54, true, |
| synaptics_rmi4_f54_init, |
| synaptics_rmi4_f54_remove, |
| synaptics_rmi4_f54_attn, |
| IC_MODE_UI); |
| |
| return 0; |
| } |
| |
| static void __exit rmi4_f54_module_exit(void) |
| { |
| init_completion(&remove_complete); |
| synaptics_rmi4_new_function(RMI_F54, false, |
| synaptics_rmi4_f54_init, |
| synaptics_rmi4_f54_remove, |
| synaptics_rmi4_f54_attn, |
| IC_MODE_UI); |
| |
| wait_for_completion(&remove_complete); |
| 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); |