From patchwork Fri Aug 17 22:17:57 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christopher Heiny X-Patchwork-Id: 1339951 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 7F42D40212 for ; Fri, 17 Aug 2012 23:26:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759118Ab2HQXWV (ORCPT ); Fri, 17 Aug 2012 19:22:21 -0400 Received: from [12.239.217.82] ([12.239.217.82]:28612 "EHLO venom.synaptics.com" rhost-flags-FAIL-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1758925Ab2HQXVL (ORCPT ); Fri, 17 Aug 2012 19:21:11 -0400 X-Greylist: delayed 3724 seconds by postgrey-1.27 at vger.kernel.org; Fri, 17 Aug 2012 19:20:45 EDT Received: from venom.synaptics.com (venom.synaptics.com [127.0.0.1]) by venom.synaptics.com (8.14.4/8.14.4) with ESMTP id q7HMI0WK016258; Fri, 17 Aug 2012 15:18:08 -0700 From: Christopher Heiny To: Dmitry Torokhov Cc: Jean Delvare , Linux Kernel , Linux Input , Christopher Heiny , Allie Xiong , William Manson , Peichen Chang , Joerie de Gram , Wolfram Sang , Mathieu Poirier , Linus Walleij , Naveen Kumar Gaddipati Subject: [RFC PATCH 17/17] input: RMI4 F54 analog data reporting Date: Fri, 17 Aug 2012 15:17:57 -0700 Message-Id: <1345241877-16200-18-git-send-email-cheiny@synaptics.com> X-Mailer: git-send-email 1.7.4.4 In-Reply-To: <1345241877-16200-1-git-send-email-cheiny@synaptics.com> References: <1345241877-16200-1-git-send-email-cheiny@synaptics.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Signed-off-by: Christopher Heiny Cc: Dmitry Torokhov Cc: Linus Walleij Cc: Naveen Kumar Gaddipati Cc: Joeri de Gram Acked-by: Jean Delvare --- drivers/input/rmi4/rmi_f54.c | 2247 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 2247 insertions(+), 0 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c new file mode 100644 index 0000000..9c19799 --- /dev/null +++ b/drivers/input/rmi4/rmi_f54.c @@ -0,0 +1,2247 @@ +/* + * Copyright (c) 2011-2012 Synaptics Incorporated + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include + +#define FUNCTION_DATA rmi_fn_54_data +#define FNUM 54 + +#include "rmi_driver.h" + +/* Set this to 1 for raw hex dump of returned data. */ +#define RAW_HEX 0 +/* Set this to 1 for human readable dump of returned data. */ +#define HUMAN_READABLE 0 +/* The watchdog timer can be useful when debugging certain firmware related + * issues. + */ +#define F54_WATCHDOG 1 + +/* define fn $54 commands */ +#define GET_REPORT 1 +#define FORCE_CAL 2 + +#define NO_AUTO_CAL_MASK 1 +/* status */ +#define BUSY 1 +#define IDLE 0 + +/* Offsets for data */ +#define RMI_F54_REPORT_DATA_OFFSET 3 +#define RMI_F54_FIFO_OFFSET 1 +#define RMI_F54_NUM_TX_OFFSET 1 +#define RMI_F54_NUM_RX_OFFSET 0 + +/* Fixed sizes of reports */ +#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4 +#define RMI_54_HIGH_RESISTANCE_SIZE 6 + +/* definitions for F54 Query Registers */ +union f54_ad_query { + struct { + /* query 0 */ + u8 num_of_rx_electrodes; + + /* query 1 */ + u8 num_of_tx_electrodes; + + /* query2 */ + u8 f54_ad_query2_b0__1:2; + u8 has_baseline:1; + u8 has_image8:1; + u8 f54_ad_query2_b4__5:2; + u8 has_image16:1; + u8 f54_ad_query2_b7:1; + + /* query 3.0 and 3.1 */ + u16 clock_rate; + + /* query 4 */ + u8 touch_controller_family; + + /* query 5 */ + u8 has_pixel_touch_threshold_adjustment:1; + u8 f54_ad_query5_b1__7:7; + + /* query 6 */ + u8 has_sensor_assignment:1; + u8 has_interference_metric:1; + u8 has_sense_frequency_control:1; + u8 has_firmware_noise_mitigation:1; + u8 f54_ad_query6_b4:1; + u8 has_two_byte_report_rate:1; + u8 has_one_byte_report_rate:1; + u8 has_relaxation_control:1; + + /* query 7 */ + u8 curve_compensation_mode:2; + u8 f54_ad_query7_b2__7:6; + + /* query 8 */ + u8 f54_ad_query2_b0:1; + u8 has_iir_filter:1; + u8 has_cmn_removal:1; + u8 has_cmn_maximum:1; + u8 has_touch_hysteresis:1; + u8 has_edge_compensation:1; + u8 has_per_frequency_noise_control:1; + u8 f54_ad_query8_b7:1; + + u8 f54_ad_query9; + u8 f54_ad_query10; + + /* query 11 */ + u8 f54_ad_query11_b0__6:7; + u8 has_query_15:1; + + /* query 12 */ + u8 number_of_sensing_frequencies:4; + u8 f54_ad_query12_b4__7:4; + } __attribute__((__packed__)); + struct { + u8 regs[14]; + u16 address; + } __attribute__((__packed__)); +}; + +/* And now for the very large amount of control registers */ + +/* Ctrl registers */ + +union f54_ad_control_0 { + /* control 0 */ + struct { + u8 no_relax:1; + u8 no_scan:1; + u8 f54_ad_ctrl0_b2__7:6; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_1 { + /* control 1 */ + struct { + /* control 1 */ + u8 bursts_per_cluster:4; + u8 f54_ad_ctrl1_b4__7:4; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_2 { + /* control 2 */ + struct { + u16 saturation_cap; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_3 { + /* control 3 */ + struct { + u16 pixel_touch_threshold; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_4__6 { + struct { + /* control 4 */ + u8 rx_feedback_cap:2; + u8 f54_ad_ctrl4_b2__7:6; + + /* control 5 */ + u8 low_ref_cap:2; + u8 low_ref_feedback_cap:2; + u8 low_ref_polarity:1; + u8 f54_ad_ctrl5_b5__7:3; + + /* control 6 */ + u8 high_ref_cap:2; + u8 high_ref_feedback_cap:2; + u8 high_ref_polarity:1; + u8 f54_ad_ctrl6_b5__7:3; + } __attribute__((__packed__)); + struct { + u8 regs[3]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_7 { + struct { + /* control 7 */ + u8 cbc_cap:2; + u8 cbc_polarity:2; + u8 cbc_tx_carrier_selection:1; + u8 f54_ad_ctrl6_b5__7:3; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_8__9 { + struct { + /* control 8 */ + u16 integration_duration:10; + u16 f54_ad_ctrl8_b10__15:6; + /* control 9 */ + u8 reset_duration; + } __attribute__((__packed__)); + struct { + u8 regs[3]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_10 { + struct { + /* control 10 */ + u8 noise_sensing_bursts_per_image:4; + u8 f54_ad_ctrl10_b4__7:4; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_11 { + struct { + /* control 11 */ + u8 reserved; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_12__13 { + struct { + /* control 12 */ + u8 slow_relaxation_rate; + + /* control 13 */ + u8 fast_relaxation_rate; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_14 { + struct { + /* control 14 */ + u8 rxs_on_xaxis:1; + u8 curve_comp_on_txs:1; + u8 f54_ad_ctrl14b2__7:6; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +struct f54_ad_control_15n { + /*Control 15.* */ + u8 sensor_rx_assignment; +}; + +struct f54_ad_control_15 { + struct f54_ad_control_15n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_16n { + /*Control 16.* */ + u8 sensor_tx_assignment; +}; + +struct f54_ad_control_16 { + struct f54_ad_control_16n *regs; + u16 address; + u8 length; +}; + + +/* control 17 */ +struct f54_ad_control_17n { + u8 burst_countb10__8:3; + u8 disable:1; + u8 f54_ad_ctrlb4:1; + u8 filter_bandwidth:3; +}; + +struct f54_ad_control_17 { + struct f54_ad_control_17n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_18n { + /*Control 18.* */ + u8 burst_countb7__0n; +}; + +struct f54_ad_control_18 { + struct f54_ad_control_18n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_19n { + /*Control 19.* */ + u8 stretch_duration; +}; + +struct f54_ad_control_19 { + struct f54_ad_control_19n *regs; + u16 address; + u8 length; +}; + +union f54_ad_control_20 { + struct { + /* control 20 */ + u8 disable_noise_mitigation:1; + u8 f54_ad_ctrl20b2__7:7; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_21 { + struct { + /* control 21 */ + u16 freq_shift_noise_threshold; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_22__26 { + struct { + /* control 22 */ + /* u8 noise_density_threshold; */ + u8 f54_ad_ctrl22; + + /* control 23 */ + u16 medium_noise_threshold; + + /* control 24 */ + u16 high_noise_threshold; + + /* control 25 */ + u8 noise_density; + + /* control 26 */ + u8 frame_count; + } __attribute__((__packed__)); + struct { + u8 regs[7]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_27 { + struct { + /* control 27 */ + u8 iir_filter_coef; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_28 { + struct { + /* control 28 */ + u16 quiet_threshold; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + } __attribute__((__packed__)); +}; + + +union f54_ad_control_29 { + struct { + /* control 29 */ + u8 f54_ad_ctrl20b0__6:7; + u8 cmn_filter_disable:1; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_30 { + struct { + /* control 30 */ + u8 cmn_filter_max; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_31 { + struct { + /* control 31 */ + u8 touch_hysteresis; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f54_ad_control_32__35 { + struct { + /* control 32 */ + u16 rx_low_edge_comp; + + /* control 33 */ + u16 rx_high_edge_comp; + + /* control 34 */ + u16 tx_low_edge_comp; + + /* control 35 */ + u16 tx_high_edge_comp; + } __attribute__((__packed__)); + struct { + u8 regs[8]; + u16 address; + } __attribute__((__packed__)); +}; + +struct f54_ad_control_36n { + /*Control 36.* */ + u8 axis1_comp; +}; + +struct f54_ad_control_36 { + struct f54_ad_control_36n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_37n { + /*Control 37.* */ + u8 axis2_comp; +}; + +struct f54_ad_control_37 { + struct f54_ad_control_37n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_38n { + /*Control 38.* */ + u8 noise_control_1; +}; + +struct f54_ad_control_38 { + struct f54_ad_control_38n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_39n { + /*Control 39.* */ + u8 noise_control_2; +}; + +struct f54_ad_control_39 { + struct f54_ad_control_39n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control_40n { + /*Control 40.* */ + u8 noise_control_3; +}; + +struct f54_ad_control_40 { + struct f54_ad_control_40n *regs; + u16 address; + u8 length; +}; + +struct f54_ad_control { + union f54_ad_control_0 *reg_0; + union f54_ad_control_1 *reg_1; + union f54_ad_control_2 *reg_2; + union f54_ad_control_3 *reg_3; + union f54_ad_control_4__6 *reg_4__6; + union f54_ad_control_7 *reg_7; + union f54_ad_control_8__9 *reg_8__9; + union f54_ad_control_10 *reg_10; + union f54_ad_control_11 *reg_11; + union f54_ad_control_12__13 *reg_12__13; + union f54_ad_control_14 *reg_14; + /* control 15 */ + struct f54_ad_control_15 *reg_15; + /* control 16 */ + struct f54_ad_control_16 *reg_16; + + /* This register is n repetitions of f54_ad_control_17 */ + struct f54_ad_control_17 *reg_17; + + /* control 18 */ + struct f54_ad_control_18 *reg_18; + + /* control 19 */ + struct f54_ad_control_19 *reg_19; + + union f54_ad_control_20 *reg_20; + union f54_ad_control_21 *reg_21; + union f54_ad_control_22__26 *reg_22__26; + union f54_ad_control_27 *reg_27; + union f54_ad_control_28 *reg_28; + union f54_ad_control_29 *reg_29; + union f54_ad_control_30 *reg_30; + union f54_ad_control_31 *reg_31; + union f54_ad_control_32__35 *reg_32__35; + /* control 36 */ + struct f54_ad_control_36 *reg_36; + + /* control 37 */ + struct f54_ad_control_37 *reg_37; + + /* control 38 */ + struct f54_ad_control_38 *reg_38; + + /* control 39 */ + struct f54_ad_control_39 *reg_39; + + /* control 40 */ + struct f54_ad_control_40 *reg_40; +}; + +/* define report types */ +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 +}; + +/* sysfs functions */ +show_store_union_struct_prototype(report_type) + +store_union_struct_prototype(get_report) + +store_union_struct_prototype(force_cal) + +show_union_struct_prototype(status) + +static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +show_union_struct_prototype(num_of_rx_electrodes) +show_union_struct_prototype(num_of_tx_electrodes) +show_union_struct_prototype(has_image16) +show_union_struct_prototype(has_image8) +show_union_struct_prototype(has_baseline) +show_union_struct_prototype(clock_rate) +show_union_struct_prototype(touch_controller_family) +show_union_struct_prototype(has_pixel_touch_threshold_adjustment) +show_union_struct_prototype(has_sensor_assignment) +show_union_struct_prototype(has_interference_metric) +show_union_struct_prototype(has_sense_frequency_control) +show_union_struct_prototype(has_firmware_noise_mitigation) +show_union_struct_prototype(has_two_byte_report_rate) +show_union_struct_prototype(has_one_byte_report_rate) +show_union_struct_prototype(has_relaxation_control) +show_union_struct_prototype(curve_compensation_mode) +show_union_struct_prototype(has_iir_filter) +show_union_struct_prototype(has_cmn_removal) +show_union_struct_prototype(has_cmn_maximum) +show_union_struct_prototype(has_touch_hysteresis) +show_union_struct_prototype(has_edge_compensation) +show_union_struct_prototype(has_per_frequency_noise_control) +show_union_struct_prototype(number_of_sensing_frequencies) +show_store_union_struct_prototype(no_auto_cal) +show_store_union_struct_prototype(fifoindex) + +/* Repeated Control Registers */ +show_union_struct_prototype(sensor_rx_assignment) +show_union_struct_prototype(sensor_tx_assignment) +show_union_struct_prototype(filter_bandwidth) +show_union_struct_prototype(disable) +show_union_struct_prototype(burst_count) +show_union_struct_prototype(stretch_duration) +show_store_union_struct_prototype(axis1_comp) +show_store_union_struct_prototype(axis2_comp) +show_union_struct_prototype(noise_control_1) +show_union_struct_prototype(noise_control_2) +show_union_struct_prototype(noise_control_3) + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +show_store_union_struct_prototype(no_relax) +show_store_union_struct_prototype(no_scan) +show_store_union_struct_prototype(bursts_per_cluster) +show_store_union_struct_prototype(saturation_cap) +show_store_union_struct_prototype(pixel_touch_threshold) +show_store_union_struct_prototype(rx_feedback_cap) +show_store_union_struct_prototype(low_ref_cap) +show_store_union_struct_prototype(low_ref_feedback_cap) +show_store_union_struct_prototype(low_ref_polarity) +show_store_union_struct_prototype(high_ref_cap) +show_store_union_struct_prototype(high_ref_feedback_cap) +show_store_union_struct_prototype(high_ref_polarity) +show_store_union_struct_prototype(cbc_cap) +show_store_union_struct_prototype(cbc_polarity) +show_store_union_struct_prototype(cbc_tx_carrier_selection) +show_store_union_struct_prototype(integration_duration) +show_store_union_struct_prototype(reset_duration) +show_store_union_struct_prototype(noise_sensing_bursts_per_image) +show_store_union_struct_prototype(slow_relaxation_rate) +show_store_union_struct_prototype(fast_relaxation_rate) +show_store_union_struct_prototype(rxs_on_xaxis) +show_store_union_struct_prototype(curve_comp_on_txs) +show_store_union_struct_prototype(disable_noise_mitigation) +show_store_union_struct_prototype(freq_shift_noise_threshold) +/*show_store_union_struct_prototype(noise_density_threshold)*/ +show_store_union_struct_prototype(medium_noise_threshold) +show_store_union_struct_prototype(high_noise_threshold) +show_store_union_struct_prototype(noise_density) +show_store_union_struct_prototype(frame_count) +show_store_union_struct_prototype(iir_filter_coef) +show_store_union_struct_prototype(quiet_threshold) +show_store_union_struct_prototype(cmn_filter_disable) +show_store_union_struct_prototype(cmn_filter_max) +show_store_union_struct_prototype(touch_hysteresis) +show_store_union_struct_prototype(rx_low_edge_comp) +show_store_union_struct_prototype(rx_high_edge_comp) +show_store_union_struct_prototype(tx_low_edge_comp) +show_store_union_struct_prototype(tx_high_edge_comp) + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +static struct attribute *attrs[] = { + attrify(report_type), + attrify(get_report), + attrify(force_cal), + attrify(status), + 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), + attrify(no_auto_cal), + attrify(fifoindex), + NULL +}; + +static struct attribute_group attrs_query = GROUP(attrs); + +struct bin_attribute dev_rep_data = { + .attr = { + .name = "rep_data", + .mode = RMI_RO_ATTR}, + .size = 0, + .read = rmi_fn_54_data_read, +}; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +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_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(filter_bandwidth), + attrify(disable), + attrify(burst_count), + 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(noise_density_threshold),*/ + 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_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) +}; + +/* data specific to fn $54 that needs to be kept around */ +struct rmi_fn_54_data { + union f54_ad_query query; + struct f54_ad_control control; + bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)]; + enum f54_report_types report_type; + u16 fifoindex; + signed char status; + bool no_auto_cal; + unsigned int report_size; + unsigned char *report_data; + unsigned int bufsize; + struct mutex data_mutex; + struct mutex status_mutex; + struct mutex control_mutex; +#if F54_WATCHDOG + struct hrtimer watchdog; +#endif + struct rmi_function_container *fc; + struct work_struct work; +}; + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +#if F54_WATCHDOG +static enum hrtimer_restart clear_status(struct hrtimer *timer); + +static void clear_status_worker(struct work_struct *work); +#endif + +static int rmi_f54_alloc_memory(struct rmi_function_container *fc); + +static void rmi_f54_free_memory(struct rmi_function_container *fc); + +static int rmi_f54_initialize(struct rmi_function_container *fc); + +static int rmi_f54_reset(struct rmi_function_container *fc); + +static int rmi_f54_create_sysfs(struct rmi_function_container *fc); + +static int rmi_f54_init(struct rmi_function_container *fc) +{ + int retval = 0; + struct rmi_fn_54_data *f54; + + retval = rmi_f54_alloc_memory(fc); + if (retval < 0) + goto error_exit; + + retval = rmi_f54_initialize(fc); + if (retval < 0) + goto error_exit; + + retval = rmi_f54_create_sysfs(fc); + if (retval < 0) + goto error_exit; + f54 = fc->data; + f54->status = IDLE; + return retval; + +error_exit: + rmi_f54_free_memory(fc); + + return retval; +} + +static int rmi_f54_alloc_memory(struct rmi_function_container *fc) +{ + struct rmi_fn_54_data *f54; + + f54 = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL); + if (!f54) { + dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n"); + return -ENOMEM; + } + fc->data = f54; + f54->fc = fc; + + + return 0; +} + +static void rmi_f54_free_memory(struct rmi_function_container *fc) +{ + int reg_num; + struct rmi_fn_54_data *f54 = fc->data; + sysfs_remove_group(&fc->dev.kobj, &attrs_query); + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) + sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl_regs[reg_num]); + sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data); + if (f54) { + kfree(f54->report_data); + kfree(f54); + fc->data = NULL; + } +} + +static int rmi_f54_reset(struct rmi_function_container *fc) +{ + struct rmi_fn_54_data *data = fc->data; + struct rmi_driver *driver = fc->rmi_dev->driver; + +#if F54_WATCHDOG + hrtimer_cancel(&data->watchdog); +#endif + + mutex_lock(&data->status_mutex); + if (driver->restore_irq_mask) { + dev_dbg(&fc->dev, "Restoring interupts!\n"); + driver->restore_irq_mask(fc->rmi_dev); + } else { + dev_err(&fc->dev, "No way to restore interrupts!\n"); + } + data->status = -ECONNRESET; + mutex_unlock(&data->status_mutex); + + return 0; +} + +static void rmi_f54_remove(struct rmi_function_container *fc) +{ + struct rmi_fn_54_data *data = fc->data; + + dev_info(&fc->dev, "Removing F54."); + + #if F54_WATCHDOG + /* Stop timer */ + hrtimer_cancel(&data->watchdog); + #endif + + rmi_f54_free_memory(fc); +} + +static int rmi_f54_create_sysfs(struct rmi_function_container *fc) +{ + int reg_num; + int retval; + struct rmi_fn_54_data *f54 = fc->data; + dev_dbg(&fc->dev, "Creating sysfs files."); + /* Set up sysfs device attributes. */ + + if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) { + dev_err(&fc->dev, "Failed to create query sysfs files."); + return -ENODEV; + } + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) { + if (f54->attrs_ctrl_regs_exist[reg_num]) { + retval = sysfs_create_group(&fc->dev.kobj, + &attrs_ctrl_regs[reg_num]); + if (retval < 0) { + dev_err(&fc->dev, "Failed to create sysfs file group for reg group %d, error = %d.", + reg_num, retval); + return -ENODEV; + } + } + } + + /* Binary sysfs file to report the data back */ + retval = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data); + if (retval < 0) { + dev_err(&fc->dev, "Failed to create sysfs file for F54 data (error = %d).\n", + retval); + return -ENODEV; + } + return 0; +} + + + +static int rmi_f54_initialize(struct rmi_function_container *fc) +{ + struct rmi_fn_54_data *f54 = fc->data; + struct f54_ad_control *control; + int retval = 0; + u8 size = 0; + u16 next_loc; + u8 reg_num; + +#if F54_WATCHDOG + /* Set up watchdog timer to catch unanswered get_report commands */ + hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + f54->watchdog.function = clear_status; + + /* work function to do unlocking */ + INIT_WORK(&f54->work, clear_status_worker); +#endif + + /* Read F54 Query Data */ + retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr, + (u8 *)&f54->query, sizeof(f54->query)); + if (retval < 0) { + dev_err(&fc->dev, "Could not read query registers from 0x%04x\n", + fc->fd.query_base_addr); + return retval; + } + + /* Initialize the control registers */ + next_loc = fc->fd.control_base_addr; + reg_num = 0; + control = &f54->control; + + f54->attrs_ctrl_regs_exist[reg_num] = true; + reg_num++; + control->reg_0 = kzalloc(sizeof(union f54_ad_control_0), GFP_KERNEL); + if (!control->reg_0) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_0->address = next_loc; + next_loc += sizeof(control->reg_0->regs); + + if (f54->query.touch_controller_family == 0 + || f54->query.touch_controller_family == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_1 = kzalloc(sizeof(union f54_ad_control_1), + GFP_KERNEL); + if (!control->reg_1) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_1->address = next_loc; + next_loc += sizeof(control->reg_1->regs); + } + reg_num++; + + f54->attrs_ctrl_regs_exist[reg_num] = true; + reg_num++; + control->reg_2 = kzalloc(sizeof(union f54_ad_control_2), GFP_KERNEL); + if (!control->reg_2) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_2->address = next_loc; + next_loc += sizeof(control->reg_2->regs); + + if (f54->query.has_pixel_touch_threshold_adjustment == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_3 = kzalloc(sizeof(union f54_ad_control_3), + GFP_KERNEL); + if (!control->reg_3) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_3->address = next_loc; + next_loc += sizeof(control->reg_3->regs); + } + reg_num++; + + if (f54->query.touch_controller_family == 0 + || f54->query.touch_controller_family == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_4__6 = kzalloc(sizeof(union f54_ad_control_4__6), + GFP_KERNEL); + if (!control->reg_4__6) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_4__6->address = next_loc; + next_loc += sizeof(control->reg_4__6->regs); + } + reg_num++; + + if (f54->query.touch_controller_family == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_7 = kzalloc(sizeof(union f54_ad_control_7), + GFP_KERNEL); + if (!control->reg_7) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_7->address = next_loc; + next_loc += sizeof(control->reg_7->regs); + } + reg_num++; + + if (f54->query.touch_controller_family == 0 + || f54->query.touch_controller_family == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_8__9 = kzalloc(sizeof(union f54_ad_control_8__9), + GFP_KERNEL); + if (!control->reg_8__9) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_8__9->address = next_loc; + next_loc += sizeof(control->reg_8__9->regs); + } + reg_num++; + + if (f54->query.has_interference_metric == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_10 = kzalloc(sizeof(union f54_ad_control_10), + GFP_KERNEL); + if (!control->reg_10) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_10->address = next_loc; + next_loc += sizeof(control->reg_10->regs); + } + reg_num++; + + /* F54 Control Register 11 is reserved */ + next_loc++; + + if (f54->query.has_relaxation_control == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_12__13 = kzalloc( + sizeof(union f54_ad_control_12__13), GFP_KERNEL); + if (!control->reg_12__13) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_12__13->address = next_loc; + next_loc += sizeof(control->reg_12__13->regs); + } + reg_num++; + + if (f54->query.has_sensor_assignment == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_14 = kzalloc(sizeof(union f54_ad_control_14), + GFP_KERNEL); + if (!control->reg_14) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_15 = + kzalloc(sizeof(struct f54_ad_control_15), GFP_KERNEL); + if (!control->reg_15) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_15->length = f54->query.num_of_rx_electrodes; + control->reg_15->regs = + kzalloc(control->reg_15->length * + sizeof(struct f54_ad_control_15n), + GFP_KERNEL); + if (!control->reg_15->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_16 = + kzalloc(sizeof(struct f54_ad_control_16), GFP_KERNEL); + if (!control->reg_16) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_16->length = f54->query.num_of_tx_electrodes; + control->reg_16->regs = + kzalloc(control->reg_16->length * + sizeof(struct f54_ad_control_16n), + GFP_KERNEL); + if (!control->reg_16->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_14->address = next_loc; + next_loc += sizeof(control->reg_14->regs); + control->reg_15->address = next_loc; + next_loc += control->reg_15->length; + control->reg_16->address = next_loc; + next_loc += control->reg_16->length; + } + reg_num++; + + if (f54->query.has_sense_frequency_control == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + size = f54->query.number_of_sensing_frequencies; + + control->reg_17 = + kzalloc(sizeof(struct f54_ad_control_17), GFP_KERNEL); + if (!control->reg_17) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_17->length = size; + control->reg_17->regs = kzalloc(size * + sizeof(struct f54_ad_control_17n), GFP_KERNEL); + if (!control->reg_17->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_18 = + kzalloc(sizeof(struct f54_ad_control_18), GFP_KERNEL); + if (!control->reg_18) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_18->length = size; + control->reg_18->regs = kzalloc(size * + sizeof(struct f54_ad_control_18n), GFP_KERNEL); + if (!control->reg_18->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_19 = + kzalloc(sizeof(struct f54_ad_control_19), GFP_KERNEL); + if (!control->reg_19) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_19->length = size; + control->reg_19->regs = kzalloc(size * + sizeof(struct f54_ad_control_19n), GFP_KERNEL); + if (!control->reg_19->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_17->address = next_loc; + next_loc += size; + control->reg_18->address = next_loc; + next_loc += size; + control->reg_19->address = next_loc; + next_loc += size; + } + reg_num++; + + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_20 = kzalloc(sizeof(union f54_ad_control_20), GFP_KERNEL); + control->reg_20->address = next_loc; + next_loc += sizeof(control->reg_20->regs); + reg_num++; + + if (f54->query.has_sense_frequency_control == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_21 = kzalloc(sizeof(union f54_ad_control_21), + GFP_KERNEL); + control->reg_21->address = next_loc; + next_loc += sizeof(control->reg_21->regs); + } + reg_num++; + + if (f54->query.has_sense_frequency_control == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_22__26 = kzalloc( + sizeof(union f54_ad_control_22__26), GFP_KERNEL); + control->reg_22__26->address = next_loc; + next_loc += sizeof(control->reg_22__26->regs); + } + reg_num++; + + if (f54->query.has_iir_filter == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_27 = kzalloc(sizeof(union f54_ad_control_27), + GFP_KERNEL); + control->reg_27->address = next_loc; + next_loc += sizeof(control->reg_27->regs); + } + reg_num++; + + if (f54->query.has_firmware_noise_mitigation == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_28 = kzalloc(sizeof(union f54_ad_control_28), + GFP_KERNEL); + control->reg_28->address = next_loc; + next_loc += sizeof(control->reg_28->regs); + } + reg_num++; + + if (f54->query.has_cmn_removal == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_29 = kzalloc(sizeof(union f54_ad_control_29), + GFP_KERNEL); + control->reg_29->address = next_loc; + next_loc += sizeof(control->reg_29->regs); + } + reg_num++; + + if (f54->query.has_cmn_maximum == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_30 = kzalloc(sizeof(union f54_ad_control_30), + GFP_KERNEL); + control->reg_30->address = next_loc; + next_loc += sizeof(control->reg_30->regs); + } + reg_num++; + + if (f54->query.has_touch_hysteresis == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_31 = kzalloc(sizeof(union f54_ad_control_31), + GFP_KERNEL); + control->reg_31->address = next_loc; + next_loc += sizeof(control->reg_31->regs); + } + reg_num++; + + if (f54->query.has_interference_metric == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + control->reg_32__35 = kzalloc( + sizeof(union f54_ad_control_32__35), GFP_KERNEL); + control->reg_32__35->address = next_loc; + next_loc += sizeof(control->reg_32__35->regs); + } + reg_num++; + + if (f54->query.curve_compensation_mode == 1) { + size = max(f54->query.num_of_rx_electrodes, + f54->query.num_of_tx_electrodes); + } + if (f54->query.curve_compensation_mode == 2) + size = f54->query.num_of_rx_electrodes; + if (f54->query.curve_compensation_mode == 1 + || f54->query.curve_compensation_mode == 2) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_36 = + kzalloc(sizeof(struct f54_ad_control_36), GFP_KERNEL); + if (!control->reg_36) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_36->length = size; + control->reg_36->regs = + kzalloc(size * sizeof(struct f54_ad_control_36n), + GFP_KERNEL); + if (!control->reg_36->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_36->address = next_loc; + next_loc += size; + } + reg_num++; + + if (f54->query.curve_compensation_mode == 2) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_37 = + kzalloc(sizeof(struct f54_ad_control_37), GFP_KERNEL); + if (!control->reg_37) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_37->length = f54->query.num_of_tx_electrodes; + control->reg_37->regs = kzalloc(control->reg_37->length * + sizeof(struct f54_ad_control_37n), + GFP_KERNEL); + if (!control->reg_37->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_37->address = next_loc; + next_loc += control->reg_37->length; + } + reg_num++; + + if (f54->query.has_per_frequency_noise_control == 1) { + f54->attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_38 = + kzalloc(sizeof(struct f54_ad_control_38), GFP_KERNEL); + if (!control->reg_38) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_38->length = + f54->query.number_of_sensing_frequencies; + control->reg_38->regs = kzalloc(control->reg_38->length * + sizeof(struct f54_ad_control_38n), + GFP_KERNEL); + if (!control->reg_38->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_39 = + kzalloc(sizeof(struct f54_ad_control_39), GFP_KERNEL); + if (!control->reg_39) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_39->length = + f54->query.number_of_sensing_frequencies; + control->reg_39->regs = kzalloc(control->reg_39->length * + sizeof(struct f54_ad_control_39n), + GFP_KERNEL); + if (!control->reg_39->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_40 = + kzalloc(sizeof(struct f54_ad_control_40), GFP_KERNEL); + if (!control->reg_40) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + control->reg_40->length = + f54->query.number_of_sensing_frequencies; + control->reg_40->regs = kzalloc(control->reg_40->length * + sizeof(struct f54_ad_control_40n), GFP_KERNEL); + if (!control->reg_40->regs) { + dev_err(&fc->dev, "Failed to allocate control registers."); + return -ENOMEM; + } + + control->reg_38->address = next_loc; + next_loc += control->reg_38->length; + control->reg_39->address = next_loc; + next_loc += control->reg_39->length; + control->reg_40->address = next_loc; + next_loc += control->reg_40->length; + } + reg_num++; + + mutex_init(&f54->data_mutex); + + mutex_init(&f54->status_mutex); + + mutex_init(&f54->control_mutex); + + return retval; +} + +static void set_report_size(struct rmi_fn_54_data *data) +{ + u8 rx = data->query.num_of_rx_electrodes; + u8 tx = data->query.num_of_tx_electrodes; + switch (data->report_type) { + case F54_8BIT_IMAGE: + data->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: + data->report_size = 2 * rx * tx; + break; + case F54_HIGH_RESISTANCE: + data->report_size = RMI_54_HIGH_RESISTANCE_SIZE; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE; + break; + case F54_TX_TO_TX_SHORT: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + data->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX1: + case F54_RX_OPENS1: + if (rx < tx) + data->report_size = 2 * rx * rx; + else + data->report_size = 2 * rx * tx; + break; + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + if (rx <= tx) + data->report_size = 0; + else + data->report_size = 2 * rx * (rx - tx); + break; + default: + data->report_size = 0; + } +} + +int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + struct rmi_driver *driver = fc->rmi_dev->driver; + char fifo[2]; + struct rmi_fn_54_data *data = fc->data; + int error = 0; + + set_report_size(data); + if (data->report_size == 0) { + dev_err(&fc->dev, "Invalid report type set in %s. " + "This should never happen.\n", __func__); + error = -EINVAL; + goto error_exit; + } + /* + * We need to ensure the buffer is big enough. A Buffer size of 0 means + * that the buffer has not been allocated. + */ + if (data->bufsize < data->report_size) { + mutex_lock(&data->data_mutex); + if (data->bufsize > 0) + kfree(data->report_data); + data->report_data = kzalloc(data->report_size, GFP_KERNEL); + if (!data->report_data) { + dev_err(&fc->dev, "Failed to allocate report_data.\n"); + error = -ENOMEM; + data->bufsize = 0; + mutex_unlock(&data->data_mutex); + goto error_exit; + } + data->bufsize = data->report_size; + mutex_unlock(&data->data_mutex); + } + dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n", + data->report_size); + /* Write 0 to fifohi and fifolo. */ + fifo[0] = 0; + fifo[1] = 0; + error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr + + RMI_F54_FIFO_OFFSET, fifo, sizeof(fifo)); + if (error < 0) + dev_err(&fc->dev, "Failed to write fifo to zero!\n"); + else + error = rmi_read_block(fc->rmi_dev, + fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET, + data->report_data, data->report_size); + if (error < 0) + dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error); + else if (error != data->report_size) { + error = -EINVAL; + goto error_exit; + } +#if RAW_HEX + int l; + /* Debugging: Print out the file in hex. */ + pr_info("Report data (raw hex):\n"); + for (l = 0; l < data->report_size; l += 2) { + pr_info("%03d: 0x%02x%02x\n", l/2, + data->report_data[l+1], data->report_data[l]); + } +#endif +#if HUMAN_READABLE + /* Debugging: Print out file in human understandable image */ + switch (data->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("Report data (Image):\n"); + int i, j, k; + char c[2]; + short s; + k = 0; + for (i = 0; i < data->query.num_of_tx_electrodes; + i++) { + for (j = 0; j < + data->query.num_of_rx_electrodes; j++) { + c[0] = data->report_data[k]; + c[1] = data->report_data[k+1]; + memcpy(&s, &c, 2); + if (s < -64) + printk("."); + else if (s < 0) + printk("-"); + else if (s > 64) + printk("*"); + else if (s > 0) + printk("+"); + else + printk("0"); + k += 2; + } + pr_info("\n"); + } + pr_info("EOF\n"); + break; + default: + pr_info("Report type %d debug image not supported", + data->report_type); + } +#endif + error = IDLE; +error_exit: + mutex_lock(&data->status_mutex); + /* Turn back on other interupts, if it + * appears that we turned them off. */ + if (driver->restore_irq_mask) { + dev_dbg(&fc->dev, "Restoring interupts!\n"); + driver->restore_irq_mask(fc->rmi_dev); + } else { + dev_err(&fc->dev, "No way to restore interrupts!\n"); + } + data->status = error; + mutex_unlock(&data->status_mutex); + return data->status; +} + + +#if F54_WATCHDOG +static void clear_status_worker(struct work_struct *work) +{ + struct rmi_fn_54_data *data = container_of(work, + struct rmi_fn_54_data, work); + struct rmi_function_container *fc = data->fc; + struct rmi_driver *driver = fc->rmi_dev->driver; + char command; + int result; + + mutex_lock(&data->status_mutex); + if (data->status == BUSY) { + result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr, + &command, 1); + if (result < 0) { + dev_err(&fc->dev, "Could not read get_report register from %#06x.\n", + fc->fd.command_base_addr); + data->status = -ETIMEDOUT; + } else { + if (command & GET_REPORT) { + dev_warn(&fc->dev, "Report type unsupported!"); + data->status = -EINVAL; + } else { + data->status = -ETIMEDOUT; + } + } + if (driver->restore_irq_mask) { + dev_dbg(&fc->dev, "Restoring interupts!\n"); + driver->restore_irq_mask(fc->rmi_dev); + } else { + dev_err(&fc->dev, "No way to restore interrupts!\n"); + } + } + mutex_unlock(&data->status_mutex); +} + +static enum hrtimer_restart clear_status(struct hrtimer *timer) +{ + struct rmi_fn_54_data *data = container_of(timer, + struct rmi_fn_54_data, watchdog); + schedule_work(&(data->work)); + return HRTIMER_NORESTART; +} +#endif + +/* Check if report_type is valid */ +static bool is_report_type_valid(enum f54_report_types reptype) +{ + /* Basic checks on report_type to ensure we write a valid type + * to the sensor. + * TODO: Check Query3 to see if some specific reports are + * available. This is currently listed as a reserved register. + */ + switch (reptype) { + 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: + return true; + break; + default: + return false; + } +} + +/* SYSFS file show/store functions */ +static ssize_t rmi_fn_54_report_type_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *f54; + + fc = to_rmi_function_container(dev); + f54 = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_type); +} + +static ssize_t rmi_fn_54_report_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + int result; + unsigned long val; + unsigned char data; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + result = strict_strtoul(buf, 10, &val); + if (result) + return result; + if (!is_report_type_valid(val)) { + dev_err(dev, "%s : Report type %d is invalid.\n", + __func__, (u8) val); + return -EINVAL; + } + mutex_lock(&instance_data->status_mutex); + if (instance_data->status != BUSY) { + instance_data->report_type = (enum f54_report_types)val; + data = (char)val; + /* Write the Report Type back to the first Block + * Data registers (F54_AD_Data0). */ + result = + rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr, + &data, 1); + mutex_unlock(&instance_data->status_mutex); + if (result < 0) { + dev_err(dev, "%s : Could not write report type to" + " 0x%x\n", __func__, fc->fd.data_base_addr); + return result; + } + return count; + } else { + dev_err(dev, "%s : Report type cannot be changed in the middle" + " of command.\n", __func__); + mutex_unlock(&instance_data->status_mutex); + return -EINVAL; + } +} + +static ssize_t rmi_fn_54_get_report_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + unsigned long val; + int error, result; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + struct rmi_driver *driver; + u8 command; + fc = to_rmi_function_container(dev); + instance_data = fc->data; + driver = fc->rmi_dev->driver; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + /* Do nothing if not set to 1. This prevents accidental commands. */ + if (val != 1) + return count; + command = (unsigned char)GET_REPORT; + /* Basic checks on report_type to ensure we write a valid type + * to the sensor. + * TODO: Check Query3 to see if some specific reports are + * available. This is currently listed as a reserved register. + */ + if (!is_report_type_valid(instance_data->report_type)) { + dev_err(dev, "%s : Report type %d is invalid.\n", + __func__, instance_data->report_type); + return -EINVAL; + } + mutex_lock(&instance_data->status_mutex); + if (instance_data->status != IDLE) { + if (instance_data->status != BUSY) { + dev_err(dev, "F54 status is in an abnormal state: 0x%x", + instance_data->status); + } + mutex_unlock(&instance_data->status_mutex); + return count; + } + /* Store interrupts */ + /* Do not exit if we fail to turn off interupts. We are likely + * to still get useful data. The report data can, however, be + * corrupted, and there may be unexpected behavior. + */ + dev_dbg(dev, "Storing and overriding interupts\n"); + if (driver->store_irq_mask) + driver->store_irq_mask(fc->rmi_dev, + fc->irq_mask); + else + dev_err(dev, "No way to store interupts!\n"); + instance_data->status = BUSY; + + /* small delay to avoid race condition in firmare. This value is a bit + * higher than absolutely necessary. Should be removed once issue is + * resolved in firmware. */ + + mdelay(2); + + /* Write the command to the command register */ + result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr, + &command, 1); + mutex_unlock(&instance_data->status_mutex); + if (result < 0) { + dev_err(dev, "%s : Could not write command to 0x%x\n", + __func__, fc->fd.command_base_addr); + return result; + } +#if F54_WATCHDOG + /* start watchdog timer */ + hrtimer_start(&instance_data->watchdog, ktime_set(1, 0), + HRTIMER_MODE_REL); +#endif + return count; +} + +static ssize_t rmi_fn_54_force_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + unsigned long val; + int error, result; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + struct rmi_driver *driver; + u8 command; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + driver = fc->rmi_dev->driver; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + /* Do nothing if not set to 1. This prevents accidental commands. */ + if (val != 1) + return count; + + command = (unsigned char)FORCE_CAL; + + if (instance_data->status == BUSY) + return -EBUSY; + /* Write the command to the command register */ + result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr, + &command, 1); + if (result < 0) { + dev_err(dev, "%s : Could not write command to 0x%x\n", + __func__, fc->fd.command_base_addr); + return result; + } + return count; +} + +static ssize_t rmi_fn_54_status_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status); +} + +simple_show_union_struct_unsigned(query, num_of_rx_electrodes) +simple_show_union_struct_unsigned(query, num_of_tx_electrodes) +simple_show_union_struct_unsigned(query, has_image16) +simple_show_union_struct_unsigned(query, has_image8) +simple_show_union_struct_unsigned(query, has_baseline) +simple_show_union_struct_unsigned(query, clock_rate) +simple_show_union_struct_unsigned(query, touch_controller_family) +simple_show_union_struct_unsigned(query, has_pixel_touch_threshold_adjustment) +simple_show_union_struct_unsigned(query, has_sensor_assignment) +simple_show_union_struct_unsigned(query, has_interference_metric) +simple_show_union_struct_unsigned(query, has_sense_frequency_control) +simple_show_union_struct_unsigned(query, has_firmware_noise_mitigation) +simple_show_union_struct_unsigned(query, has_two_byte_report_rate) +simple_show_union_struct_unsigned(query, has_one_byte_report_rate) +simple_show_union_struct_unsigned(query, has_relaxation_control) +simple_show_union_struct_unsigned(query, curve_compensation_mode) +simple_show_union_struct_unsigned(query, has_iir_filter) +simple_show_union_struct_unsigned(query, has_cmn_removal) +simple_show_union_struct_unsigned(query, has_cmn_maximum) +simple_show_union_struct_unsigned(query, has_touch_hysteresis) +simple_show_union_struct_unsigned(query, has_edge_compensation) +simple_show_union_struct_unsigned(query, has_per_frequency_noise_control) +simple_show_union_struct_unsigned(query, number_of_sensing_frequencies) + +static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + + fc = to_rmi_function_container(dev); + data = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + data->no_auto_cal ? 1 : 0); +} + +static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { + int result; + unsigned long val; + unsigned char data; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + result = strict_strtoul(buf, 10, &val); + + /* if an error occured, return it */ + if (result) + return result; + /* Do nothing if not 0 or 1. This prevents accidental commands. */ + if (val > 1) + return -EINVAL; + /* Read current control values */ + result = + rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1); + + /* if the current control registers are already set as we want them, do + * nothing to them */ + if ((data & NO_AUTO_CAL_MASK) == val) + return count; + /* Write the control back to the control register (F54_AD_Ctrl0) + * Ignores everything but bit 0 */ + data = (data & ~NO_AUTO_CAL_MASK) | (val & NO_AUTO_CAL_MASK); + result = + rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1); + if (result < 0) { + dev_err(dev, "%s : Could not write control to 0x%x\n", + __func__, fc->fd.control_base_addr); + return result; + } + /* update our internal representation iff the write succeeds */ + instance_data->no_auto_cal = (val == 1); + return count; +} + +static ssize_t rmi_fn_54_fifoindex_show(struct device *dev, + struct device_attribute *attr, char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + struct rmi_driver *driver; + unsigned char temp_buf[2]; + int retval; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + driver = fc->rmi_dev->driver; + + /* Read fifoindex from device */ + retval = rmi_read_block(fc->rmi_dev, + fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET, + temp_buf, ARRAY_SIZE(temp_buf)); + + if (retval < 0) { + dev_err(dev, "Could not read fifoindex from 0x%04x\n", + fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET); + return retval; + } + batohs(&instance_data->fifoindex, temp_buf); + return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex); +} +static ssize_t rmi_fn_54_fifoindex_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int error; + unsigned long val; + unsigned char data[2]; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + fc = to_rmi_function_container(dev); + instance_data = fc->data; + + /* need to convert the string data to an actual value */ + error = strict_strtoul(buf, 10, &val); + + if (error) + return error; + + instance_data->fifoindex = val; + + /* Write the FifoIndex back to the first data registers. */ + hstoba(data, (unsigned short)val); + + error = rmi_write_block(fc->rmi_dev, + fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET, + data, + ARRAY_SIZE(data)); + + if (error < 0) { + dev_err(dev, "%s : Could not write fifoindex to 0x%x\n", + __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET); + return error; + } + return count; +} + +/* Provide access to last report */ +static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj, + struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct device *dev; + struct rmi_function_container *fc; + struct rmi_fn_54_data *instance_data; + + dev = container_of(kobj, struct device, kobj); + fc = to_rmi_function_container(dev); + instance_data = fc->data; + mutex_lock(&instance_data->data_mutex); + if (count < instance_data->report_size) { + dev_err(dev, + "%s: F54 report size too large for buffer: %d." + " Need at least: %d for Report type: %d.\n", + __func__, count, instance_data->report_size, + instance_data->report_type); + mutex_unlock(&instance_data->data_mutex); + return -EINVAL; + } + if (instance_data->report_data) { + /* Copy data from instance_data to buffer */ + memcpy(buf, instance_data->report_data, + instance_data->report_size); + mutex_unlock(&instance_data->data_mutex); + dev_dbg(dev, "%s: Presumably successful.", __func__); + return instance_data->report_size; + } else { + dev_err(dev, "%s: F54 report_data does not exist!\n", __func__); + mutex_unlock(&instance_data->data_mutex); + return -EINVAL; + } +} + +/* Repeated Register sysfs functions */ +show_repeated_union_struct_unsigned(control, reg_15, sensor_rx_assignment) +show_repeated_union_struct_unsigned(control, reg_16, sensor_tx_assignment) + +show_repeated_union_struct_unsigned(control, reg_17, filter_bandwidth) +show_repeated_union_struct_unsigned(control, reg_17, disable) + + +static ssize_t rmi_fn_54_burst_count_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct rmi_function_container *fc; + struct rmi_fn_54_data *data; + int result, size = 0; + char *temp; + int i; + + fc = to_rmi_function_container(dev); + data = fc->data; + mutex_lock(&data->control_mutex); + /* Read current control values */ + result = rmi_read_block(fc->rmi_dev, data->control.reg_17->address, + (u8 *) data->control.reg_17->regs, + data->control.reg_17->length * sizeof(u8)); + if (result < 0) { + dev_err(dev, "%s : Could not read control at 0x%x\n" + "Data may be outdated.", __func__, + data->control.reg_17->address); + } + + result = rmi_read_block(fc->rmi_dev, data->control.reg_18->address, + (u8 *)data->control.reg_18->regs, + data->control.reg_18->length * sizeof(u8)); + if (result < 0) { + dev_err(dev, "%s : Could not read control at 0x%x\n" + "Data may be outdated.", __func__, + data->control.reg_18->address); + } + mutex_unlock(&data->control_mutex); + temp = buf; + for (i = 0; i < data->control.reg_17->length; i++) { + result = snprintf(temp, PAGE_SIZE - size, "%u ", (1<<8) * + data->control.reg_17->regs[i].burst_countb10__8 + + data->control.reg_18->regs[i].burst_countb7__0n); + size += result; + temp += result; + } + return size + snprintf(temp, PAGE_SIZE - size, "\n"); +} + +show_repeated_union_struct_unsigned(control, reg_19, stretch_duration) +show_store_repeated_union_struct_unsigned(control, reg_36, axis1_comp) +show_store_repeated_union_struct_unsigned(control, reg_37, axis2_comp) + +show_repeated_union_struct_unsigned(control, reg_38, noise_control_1) +show_repeated_union_struct_unsigned(control, reg_39, noise_control_2) +show_repeated_union_struct_unsigned(control, reg_40, noise_control_3) + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +show_store_union_struct_unsigned(control, reg_0, no_relax) +show_store_union_struct_unsigned(control, reg_0, no_scan) +show_store_union_struct_unsigned(control, reg_1, bursts_per_cluster) +show_store_union_struct_unsigned(control, reg_2, saturation_cap) +show_store_union_struct_unsigned(control, reg_3, pixel_touch_threshold) +show_store_union_struct_unsigned(control, reg_4__6, rx_feedback_cap) +show_store_union_struct_unsigned(control, reg_4__6, low_ref_cap) +show_store_union_struct_unsigned(control, reg_4__6, low_ref_feedback_cap) +show_store_union_struct_unsigned(control, reg_4__6, low_ref_polarity) +show_store_union_struct_unsigned(control, reg_4__6, high_ref_cap) +show_store_union_struct_unsigned(control, reg_4__6, high_ref_feedback_cap) +show_store_union_struct_unsigned(control, reg_4__6, high_ref_polarity) +show_store_union_struct_unsigned(control, reg_7, cbc_cap) +show_store_union_struct_unsigned(control, reg_7, cbc_polarity) +show_store_union_struct_unsigned(control, reg_7, cbc_tx_carrier_selection) +show_store_union_struct_unsigned(control, reg_8__9, integration_duration) +show_store_union_struct_unsigned(control, reg_8__9, reset_duration) +show_store_union_struct_unsigned(control, reg_10, + noise_sensing_bursts_per_image) +show_store_union_struct_unsigned(control, reg_12__13, slow_relaxation_rate) +show_store_union_struct_unsigned(control, reg_12__13, fast_relaxation_rate) +show_store_union_struct_unsigned(control, reg_14, rxs_on_xaxis) +show_store_union_struct_unsigned(control, reg_14, curve_comp_on_txs) +show_store_union_struct_unsigned(control, reg_20, disable_noise_mitigation) +show_store_union_struct_unsigned(control, reg_21, freq_shift_noise_threshold) +show_store_union_struct_unsigned(control, reg_22__26, medium_noise_threshold) +show_store_union_struct_unsigned(control, reg_22__26, high_noise_threshold) +show_store_union_struct_unsigned(control, reg_22__26, noise_density) +show_store_union_struct_unsigned(control, reg_22__26, frame_count) +show_store_union_struct_unsigned(control, reg_27, iir_filter_coef) +show_store_union_struct_unsigned(control, reg_28, quiet_threshold) +show_store_union_struct_unsigned(control, reg_29, cmn_filter_disable) +show_store_union_struct_unsigned(control, reg_30, cmn_filter_max) +show_store_union_struct_unsigned(control, reg_31, touch_hysteresis) +show_store_union_struct_unsigned(control, reg_32__35, rx_low_edge_comp) +show_store_union_struct_unsigned(control, reg_32__35, rx_high_edge_comp) +show_store_union_struct_unsigned(control, reg_32__35, tx_low_edge_comp) +show_store_union_struct_unsigned(control, reg_32__35, tx_high_edge_comp) + +/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ +static struct rmi_function_handler function_handler = { + .func = 0x54, + .init = rmi_f54_init, + .config = NULL, + .reset = rmi_f54_reset, + .attention = rmi_f54_attention, + .remove = rmi_f54_remove +}; + +static int __init rmi_f54_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + return 0; +} + +static void rmi_f54_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +module_init(rmi_f54_module_init); +module_exit(rmi_f54_module_exit); + +MODULE_AUTHOR("Daniel Rosenberg "); +MODULE_DESCRIPTION("RMI F54 module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION);