diff mbox

[RFC,12/17] input: RMI4 F1A simple capacitive buttons

Message ID 1345241877-16200-13-git-send-email-cheiny@synaptics.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christopher Heiny Aug. 17, 2012, 10:17 p.m. UTC
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>

Acked-by: Jean Delvare <khali@linux-fr.org>

---

 drivers/input/rmi4/rmi_f1a.c | 1025 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1025 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 mbox

Patch

diff --git a/drivers/input/rmi4/rmi_f1a.c b/drivers/input/rmi4/rmi_f1a.c
new file mode 100644
index 0000000..a7cccfa
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f1a.c
@@ -0,0 +1,1025 @@ 
+/*
+ * Copyright (c) 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.
+ */
+#define FUNCTION_DATA f1a_data
+#define FNUM 1a
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#define QUERY_BASE_INDEX 1
+#define MAX_NAME_LEN 256
+#define MAX_BUFFER_LEN 80
+
+#define FILTER_MODE_MIN				0
+#define FILTER_MODE_MAX				3
+#define MULTI_BUTTON_REPORT_MIN			0
+#define MULTI_BUTTON_REPORT_MAX			3
+#define TX_RX_BUTTON_MIN			0
+#define TX_RX_BUTTON_MAX			255
+#define THREADHOLD_BUTTON_MIN			0
+#define THREADHOLD_BUTTON_MAX			255
+#define RELEASE_THREADHOLD_BUTTON_MIN		0
+#define RELEASE_THREADHOLD_BUTTON_MAX		255
+#define STRONGEST_BUTTON_HYSTERESIS_MIN		0
+#define STRONGEST_BUTTON_HYSTERESIS_MAX		255
+#define FILTER_STRENGTH_MIN			0
+#define FILTER_STRENGTH_MAX			255
+
+union f1a_0d_query {
+	struct {
+		u8 max_button_count:3;
+		u8 reserved:5;
+
+		u8 has_general_control:1;
+		u8 has_interrupt_enable:1;
+		u8 has_multibutton_select:1;
+		u8 has_tx_rx_map:1;
+		u8 has_perbutton_threshold:1;
+		u8 has_release_threshold:1;
+		u8 has_strongestbtn_hysteresis:1;
+		u8 has_filter_strength:1;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[2];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f1a_0d_control_0 {
+	struct {
+		u8 multibutton_report:2;
+		u8 filter_mode:2;
+	} __attribute__((__packed__));
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f1a_0d_control_1n {
+	u8 interrupt_enabled_button;
+};
+
+struct f1a_0d_control_1 {
+	struct f1a_0d_control_1n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_2n {
+	u8 multi_button;
+};
+
+struct f1a_0d_control_2 {
+	struct f1a_0d_control_2n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_3_4n {
+	u8 transmitterbtn;
+	u8 receiverbtn;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_3_4 {
+	struct f1a_0d_control_3_4n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+struct f1a_0d_control_5n {
+	u8 threshold_button;
+};
+
+struct f1a_0d_control_5 {
+	struct f1a_0d_control_5n *regs;
+	u16 address;
+	u8 length;
+} __attribute__((__packed__));
+
+union f1a_0d_control_6 {
+	struct {
+		u8 button_release_threshold;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f1a_0d_control_7 {
+	struct {
+		u8 strongest_button_hysteresis;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+union f1a_0d_control_8 {
+	struct {
+		u8 filter_strength;
+	};
+	struct {
+		u8 regs[1];
+		u16 address;
+	} __attribute__((__packed__));
+};
+
+struct f1a_0d_control {
+	union f1a_0d_control_0 *reg_0;
+	struct f1a_0d_control_1 *reg_1;
+	struct f1a_0d_control_2 *reg_2;
+	struct f1a_0d_control_3_4 *reg_3_4;
+	struct f1a_0d_control_5 *reg_5;
+	union f1a_0d_control_6 *reg_6;
+	union f1a_0d_control_7 *reg_7;
+	union f1a_0d_control_8 *reg_8;
+};
+
+/* data specific to fn $1a that needs to be kept around */
+struct f1a_data {
+	struct f1a_0d_control control;
+	union f1a_0d_query query;
+	u8 sensor_button_count;
+	int button_bitmask_size;
+	u8 *button_data_buffer;
+	u8 button_map_size;
+	u8 *button_map;
+	char input_name[MAX_NAME_LEN];
+	char input_phys[MAX_NAME_LEN];
+	struct input_dev *input;
+	struct mutex control_mutex;
+	struct mutex data_mutex;
+};
+
+
+static int rmi_f1a_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f1a_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f1a_initialize(struct rmi_function_container *fc);
+
+static int rmi_f1a_register_device(struct rmi_function_container *fc);
+
+static int rmi_f1a_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f1a_config(struct rmi_function_container *fc);
+
+static int rmi_f1a_reset(struct rmi_function_container *fc);
+
+/* Query sysfs files */
+show_union_struct_prototype(max_button_count)
+show_union_struct_prototype(has_general_control)
+show_union_struct_prototype(has_interrupt_enable)
+show_union_struct_prototype(has_multibutton_select)
+show_union_struct_prototype(has_tx_rx_map)
+show_union_struct_prototype(has_perbutton_threshold)
+show_union_struct_prototype(has_release_threshold)
+show_union_struct_prototype(has_strongestbtn_hysteresis)
+show_union_struct_prototype(has_filter_strength)
+
+static struct attribute *attrs[] = {
+	attrify(max_button_count),
+	attrify(has_general_control),
+	attrify(has_interrupt_enable),
+	attrify(has_multibutton_select),
+	attrify(has_tx_rx_map),
+	attrify(has_perbutton_threshold),
+	attrify(has_release_threshold),
+	attrify(has_strongestbtn_hysteresis),
+	attrify(has_filter_strength),
+	NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs);
+
+/* Control sysfs files */
+show_store_union_struct_prototype(multibutton_report)
+show_store_union_struct_prototype(filter_mode)
+show_store_union_struct_prototype(interrupt_enabled_button)
+show_store_union_struct_prototype(multi_button)
+show_union_struct_prototype(tx_rx_map)
+show_store_union_struct_prototype(threshold_button)
+show_store_union_struct_prototype(button_release_threshold)
+show_store_union_struct_prototype(strongest_button_hysteresis)
+show_store_union_struct_prototype(filter_strength)
+
+static int rmi_f1a_read_control_parameters(struct rmi_device *rmi_dev,
+	struct f1a_data *f1a)
+{
+	int retval = 0;
+	union f1a_0d_query *query = &f1a->query;
+	struct f1a_0d_control *control = &f1a->control;
+
+	if (query->has_general_control) {
+		retval = rmi_read_block(rmi_dev, control->reg_0->address,
+				(u8 *)control->reg_0->regs,
+				sizeof(control->reg_0->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg0 to %#06x.\n",
+					control->reg_0->address);
+			return retval;
+		}
+	}
+
+	if (query->has_interrupt_enable) {
+		retval = rmi_read_block(rmi_dev, control->reg_1->address,
+			(u8 *)control->reg_1->regs, f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg1 to %#06x.\n",
+				 control->reg_1->address);
+			return retval;
+		}
+	}
+
+	if (query->has_multibutton_select) {
+		retval = rmi_read_block(rmi_dev, control->reg_2->address,
+			(u8 *)control->reg_2->regs, f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg2 to %#06x.\n",
+				 control->reg_2->address);
+			return retval;
+		}
+	}
+
+	if (query->has_tx_rx_map) {
+		retval = rmi_read_block(rmi_dev, control->reg_3_4->address,
+			(u8 *)control->reg_3_4->regs,
+			sizeof(struct f1a_0d_control_3_4n) *
+					f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 3_4 to %#06x.\n",
+				 control->reg_3_4->address);
+			return retval;
+		}
+	}
+
+	if (query->has_perbutton_threshold) {
+		retval = rmi_read_block(rmi_dev, control->reg_5->address,
+			(u8 *)control->reg_5->regs, f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 5 to %#06x.\n",
+				 control->reg_5->address);
+			return retval;
+		}
+	}
+
+	if (query->has_release_threshold) {
+		retval = rmi_read_block(rmi_dev, control->reg_6->address,
+				(u8 *)control->reg_6->regs,
+				sizeof(control->reg_6->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 6 to %#06x.\n",
+					control->reg_6->address);
+			return retval;
+		}
+	}
+
+	if (query->has_strongestbtn_hysteresis) {
+		retval = rmi_read_block(rmi_dev, control->reg_7->address,
+				(u8 *)control->reg_7->regs,
+				sizeof(control->reg_7->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 7 to %#06x.\n",
+					control->reg_7->address);
+			return retval;
+		}
+	}
+
+	if (query->has_filter_strength) {
+		retval = rmi_read_block(rmi_dev, control->reg_8->address,
+				(u8 *)control->reg_8->regs,
+				sizeof(control->reg_8->regs));
+		if (retval < 0) {
+			dev_err(&rmi_dev->dev, "Could not read control reg 8 to %#06x.\n",
+					control->reg_8->address);
+			return retval;
+		}
+	}
+	return 0;
+}
+
+
+static int rmi_f1a_init(struct rmi_function_container *fc)
+{
+	int rc;
+
+	rc = rmi_f1a_alloc_memory(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f1a_initialize(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f1a_register_device(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	rc = rmi_f1a_create_sysfs(fc);
+	if (rc < 0)
+		goto err_free_data;
+
+	return 0;
+
+err_free_data:
+	rmi_f1a_free_memory(fc);
+
+	return rc;
+}
+
+
+static int rmi_f1a_alloc_memory(struct rmi_function_container *fc)
+{
+	struct f1a_data *f1a;
+	int rc;
+	int regSize;
+	u16 ctrl_base_addr;
+
+	f1a = kzalloc(sizeof(struct f1a_data), GFP_KERNEL);
+	if (!f1a) {
+		dev_err(&fc->dev, "Failed to allocate function data.\n");
+		return -ENOMEM;
+	}
+	fc->data = f1a;
+
+	rc = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+			f1a->query.regs, sizeof(f1a->query.regs));
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to read query register.\n");
+		return rc;
+	}
+
+	f1a->sensor_button_count = f1a->query.max_button_count+1;
+
+	f1a->button_bitmask_size =
+			sizeof(u8)*(f1a->sensor_button_count + 7) / 8;
+	f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size,
+			sizeof(u8), GFP_KERNEL);
+	if (!f1a->button_data_buffer) {
+		dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+		return -ENOMEM;
+	}
+
+	f1a->button_map = kcalloc(f1a->sensor_button_count,
+				sizeof(unsigned char), GFP_KERNEL);
+	if (!f1a->button_map) {
+		dev_err(&fc->dev, "Failed to allocate button map.\n");
+		return -ENOMEM;
+	}
+
+	/* allocate memory for control reg */
+	/* reg 0 */
+	ctrl_base_addr = fc->fd.control_base_addr;
+	if (f1a->query.has_general_control) {
+		f1a->control.reg_0 =
+			kzalloc(sizeof(f1a->control.reg_0->regs), GFP_KERNEL);
+		if (!f1a->control.reg_0) {
+			dev_err(&fc->dev, "Failed to allocate reg_0 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_0->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_0->regs);
+	}
+	/* reg 1 */
+	if (f1a->query.has_interrupt_enable) {
+		f1a->control.reg_1 =
+			kzalloc(sizeof(struct f1a_0d_control_1), GFP_KERNEL);
+		if (!f1a->control.reg_1) {
+			dev_err(&fc->dev, "Failed to allocate reg_1 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_1->regs =
+				kzalloc(f1a->button_bitmask_size, GFP_KERNEL);
+		if (!f1a->control.reg_1->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_1->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_1->address = ctrl_base_addr;
+		f1a->control.reg_1->length = f1a->button_bitmask_size;
+		ctrl_base_addr += f1a->button_bitmask_size;
+	}
+
+	/* reg 2 */
+	if (f1a->query.has_multibutton_select) {
+		f1a->control.reg_2 =
+			kzalloc(sizeof(struct f1a_0d_control_2), GFP_KERNEL);
+		if (!f1a->control.reg_2) {
+			dev_err(&fc->dev, "Failed to allocate reg_2 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_2->regs =
+				kzalloc(f1a->button_bitmask_size, GFP_KERNEL);
+		if (!f1a->control.reg_2->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_2->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_2->address = ctrl_base_addr;
+		f1a->control.reg_2->length = f1a->button_bitmask_size;
+		ctrl_base_addr += f1a->button_bitmask_size;
+	}
+
+	/* reg 3_4*/
+	if (f1a->query.has_tx_rx_map) {
+		f1a->control.reg_3_4 =
+			kzalloc(sizeof(struct f1a_0d_control_3_4), GFP_KERNEL);
+		if (!f1a->control.reg_3_4) {
+			dev_err(&fc->dev, "Failed to allocate reg_3_4 control registers.");
+			return -ENOMEM;
+		}
+		regSize = sizeof(struct f1a_0d_control_3_4n);
+		f1a->control.reg_3_4->regs =
+			kzalloc(regSize * f1a->sensor_button_count, GFP_KERNEL);
+		if (!f1a->control.reg_3_4->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_3_4->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_3_4->address = ctrl_base_addr;
+		f1a->control.reg_3_4->length =
+					regSize * f1a->sensor_button_count;
+		ctrl_base_addr += regSize * f1a->sensor_button_count;
+	}
+
+	/* reg 5 */
+	if (f1a->query.has_perbutton_threshold) {
+		f1a->control.reg_5 =
+			kzalloc(sizeof(struct f1a_0d_control_5), GFP_KERNEL);
+		if (!f1a->control.reg_5) {
+			dev_err(&fc->dev, "Failed to allocate reg_5 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_5->regs =
+				kzalloc(f1a->sensor_button_count, GFP_KERNEL);
+		if (!f1a->control.reg_5->regs) {
+			dev_err(&fc->dev, "Failed to allocate reg_5->regs control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_5->address = ctrl_base_addr;
+		f1a->control.reg_5->length = f1a->sensor_button_count;
+		ctrl_base_addr += f1a->sensor_button_count;
+	}
+
+	/* reg 6 */
+	if (f1a->query.has_release_threshold) {
+		f1a->control.reg_6 =
+			kzalloc(sizeof(f1a->control.reg_6->regs), GFP_KERNEL);
+		if (!f1a->control.reg_6) {
+			dev_err(&fc->dev, "Failed to allocate reg_6 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_6->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_6->regs);
+	}
+	/* reg 7 */
+	if (f1a->query.has_strongestbtn_hysteresis) {
+		f1a->control.reg_7 =
+			kzalloc(sizeof(f1a->control.reg_7->regs), GFP_KERNEL);
+		if (!f1a->control.reg_7) {
+			dev_err(&fc->dev, "Failed to allocate reg_7 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_7->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_7->regs);
+	}
+	/* reg 8 */
+	if (f1a->query.has_filter_strength) {
+		f1a->control.reg_8 =
+			kzalloc(sizeof(f1a->control.reg_8->regs), GFP_KERNEL);
+		if (!f1a->control.reg_8) {
+			dev_err(&fc->dev, "Failed to allocate reg_8 control registers.");
+			return -ENOMEM;
+		}
+		f1a->control.reg_8->address = ctrl_base_addr;
+		ctrl_base_addr += sizeof(f1a->control.reg_8->regs);
+	}
+	return 0;
+}
+
+
+
+static void rmi_f1a_free_memory(struct rmi_function_container *fc)
+{
+	union f1a_0d_query *query;
+	struct f1a_data *f1a = fc->data;
+
+	query = &f1a->query;
+	sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+
+	if (query->has_general_control) {
+		sysfs_remove_file(&fc->dev.kobj, attrify(multibutton_report));
+		sysfs_remove_file(&fc->dev.kobj, attrify(filter_mode));
+	}
+
+	if (query->has_interrupt_enable)
+		sysfs_remove_file(&fc->dev.kobj,
+			attrify(interrupt_enabled_button));
+
+	if (query->has_multibutton_select)
+		sysfs_remove_file(&fc->dev.kobj, attrify(multi_button));
+
+	if (query->has_tx_rx_map)
+		sysfs_remove_file(&fc->dev.kobj, attrify(tx_rx_map));
+
+	if (query->has_perbutton_threshold)
+		sysfs_remove_file(&fc->dev.kobj, attrify(threshold_button));
+
+	if (query->has_release_threshold)
+		sysfs_remove_file(&fc->dev.kobj,
+				attrify(button_release_threshold));
+
+	if (query->has_strongestbtn_hysteresis)
+		sysfs_remove_file(&fc->dev.kobj,
+				attrify(strongest_button_hysteresis));
+
+	if (query->has_filter_strength)
+		sysfs_remove_file(&fc->dev.kobj, attrify(filter_strength));
+
+	if (f1a) {
+		kfree(f1a->button_data_buffer);
+		kfree(f1a->button_map);
+		kfree(f1a->control.reg_0);
+		if (f1a->control.reg_1) {
+			kfree(f1a->control.reg_1->regs);
+			kfree(f1a->control.reg_1);
+		}
+		if (f1a->control.reg_2) {
+			kfree(f1a->control.reg_2->regs);
+			kfree(f1a->control.reg_2);
+		}
+		if (f1a->control.reg_3_4) {
+			kfree(f1a->control.reg_3_4->regs);
+			kfree(f1a->control.reg_3_4);
+		}
+		if (f1a->control.reg_5) {
+			kfree(f1a->control.reg_5->regs);
+			kfree(f1a->control.reg_5);
+		}
+		kfree(f1a->control.reg_5);
+		kfree(f1a->control.reg_6);
+		kfree(f1a->control.reg_7);
+		kfree(f1a->control.reg_8);
+		kfree(f1a);
+		fc->data = NULL;
+	}
+}
+
+
+static int rmi_f1a_initialize(struct rmi_function_container *fc)
+{
+	int i;
+	int retval;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct rmi_device_platform_data *pdata;
+	struct f1a_data *f1a = fc->data;
+
+	dev_dbg(&fc->dev, "Intializing F1A values.");
+
+	/* initial all default values for f1a data here */
+	pdata = to_rmi_platform_data(rmi_dev);
+	if (pdata) {
+		if (!pdata->f1a_button_map)
+			dev_warn(&fc->dev, "%s - button_map is NULL", __func__);
+		else if (!pdata->f1a_button_map->map)
+			dev_warn(&fc->dev,
+				 "Platformdata button map is missing!\n");
+		else {
+			if (pdata->f1a_button_map->nbuttons !=
+					f1a->sensor_button_count)
+				dev_warn(&fc->dev,
+					"Platform data buttonmap size(%d) != number buttons on device(%d)\n",
+					pdata->f1a_button_map->nbuttons,
+					f1a->sensor_button_count);
+			f1a->button_map_size = min(f1a->sensor_button_count,
+					 pdata->f1a_button_map->nbuttons);
+			for (i = 0; i < f1a->button_map_size; i++)
+				f1a->button_map[i] =
+					pdata->f1a_button_map->map[i];
+		}
+	}
+
+	retval = rmi_f1a_read_control_parameters(rmi_dev, f1a);
+	if (retval < 0) {
+		dev_err(&fc->dev,
+			"Failed to initialize F1a control params.\n");
+		return retval;
+	}
+
+	mutex_init(&f1a->control_mutex);
+	mutex_init(&f1a->data_mutex);
+	return 0;
+}
+
+
+
+static int rmi_f1a_register_device(struct rmi_function_container *fc)
+{
+	int i;
+	int rc;
+	struct input_dev *input_dev;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f1a_data *f1a = fc->data;
+
+	input_dev = input_allocate_device();
+	if (!input_dev) {
+		dev_err(&fc->dev, "Failed to allocate input device.\n");
+		return -ENOMEM;
+	}
+
+	f1a->input = input_dev;
+	snprintf(f1a->input_name, MAX_NAME_LEN, "%sfn%02x",
+			dev_name(&rmi_dev->dev), fc->fd.function_number);
+	input_dev->name = f1a->input_name;
+	snprintf(f1a->input_phys, MAX_NAME_LEN, "%s/input0", input_dev->name);
+	input_dev->phys = f1a->input_phys;
+	input_dev->dev.parent = &rmi_dev->dev;
+	input_set_drvdata(input_dev, f1a);
+
+	/* Set up any input events. */
+	set_bit(EV_SYN, input_dev->evbit);
+	set_bit(EV_KEY, input_dev->evbit);
+
+	/* manage button map using input subsystem */
+	input_dev->keycode = f1a->button_map;
+	input_dev->keycodesize = sizeof(f1a->button_map);
+	input_dev->keycodemax = f1a->button_map_size;
+
+	/* set bits for each button. */
+	for (i = 0; i < f1a->button_map_size; i++) {
+		set_bit(f1a->button_map[i], input_dev->keybit);
+		input_set_capability(input_dev, EV_KEY, f1a->button_map[i]);
+	}
+
+	rc = input_register_device(input_dev);
+	if (rc < 0) {
+		dev_err(&fc->dev, "Failed to register input device.\n");
+		goto error_free_device;
+	}
+
+	return 0;
+
+error_free_device:
+	input_free_device(input_dev);
+
+	return rc;
+}
+
+
+static int rmi_f1a_create_sysfs(struct rmi_function_container *fc)
+{
+	struct f1a_data *f19 = fc->data;
+	union f1a_0d_query *query = &f19->query;
+
+	dev_dbg(&fc->dev, "Creating sysfs files.\n");
+	/* 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;
+	}
+
+	if (query->has_general_control) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(multibutton_report)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(filter_mode)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_interrupt_enable) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(interrupt_enabled_button)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_multibutton_select) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(multi_button)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_tx_rx_map) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(tx_rx_map)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_perbutton_threshold) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(threshold_button)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_release_threshold) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(button_release_threshold)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_strongestbtn_hysteresis) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(strongest_button_hysteresis)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+
+	if (query->has_filter_strength) {
+		if (sysfs_create_file(&fc->dev.kobj,
+				attrify(filter_strength)) < 0) {
+			dev_err(&fc->dev, "Failed to create control sysfs files.");
+			return -ENODEV;
+		}
+	}
+	return 0;
+}
+
+
+
+static int rmi_f1a_config(struct rmi_function_container *fc)
+{
+	int retval;
+	struct f1a_data *f1a = fc->data;
+	union f1a_0d_query *query = &f1a->query;
+	struct f1a_0d_control *control = &f1a->control;
+
+	if (query->has_general_control) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_0->address,
+			(u8 *)control->reg_0->regs,
+			sizeof(control->reg_0->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg0 to 0x%x\n",
+					__func__, control->reg_0->address);
+			return retval;
+		}
+	}
+	if (query->has_interrupt_enable) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_1->address,
+				(u8 *)control->reg_1->regs,
+				f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 1 to 0x%x\n",
+				__func__, control->reg_1->address);
+			return retval;
+		}
+	}
+	if (query->has_multibutton_select) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_2->address,
+				(u8 *)control->reg_2->regs,
+				f1a->button_bitmask_size);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 2 to 0x%x\n",
+				__func__, control->reg_2->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_tx_rx_map) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_3_4->address,
+				(u8 *)control->reg_3_4->regs,
+				f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg_3_4 to 0x%x\n",
+					__func__, control->reg_3_4->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_perbutton_threshold) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_5->address,
+				(u8 *)control->reg_5->regs,
+				f1a->sensor_button_count);
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write to reg 5 to 0x%x\n",
+				__func__, control->reg_5->address);
+			return retval;
+		}
+	}
+	if (query->has_release_threshold) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_6->address,
+			(u8 *)control->reg_6->regs,
+			sizeof(control->reg_6->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write  reg 6 to 0x%x\n",
+				__func__, control->reg_6->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_strongestbtn_hysteresis) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_7->address,
+			(u8 *)control->reg_7->regs,
+			sizeof(control->reg_7->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 7 to 0x%x\n",
+				__func__, control->reg_7->address);
+			return -EINVAL;
+		}
+	}
+	if (query->has_filter_strength) {
+		retval = rmi_write_block(fc->rmi_dev, control->reg_8->address,
+			(u8 *)control->reg_8->regs,
+			sizeof(control->reg_8->regs));
+		if (retval < 0) {
+			dev_err(&fc->dev, "%s : Could not write reg 8 to 0x%x\n",
+				__func__, control->reg_8->address);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+
+static int rmi_f1a_reset(struct rmi_function_container *fc)
+{
+	/* we do nnothing here */
+	return 0;
+}
+
+
+static void rmi_f1a_remove(struct rmi_function_container *fc)
+{
+	struct f1a_data *f1a = fc->data;
+
+	input_unregister_device(f1a->input);
+
+	rmi_f1a_free_memory(fc);
+}
+
+static int rmi_f1a_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+	int error;
+	int button;
+	struct rmi_device *rmi_dev = fc->rmi_dev;
+	struct f1a_data *f1a = fc->data;
+	u16 data_base_addr = fc->fd.data_base_addr;
+
+	/* Read the button data. */
+	error = rmi_read_block(rmi_dev, data_base_addr, f1a->button_data_buffer,
+			f1a->button_bitmask_size);
+	if (error < 0) {
+		dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
+			__func__);
+		return error;
+	}
+
+	/* Generate events for buttons that change state. */
+	for (button = 0; button < f1a->sensor_button_count; button++) {
+		int button_reg;
+		int button_shift;
+		bool button_status;
+
+		/* determine which data byte the button status is in */
+		button_reg = button / 8;
+		/* bit shift to get button's status */
+		button_shift = button % 8;
+		button_status =
+		    ((f1a->button_data_buffer[button_reg] >> button_shift)
+			& 0x01) != 0;
+
+		dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n",
+			__func__, button, f1a->button_map[button],
+				button_status);
+		/* Generate an event here. */
+		input_report_key(f1a->input, f1a->button_map[button],
+					button_status);
+	}
+
+	input_sync(f1a->input); /* sync after groups of events */
+	return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+	.func = 0x1a,
+	.init = rmi_f1a_init,
+	.config = rmi_f1a_config,
+	.reset = rmi_f1a_reset,
+	.attention = rmi_f1a_attention,
+	.remove = rmi_f1a_remove
+};
+
+static int __init rmi_f1a_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_f1a_module_exit(void)
+{
+	rmi_unregister_function_driver(&function_handler);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, max_button_count)
+simple_show_union_struct_unsigned(query, has_general_control)
+simple_show_union_struct_unsigned(query, has_interrupt_enable)
+simple_show_union_struct_unsigned(query, has_multibutton_select)
+simple_show_union_struct_unsigned(query, has_tx_rx_map)
+simple_show_union_struct_unsigned(query, has_perbutton_threshold)
+simple_show_union_struct_unsigned(query, has_release_threshold)
+simple_show_union_struct_unsigned(query, has_strongestbtn_hysteresis)
+simple_show_union_struct_unsigned(query, has_filter_strength)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_0, multibutton_report)
+show_store_union_struct_unsigned(control, reg_0, filter_mode)
+show_store_union_struct_unsigned(control, reg_6, button_release_threshold)
+show_store_union_struct_unsigned(control, reg_7, strongest_button_hysteresis)
+show_store_union_struct_unsigned(control, reg_8, filter_strength)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_1,
+						interrupt_enabled_button)
+show_store_repeated_union_struct_unsigned(control, reg_2, multi_button)
+show_store_repeated_union_struct_unsigned(control, reg_5, threshold_button)
+
+static ssize_t rmi_fn_1a_tx_rx_map_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_container *fc;
+	struct FUNCTION_DATA *data;
+	struct f1a_0d_control *control;
+	int reg_length;
+	int result, size = 0;
+	char *temp;
+	int i;
+
+	fc = to_rmi_function_container(dev);
+	data = fc->data;
+	control = &data->control;
+	/* Read current regtype values */
+	reg_length = control->reg_3_4->length;
+	result = rmi_read_block(fc->rmi_dev, control->reg_3_4->address,
+			(u8 *)control->reg_3_4->regs,
+			reg_length * sizeof(u8));
+
+	if (result < 0) {
+		dev_dbg(dev, "%s : Could not read regtype at 0x%x\n Data may be outdated.",
+				__func__, control->reg_3_4->address);
+	}
+	temp = buf;
+	for (i = 0; i < reg_length/2; i++) {
+		result = snprintf(temp, PAGE_SIZE - size, "%u-%u ",
+				control->reg_3_4->regs[i].transmitterbtn,
+				control->reg_3_4->regs[i].receiverbtn);
+		if (result < 0) {
+			dev_err(dev, "%s : Could not write output.", __func__);
+			return result;
+		}
+		size += result;
+		temp += result;
+	}
+	result = snprintf(temp, PAGE_SIZE - size, "\n");
+	if (result < 0) {
+			dev_err(dev, "%s : Could not write output.", __func__);
+			return result;
+	}
+	return size + result;
+}
+
+module_init(rmi_f1a_module_init);
+module_exit(rmi_f1a_module_exit);
+
+MODULE_AUTHOR("Vivian Ly <vly@synaptics.com>");
+MODULE_DESCRIPTION("RMI F1a module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);