From: Christopher Heiny <cheiny@synaptics.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Jean Delvare <khali@linux-fr.org>,
Linux Kernel <linux-kernel@vger.kernel.org>,
Linux Input <linux-input@vger.kernel.org>,
Christopher Heiny <cheiny@synaptics.com>,
Joerie de Gram <j.de.gram@gmail.com>,
Linus Walleij <linus.walleij@stericsson.com>,
Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
Subject: [RFC PATCH 8/11] input: RMI4 F54 - analog data reporting
Date: Wed, 21 Dec 2011 18:09:59 -0800 [thread overview]
Message-ID: <1324519802-23894-9-git-send-email-cheiny@synaptics.com> (raw)
In-Reply-To: <1324519802-23894-1-git-send-email-cheiny@synaptics.com>
Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Linus Walleij <linus.walleij@stericsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
Cc: Joeri de Gram <j.de.gram@gmail.com>
---
drivers/input/rmi4/rmi_f54.c | 1506 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1506 insertions(+), 0 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
new file mode 100644
index 0000000..5119184
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,1506 @@
+
+/*
+ * Copyright (c) 2011 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 <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#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
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* define fn $54 commands */
+#define GET_REPORT 1
+#define FORCE_CAL 2
+
+/* 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 in ultra-portable unionstruct form */
+struct f54_ad_query {
+ /* query 0 */
+ u8 number_of_receiver_electrodes;
+
+ /* query 1 */
+ u8 number_of_transmitter_electrodes;
+
+ union {
+ struct {
+ /* 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;
+ };
+ u8 f54_ad_query2;
+ };
+
+ /* query 3.0 and 3.1 */
+ u16 clock_rate;
+
+ /* query 4 */
+ u8 touch_controller_family;
+
+ /* query 5 */
+ union {
+ struct {
+ u8 has_pixel_touch_threshold_adjustment:1;
+ u8 f54_ad_query5_b1__7:7;
+ };
+ u8 f54_ad_query5;
+ };
+
+ /* query 6 */
+ union {
+ struct {
+ 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;
+ };
+ u8 f54_ad_query6;
+ };
+
+ /* query 7 */
+ union {
+ struct {
+ u8 curve_compensation_mode:2;
+ u8 f54_ad_query7_b2__7:6;
+ };
+ u8 f54_ad_query7;
+ };
+
+ /* query 8 */
+ union {
+ struct {
+ u8 f54_ad_query2_b0:1;
+ u8 has_iir_filter:1;
+ u8 has_cmn_removal:1;
+ u8 has_cmn_maximum:1;
+ u8 has_pixel_threshold_hysteresis:1;
+ u8 has_edge_compensation:1;
+ u8 has_perf_frequency_noisecontrol:1;
+ u8 f54_ad_query8_b7:1;
+ };
+ u8 f54_ad_query8;
+ };
+
+ u8 f54_ad_query9;
+ u8 f54_ad_query10;
+ u8 f54_ad_query11;
+
+ /* query 12 */
+ union {
+ struct {
+ u8 number_of_sensing_frequencies:4;
+ u8 f54_ad_query12_b4__7:4;
+ };
+ u8 f54_ad_query12;
+ };
+};
+
+/* define report types */
+enum f54_report_types {
+ /* The numbering should follow automatically, here for clarity */
+ 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
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+ struct f54_ad_query query;
+ u8 cmd;
+ enum f54_report_types report_type;
+ u16 fifoindex;
+ signed char status;
+ bool no_auto_cal;
+ /*
+ * May need to do something to make sure this reflects what is currently
+ * in data.
+ */
+ unsigned int report_size;
+ unsigned char *report_data;
+ unsigned int bufsize;
+ struct mutex data_mutex;
+ struct lock_class_key data_key;
+ struct mutex status_mutex;
+ struct lock_class_key status_key;
+#if F54_WATCHDOG
+ struct hrtimer watchdog;
+#endif
+ struct rmi_function_container *fc;
+ struct work_struct work;
+};
+
+/* sysfs functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+ struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+ struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+static inline int rmi_f54_alloc_memory(struct rmi_function_container *fc);
+
+static inline void rmi_f54_free_memory(struct rmi_function_container *fc);
+
+static inline int rmi_f54_initialize(struct rmi_function_container *fc);
+
+static int rmi_f54_config(struct rmi_function_container *fc);
+
+static int rmi_f54_reset(struct rmi_function_container *fc);
+
+static inline int rmi_f54_create_sysfs(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+ __ATTR(report_type, RMI_RW_ATTR,
+ rmi_fn_54_report_type_show, rmi_fn_54_report_type_store),
+ __ATTR(get_report, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_54_get_report_store),
+ __ATTR(force_cal, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_54_force_cal_store),
+ __ATTR(status, RMI_RW_ATTR,
+ rmi_fn_54_status_show, rmi_fn_54_status_store),
+ __ATTR(num_rx_electrodes, RMI_RO_ATTR,
+ rmi_fn_54_num_rx_electrodes_show, rmi_store_error),
+ __ATTR(num_tx_electrodes, RMI_RO_ATTR,
+ rmi_fn_54_num_tx_electrodes_show, rmi_store_error),
+ __ATTR(has_image16, RMI_RO_ATTR,
+ rmi_fn_54_has_image16_show, rmi_store_error),
+ __ATTR(has_image8, RMI_RO_ATTR,
+ rmi_fn_54_has_image8_show, rmi_store_error),
+ __ATTR(has_baseline, RMI_RO_ATTR,
+ rmi_fn_54_has_baseline_show, rmi_store_error),
+ __ATTR(clock_rate, RMI_RO_ATTR,
+ rmi_fn_54_clock_rate_show, rmi_store_error),
+ __ATTR(touch_controller_family, RMI_RO_ATTR,
+ rmi_fn_54_touch_controller_family_show, rmi_store_error),
+ __ATTR(has_pixel_touch_threshold_adjustment, RMI_RO_ATTR,
+ rmi_fn_54_has_pixel_touch_threshold_adjustment_show
+ , rmi_store_error),
+ __ATTR(has_sensor_assignment, RMI_RO_ATTR,
+ rmi_fn_54_has_sensor_assignment_show, rmi_store_error),
+ __ATTR(has_interference_metric, RMI_RO_ATTR,
+ rmi_fn_54_has_interference_metric_show, rmi_store_error),
+ __ATTR(has_sense_frequency_control, RMI_RO_ATTR,
+ rmi_fn_54_has_sense_frequency_control_show, rmi_store_error),
+ __ATTR(has_firmware_noise_mitigation, RMI_RO_ATTR,
+ rmi_fn_54_has_firmware_noise_mitigation_show, rmi_store_error),
+ __ATTR(has_two_byte_report_rate, RMI_RO_ATTR,
+ rmi_fn_54_has_two_byte_report_rate_show, rmi_store_error),
+ __ATTR(has_one_byte_report_rate, RMI_RO_ATTR,
+ rmi_fn_54_has_one_byte_report_rate_show, rmi_store_error),
+ __ATTR(has_relaxation_control, RMI_RO_ATTR,
+ rmi_fn_54_has_relaxation_control_show, rmi_store_error),
+ __ATTR(curve_compensation_mode, RMI_RO_ATTR,
+ rmi_fn_54_curve_compensation_mode_show, rmi_store_error),
+ __ATTR(has_iir_filter, RMI_RO_ATTR,
+ rmi_fn_54_has_iir_filter_show, rmi_store_error),
+ __ATTR(has_cmn_removal, RMI_RO_ATTR,
+ rmi_fn_54_has_cmn_removal_show, rmi_store_error),
+ __ATTR(has_cmn_maximum, RMI_RO_ATTR,
+ rmi_fn_54_has_cmn_maximum_show, rmi_store_error),
+ __ATTR(has_pixel_threshold_hysteresis, RMI_RO_ATTR,
+ rmi_fn_54_has_pixel_threshold_hysteresis_show, rmi_store_error),
+ __ATTR(has_edge_compensation, RMI_RO_ATTR,
+ rmi_fn_54_has_edge_compensation_show, rmi_store_error),
+ __ATTR(has_perf_frequency_noisecontrol, RMI_RO_ATTR,
+ rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error),
+ __ATTR(number_of_sensing_frequencies, RMI_RO_ATTR,
+ rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error),
+ __ATTR(no_auto_cal, RMI_RW_ATTR,
+ rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store),
+ __ATTR(fifoindex, RMI_RW_ATTR,
+ rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store),
+};
+
+struct bin_attribute dev_rep_data = {
+ .attr = {
+ .name = "rep_data",
+ .mode = RMI_RO_ATTR},
+ .size = 0,
+ .read = rmi_fn_54_data_read,
+};
+
+#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_init(struct rmi_function_container *fc)
+{
+ int retval = 0;
+
+ dev_info(&fc->dev, "Intializing 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;
+
+ return retval;
+
+error_exit:
+ rmi_f54_free_memory(fc);
+
+ return retval;
+}
+
+
+
+static inline 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;
+
+ return 0;
+}
+
+
+static inline void rmi_f54_free_memory(struct rmi_function_container *fc)
+{
+ struct rmi_fn_54_data *f54 = fc->data;
+
+ if (f54) {
+ kfree(f54->report_data);
+ kfree(f54);
+ fc->data = NULL;
+ }
+}
+
+
+static inline int rmi_f54_initialize(struct rmi_function_container *fc)
+{
+ struct rmi_fn_54_data *instance_data = fc->data;
+ int retval = 0;
+
+ instance_data->fc = fc;
+
+#if F54_WATCHDOG
+ /* Set up watchdog timer to catch unanswered get_report commands */
+ hrtimer_init(&instance_data->watchdog, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ instance_data->watchdog.function = clear_status;
+
+ /* work function to do unlocking */
+ INIT_WORK(&instance_data->work, clear_status_worker);
+#endif
+
+ /* Read F54 Query Data */
+ retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+ (u8 *)&instance_data->query, sizeof(instance_data->query));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read query registers"
+ " from 0x%04x\n", fc->fd.query_base_addr);
+ return retval;
+ }
+
+ __mutex_init(&instance_data->data_mutex, "data_mutex",
+ &instance_data->data_key);
+
+ __mutex_init(&instance_data->status_mutex, "status_mutex",
+ &instance_data->status_key);
+
+ instance_data->status = IDLE;
+
+ return 0;
+}
+
+
+static inline int rmi_f54_create_sysfs(struct rmi_function_container *fc)
+{
+ int attr_count = 0;
+ int rc;
+
+ dev_dbg(&fc->dev, "Creating sysfs files.");
+
+ /* Binary sysfs file to report the data back */
+ rc = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data);
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for F54 data "
+ "(error = %d).\n", rc);
+ return -ENODEV;
+ }
+
+ /* Set up sysfs device attributes. */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ attrs[attr_count].attr.name);
+ rc = -ENODEV;
+ goto err_remove_sysfs;
+ }
+ }
+
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+
+ return rc;
+}
+
+
+static int rmi_f54_config(struct rmi_function_container *fc)
+{
+ /*we shouldn't do anything here.
+ * (reset should write a 1 to a "beenreset" sysfs file)
+ */
+ return 0;
+}
+
+
+
+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;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Removing F54.");
+
+ #if F54_WATCHDOG
+ /* Stop timer */
+ hrtimer_cancel(&data->watchdog);
+ #endif
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+ sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+
+ sysfs_remove_bin_file(&fc->dev.kobj, &dev_rep_data);
+
+ rmi_f54_free_memory(fc);
+}
+
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+ u8 rx = data->query.number_of_receiver_electrodes;
+ u8 tx = data->query.number_of_transmitter_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.number_of_transmitter_electrodes;
+ i++) {
+ for (j = 0; j <
+ data->query.number_of_receiver_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) {
+ pr_debug("F54 Timout Occured: Determining status.\n");
+ 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 0x%04x\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 *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->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);
+}
+
+
+
+static ssize_t rmi_fn_54_status_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ mutex_lock(&instance_data->status_mutex);
+ /* any write to status resets it */
+ instance_data->status = 0;
+ mutex_unlock(&instance_data->status_mutex);
+
+ return 0;
+}
+
+
+static ssize_t rmi_fn_54_num_rx_electrodes_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->query.number_of_receiver_electrodes);
+}
+
+static ssize_t rmi_fn_54_num_tx_electrodes_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->query.number_of_transmitter_electrodes);
+}
+
+static ssize_t rmi_fn_54_has_image16_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->query.has_image16);
+}
+
+static ssize_t rmi_fn_54_has_image8_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->query.has_image8);
+}
+
+static ssize_t rmi_fn_54_has_baseline_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->query.has_baseline);
+}
+
+static ssize_t rmi_fn_54_clock_rate_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->query.clock_rate);
+}
+
+
+static ssize_t rmi_fn_54_touch_controller_family_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->query.touch_controller_family);
+}
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_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->query.has_pixel_touch_threshold_adjustment);
+}
+
+static ssize_t rmi_fn_54_has_sensor_assignment_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->query.has_sensor_assignment);
+}
+
+static ssize_t rmi_fn_54_has_interference_metric_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->query.has_interference_metric);
+}
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_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->query.has_sense_frequency_control);
+}
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_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->query.has_firmware_noise_mitigation);
+}
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_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->query.has_two_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_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->query.has_one_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_relaxation_control_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->query.has_relaxation_control);
+}
+
+static ssize_t rmi_fn_54_curve_compensation_mode_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->query.curve_compensation_mode);
+}
+
+static ssize_t rmi_fn_54_has_iir_filter_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->query.has_iir_filter);
+}
+
+static ssize_t rmi_fn_54_has_cmn_removal_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->query.has_cmn_removal);
+}
+
+static ssize_t rmi_fn_54_has_cmn_maximum_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->query.has_cmn_maximum);
+}
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_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->query.has_pixel_threshold_hysteresis);
+}
+
+static ssize_t rmi_fn_54_has_edge_compensation_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->query.has_edge_compensation);
+}
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_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->query.has_perf_frequency_noisecontrol);
+}
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_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->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 *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_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 count;
+ /* 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 & 1) == val)
+ return count;
+ /* Write the control back to the control register (F54_AD_Ctrl0)
+ * Ignores everything but bit 0 */
+ data = (data & ~1) | (val & 0x01); /* bit mask for lowest bit */
+ 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 */
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+ 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;
+ }
+}
+
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x54,
+ .init = rmi_f54_init,
+ .config = rmi_f54_config,
+ .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)
+{
+ /* There is no function handler specific data to be freed here.
+ * Function container specific data will be freed as part of the
+ * unregistration process.
+ */
+ rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f54_module_init);
+module_exit(rmi_f54_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
+MODULE_DESCRIPTION("RMI F54 module");
+MODULE_LICENSE("GPL");
+
next prev parent reply other threads:[~2011-12-22 2:20 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-12-22 2:09 [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 1/11] input: RMI4 public header file and documentation Christopher Heiny
2012-01-06 6:35 ` Dmitry Torokhov
2012-01-09 20:31 ` Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 2/11] input: RMI4 core bus and sensor drivers Christopher Heiny
2012-01-02 6:38 ` Shubhrajyoti
2012-01-05 20:49 ` Christopher Heiny
2012-01-05 21:58 ` Lars-Peter Clausen
2012-01-06 1:56 ` Christopher Heiny
2012-01-06 2:34 ` Dmitry Torokhov
2012-01-07 3:26 ` Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 3/11] input: RMI4 physical layer drivers for I2C and SPI Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 4/11] input: RMI4 KConfigs and Makefiles Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 5/11] input: rmidev character driver for RMI4 sensors Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 6/11] input: RMI4 F09 - self test Christopher Heiny
2011-12-22 2:09 ` [RFC PATCH 7/11] input: RMI4 F01 - device control Christopher Heiny
2011-12-22 2:09 ` Christopher Heiny [this message]
2011-12-22 2:10 ` [RFC PATCH 9/11] input: RMI F34 - firmware reflash Christopher Heiny
2011-12-22 2:10 ` [RFC PATCH 10/11] input: RMI4 F19 - capacitive buttons Christopher Heiny
2012-01-05 7:53 ` Dmitry Torokhov
2012-01-06 0:05 ` Christopher Heiny
2012-01-06 2:50 ` Dmitry Torokhov
2012-01-09 21:02 ` Christopher Heiny
2011-12-22 2:10 ` [RFC PATCH 11/11] input: RMI4 F11 - multifinger pointing Christopher Heiny
2012-01-01 13:51 ` [RFC PATCH 00/11] input: Synaptics RMI4 Touchscreen Driver Linus Walleij
2012-01-04 1:51 ` Christopher Heiny
2012-01-05 7:58 ` Dmitry Torokhov
2012-01-05 20:09 ` Christopher Heiny
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1324519802-23894-9-git-send-email-cheiny@synaptics.com \
--to=cheiny@synaptics.com \
--cc=dmitry.torokhov@gmail.com \
--cc=j.de.gram@gmail.com \
--cc=khali@linux-fr.org \
--cc=linus.walleij@stericsson.com \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=naveen.gaddipati@stericsson.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.