Signed-off-by: Christopher Heiny <che...@synaptics.com>

Cc: Dmitry Torokhov <dmitry.torok...@gmail.com>
Cc: Linus Walleij <linus.wall...@stericsson.com>
Cc: Naveen Kumar Gaddipati <naveen.gaddip...@stericsson.com>
Cc: Joeri de Gram <j.de.g...@gmail.com>

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

---

 drivers/input/rmi4/rmi_f19.c |  869 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 869 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f19.c b/drivers/input/rmi4/rmi_f19.c
new file mode 100644
index 0000000..9e9ea93
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f19.c
@@ -0,0 +1,869 @@
+/*
+ * 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 f19_data
+#define FNUM 19
+
+#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_LEN 256
+#define MAX_BUFFER_LEN 80
+
+#define SENSOR_MAP_MIN                 0
+#define SENSOR_MAP_MAX                 127
+#define SENSITIVITY_ADJ_MIN            0
+#define SENSITIVITY_ADJ_MAX            31
+#define HYSTERESIS_THRESHOLD_MIN       0
+#define HYSTERESIS_THRESHOLD_MAX       15
+
+union f19_0d_query {
+       struct {
+               u8 configurable:1;
+               u8 has_sensitivity_adjust:1;
+               u8 has_hysteresis_threshold:1;
+               u8 reserved_1:5;
+
+               u8 button_count:5;
+               u8 reserved_2:3;
+       } __attribute__((__packed__));
+       struct {
+               u8 regs[2];
+               u16 address;
+       } __attribute__((__packed__));
+};
+
+union f19_0d_control_0 {
+       struct {
+               u8 button_usage:2;
+               u8 filter_mode:2;
+       } __attribute__((__packed__));
+       struct {
+               u8 regs[1];
+               u16 address;
+       } __attribute__((__packed__));
+};
+/* rewrite control regs */
+struct f19_0d_control_1n {
+       u8 int_enabled_button;
+};
+
+struct f19_0d_control_1 {
+       struct f19_0d_control_1n *regs;
+       u16 address;
+       u8 length;
+} __attribute__((__packed__));
+
+struct f19_0d_control_2n {
+       u8 single_button;
+};
+
+struct f19_0d_control_2 {
+       struct f19_0d_control_2n *regs;
+       u16 address;
+       u8 length;
+} __attribute__((__packed__));
+
+struct f19_0d_control_3n {
+       u8 sensor_map_button:7;
+};
+
+struct f19_0d_control_3 {
+       struct f19_0d_control_3n *regs;
+       u16 address;
+       u8 length;
+} __attribute__((__packed__));
+
+struct f19_0d_control_4n {
+       u8 sensitivity_button:7;
+};
+
+struct f19_0d_control_4 {
+       struct f19_0d_control_4n *regs;
+       u16 address;
+       u8 length;
+} __attribute__((__packed__));
+
+union f19_0d_control_5 {
+       struct {
+               u8 sensitivity_adj:5;
+       };
+       struct {
+               u8 regs[1];
+               u16 address;
+       } __attribute__((__packed__));
+};
+
+union f19_0d_control_6 {
+       struct {
+               u8 hysteresis_threshold:4;
+       };
+       struct {
+               u8 regs[1];
+               u16 address;
+       } __attribute__((__packed__));
+};
+
+struct f19_0d_control {
+       union f19_0d_control_0 *reg_0;
+       struct f19_0d_control_1 *reg_1;
+       struct f19_0d_control_2 *reg_2;
+       struct f19_0d_control_3 *reg_3;
+       struct f19_0d_control_4 *reg_4;
+       union f19_0d_control_5 *reg_5;
+       union f19_0d_control_6 *reg_6;
+};
+
+/* data specific to fn $19 that needs to be kept around */
+struct f19_data {
+       struct f19_0d_control control;
+       union f19_0d_query query;
+       u8 button_rezero;
+       unsigned char button_count;
+       unsigned char button_bitmask_size;
+       unsigned char *button_data_buffer;
+       unsigned short *button_map;
+       char input_name[MAX_LEN];
+       char input_phys[MAX_LEN];
+       struct input_dev *input;
+       struct mutex control_mutex;
+       struct mutex data_mutex;
+};
+
+
+static int rmi_f19_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f19_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f19_initialize(struct rmi_function_container *fc);
+
+static int rmi_f19_register_device(struct rmi_function_container *fc);
+
+static int rmi_f19_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f19_config(struct rmi_function_container *fc);
+
+static int rmi_f19_reset(struct rmi_function_container *fc);
+
+/* Query sysfs files */
+show_union_struct_prototype(configurable)
+show_union_struct_prototype(has_sensitivity_adjust)
+show_union_struct_prototype(has_hysteresis_threshold)
+show_union_struct_prototype(button_count)
+
+static struct attribute *attrs[] = {
+       attrify(configurable),
+       attrify(has_sensitivity_adjust),
+       attrify(has_hysteresis_threshold),
+       attrify(button_count),
+       NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs);
+/* Control sysfs files */
+show_store_union_struct_prototype(button_usage)
+show_store_union_struct_prototype(filter_mode)
+show_store_union_struct_prototype(int_enabled_button)
+show_store_union_struct_prototype(single_button)
+show_store_union_struct_prototype(sensitivity_button)
+show_store_union_struct_prototype(sensitivity_adj)
+show_store_union_struct_prototype(hysteresis_threshold)
+
+
+static struct attribute *attrsCtrl[] = {
+       attrify(button_usage),
+       attrify(filter_mode),
+       attrify(int_enabled_button),
+       attrify(single_button),
+       attrify(sensitivity_button),
+       NULL
+};
+static struct attribute_group attrs_control = GROUP(attrsCtrl);
+
+static ssize_t rmi_fn_19_sensor_map_button_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf);
+static ssize_t rmi_fn_19_sensor_map_button_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count);
+static struct device_attribute sensor_map_attr = __ATTR(sensor_map_button,
+                       RMI_RW_ATTR, rmi_fn_19_sensor_map_button_show,
+                       rmi_fn_19_sensor_map_button_store);
+
+static struct device_attribute sensor_map_ro_attr =  __ATTR(sensor_map_button,
+                       RMI_RO_ATTR, rmi_fn_19_sensor_map_button_show,
+                        rmi_store_error);
+
+/* Command sysfs files */
+static ssize_t f19_rezero_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count);
+static DEVICE_ATTR(rezero, RMI_WO_ATTR,
+               rmi_show_error,
+               f19_rezero_store);
+
+static struct attribute *attrsCommand[] = {
+       attrify(rezero),
+       NULL
+};
+static struct attribute_group attrs_command = GROUP(attrsCommand);
+
+int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
+       struct f19_data *f19)
+{
+       int retval = 0;
+       union f19_0d_query *query = &f19->query;
+       struct f19_0d_control *control = &f19->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;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_1->address,
+                       (u8 *)control->reg_1->regs, f19->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;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_2->address,
+                       (u8 *)control->reg_2->regs, f19->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;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_3->address,
+                       (u8 *)control->reg_3->regs, f19->button_count);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev, "Could not read control reg3 to 
%#06x.\n",
+                        control->reg_3->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_4->address,
+                       (u8 *)control->reg_4->regs, f19->button_count);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev, "Could not read control reg3 to 
%#06x.\n",
+                        control->reg_4->address);
+               return retval;
+       }
+
+       if (query->has_sensitivity_adjust) {
+               retval = rmi_read_block(rmi_dev, control->reg_5->address,
+                               (u8 *)control->reg_5->regs,
+                               sizeof(control->reg_5->regs));
+               if (retval < 0) {
+                       dev_err(&rmi_dev->dev, "Could not read control reg5 to 
%#06x.\n",
+                               control->reg_5->address);
+                       return retval;
+               }
+       }
+       if (query->has_hysteresis_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 reg6 to 
%#06x.\n",
+                               control->reg_6->address);
+                       return retval;
+               }
+       }
+       return 0;
+}
+static int rmi_f19_init(struct rmi_function_container *fc)
+{
+       int rc = rmi_f19_alloc_memory(fc);
+       if (rc < 0)
+               goto err_free_data;
+
+       rc = rmi_f19_initialize(fc);
+       if (rc < 0)
+               goto err_free_data;
+
+       rc = rmi_f19_register_device(fc);
+       if (rc < 0)
+               goto err_free_data;
+
+       rc = rmi_f19_create_sysfs(fc);
+       if (rc < 0)
+               goto err_free_data;
+
+       return 0;
+
+err_free_data:
+       rmi_f19_free_memory(fc);
+       return rc;
+}
+
+
+static int rmi_f19_alloc_memory(struct rmi_function_container *fc)
+{
+       struct f19_data *f19;
+       int rc;
+       u16 ctrl_base_addr;
+
+       /* allow memory for fn19 data */
+       f19 = kzalloc(sizeof(struct f19_data), GFP_KERNEL);
+       if (!f19) {
+               dev_err(&fc->dev, "Failed to allocate function data.\n");
+               return -ENOMEM;
+       }
+       fc->data = f19;
+       rc = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+                               (u8 *)&f19->query,
+                               sizeof(f19->query.regs));
+       if (rc < 0) {
+               dev_err(&fc->dev, "Failed to read query register.\n");
+               return rc;
+       }
+       f19->query.address = fc->fd.query_base_addr;
+
+       f19->button_bitmask_size = sizeof(u8)*(f19->query.button_count + 7) / 8;
+       f19->button_data_buffer = kcalloc(f19->button_bitmask_size,
+                   sizeof(unsigned char), GFP_KERNEL);
+       if (!f19->button_data_buffer) {
+               dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+               return -ENOMEM;
+       }
+
+       f19->button_map = kcalloc(f19->query.button_count,
+                               sizeof(unsigned short), GFP_KERNEL);
+       if (!f19->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;
+       f19->control.reg_0 =
+                       kzalloc(sizeof(f19->control.reg_0->regs), GFP_KERNEL);
+       if (!f19->control.reg_0) {
+               dev_err(&fc->dev, "Failed to allocate reg_0 control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_0->address = ctrl_base_addr;
+       ctrl_base_addr += sizeof(f19->control.reg_0->regs);
+
+       /* reg 1 */
+       f19->control.reg_1 =
+                       kzalloc(sizeof(struct f19_0d_control_1), GFP_KERNEL);
+       if (!f19->control.reg_1) {
+               dev_err(&fc->dev, "Failed to allocate reg_1 control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_1->regs =
+                       kzalloc(f19->button_bitmask_size, GFP_KERNEL);
+       if (!f19->control.reg_1->regs) {
+               dev_err(&fc->dev, "Failed to allocate reg_1->regs control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_1->address = ctrl_base_addr;
+       f19->control.reg_1->length = f19->button_bitmask_size;
+       ctrl_base_addr += f19->button_bitmask_size;
+
+       /* reg 2 */
+       f19->control.reg_2 =
+                       kzalloc(sizeof(struct f19_0d_control_2), GFP_KERNEL);
+       if (!f19->control.reg_2) {
+               dev_err(&fc->dev, "Failed to allocate reg_2 control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_2->regs =
+                       kzalloc(f19->button_bitmask_size, GFP_KERNEL);
+       if (!f19->control.reg_2->regs) {
+               dev_err(&fc->dev, "Failed to allocate reg_2->regs control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_2->address = ctrl_base_addr;
+       f19->control.reg_2->length = f19->button_bitmask_size;
+       ctrl_base_addr += f19->button_bitmask_size;
+
+       /* reg 3 */
+       f19->control.reg_3 =
+                       kzalloc(sizeof(struct f19_0d_control_3), GFP_KERNEL);
+       if (!f19->control.reg_3) {
+               dev_err(&fc->dev, "Failed to allocate reg_3 control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_3->regs =
+                       kzalloc(f19->query.button_count, GFP_KERNEL);
+       if (!f19->control.reg_3->regs) {
+               dev_err(&fc->dev, "Failed to allocate reg_3->regs control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_3->address = ctrl_base_addr;
+       f19->control.reg_3->length = f19->query.button_count;
+       ctrl_base_addr += f19->query.button_count;
+
+       /* reg 4 */
+       f19->control.reg_4 =
+                       kzalloc(sizeof(struct f19_0d_control_4), GFP_KERNEL);
+       if (!f19->control.reg_4) {
+               dev_err(&fc->dev, "Failed to allocate reg_3 control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_4->regs =
+                       kzalloc(f19->query.button_count, GFP_KERNEL);
+       if (!f19->control.reg_4->regs) {
+               dev_err(&fc->dev, "Failed to allocate reg_3->regs control 
registers.");
+               return -ENOMEM;
+       }
+       f19->control.reg_4->address = ctrl_base_addr;
+       f19->control.reg_4->length = f19->query.button_count;
+       ctrl_base_addr += f19->query.button_count;
+
+       /* reg 5 */
+       if (f19->query.has_sensitivity_adjust) {
+               f19->control.reg_5 =
+                       kzalloc(sizeof(f19->control.reg_5->regs), GFP_KERNEL);
+               if (!f19->control.reg_5) {
+                       dev_err(&fc->dev, "Failed to allocate reg_5 control 
registers.");
+                       return -ENOMEM;
+               }
+               f19->control.reg_5->address = ctrl_base_addr;
+               ctrl_base_addr += sizeof(f19->control.reg_5->regs);
+       }
+       /* reg 6 */
+       if (f19->query.has_hysteresis_threshold) {
+               f19->control.reg_6 =
+                       kzalloc(sizeof(f19->control.reg_6->regs), GFP_KERNEL);
+               if (!f19->control.reg_6) {
+                       dev_err(&fc->dev, "Failed to allocate reg_6 control 
registers.");
+                       return -ENOMEM;
+               }
+               f19->control.reg_6->address = ctrl_base_addr;
+       }
+       return 0;
+}
+
+
+
+static void rmi_f19_free_memory(struct rmi_function_container *fc)
+{
+       union f19_0d_query *query;
+       struct f19_data *f19 = fc->data;
+
+       query = &f19->query;
+       sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+       sysfs_remove_group(&fc->dev.kobj, &attrs_control);
+       if (query->has_sensitivity_adjust)
+               sysfs_remove_file(&fc->dev.kobj, attrify(sensitivity_adj));
+
+       if (query->has_hysteresis_threshold)
+               sysfs_remove_file(&fc->dev.kobj,
+                               attrify(hysteresis_threshold));
+       sysfs_remove_group(&fc->dev.kobj, &attrs_command);
+       if (query->configurable)
+               sysfs_remove_file(&fc->dev.kobj, &sensor_map_attr.attr);
+       else
+               sysfs_remove_file(&fc->dev.kobj, &sensor_map_ro_attr.attr);
+
+       if (f19) {
+               kfree(f19->button_data_buffer);
+               kfree(f19->button_map);
+               kfree(f19->control.reg_0);
+               kfree(f19->control.reg_1);
+               kfree(f19->control.reg_1->regs);
+               kfree(f19->control.reg_2);
+               kfree(f19->control.reg_2->regs);
+               kfree(f19->control.reg_3);
+               kfree(f19->control.reg_3->regs);
+               kfree(f19->control.reg_5);
+               kfree(f19->control.reg_6);
+               kfree(f19);
+               fc->data = NULL;
+       }
+}
+
+static int rmi_f19_initialize(struct rmi_function_container *fc)
+{
+       int i;
+       int rc;
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct rmi_device_platform_data *pdata;
+       struct f19_data *f19 = fc->data;
+
+       /* initial all default values for f19 data here */
+       rc = rmi_read(rmi_dev, fc->fd.command_base_addr,
+               (u8 *)&f19->button_rezero);
+       if (rc < 0) {
+               dev_err(&fc->dev, "Failed to read command register.\n");
+               return rc;
+       }
+       f19->button_rezero = f19->button_rezero & 1;
+
+       pdata = to_rmi_platform_data(rmi_dev);
+       if (pdata) {
+               if (!pdata->f19_button_map)
+                       dev_warn(&fc->dev, "F19 button_map is NULL");
+               else if (!pdata->f19_button_map->map)
+                       dev_warn(&fc->dev,
+                                "Platformdata button map is missing!\n");
+               else {
+                       if (pdata->f19_button_map->nbuttons !=
+                                               f19->query.button_count)
+                               dev_warn(&fc->dev, "Platformdata button map 
size (%d) != number of buttons on device (%d) - ignored.\n",
+                                       pdata->f19_button_map->nbuttons,
+                                       f19->query.button_count);
+                       f19->button_count = min(pdata->f19_button_map->nbuttons,
+                                        (u8) f19->query.button_count);
+                       for (i = 0; i < f19->button_count; i++)
+                               f19->button_map[i] =
+                                       pdata->f19_button_map->map[i];
+               }
+       }
+       rc = rmi_f19_read_control_parameters(rmi_dev, f19);
+       if (rc < 0) {
+               dev_err(&fc->dev,
+                       "Failed to initialize F19 control params.\n");
+               return rc;
+       }
+
+       mutex_init(&f19->control_mutex);
+       mutex_init(&f19->data_mutex);
+       return 0;
+}
+
+static int rmi_f19_register_device(struct rmi_function_container *fc)
+{
+       int i;
+       int rc;
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct f19_data *f19 = fc->data;
+       struct input_dev *input_dev = input_allocate_device();
+
+       if (!input_dev) {
+               dev_err(&fc->dev, "Failed to allocate input device.\n");
+               return -ENOMEM;
+       }
+
+       f19->input = input_dev;
+       snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+               fc->fd.function_number);
+       input_dev->name = f19->input_name;
+       snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+       input_dev->phys = f19->input_phys;
+       input_dev->dev.parent = &rmi_dev->dev;
+       input_set_drvdata(input_dev, f19);
+
+       /* 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 = f19->button_map;
+       input_dev->keycodesize = sizeof(f19->button_map);
+       input_dev->keycodemax = f19->button_count;
+
+       /* set bits for each button... */
+       for (i = 0; i < f19->button_count; i++)
+               set_bit(f19->button_map[i], input_dev->keybit);
+       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_f19_create_sysfs(struct rmi_function_container *fc)
+{
+       struct f19_data *f19 = fc->data;
+       union f19_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 (sysfs_create_group(&fc->dev.kobj, &attrs_control) < 0) {
+               dev_err(&fc->dev, "Failed to create control sysfs files.");
+               return -ENODEV;
+       }
+       if (query->has_sensitivity_adjust) {
+               if (sysfs_create_file(&fc->dev.kobj,
+                               attrify(sensitivity_adj)) < 0) {
+                       dev_err(&fc->dev,
+                               "Failed to create control sysfs files.");
+                       return -ENODEV;
+               }
+       }
+       if (query->has_hysteresis_threshold) {
+               if (sysfs_create_file(&fc->dev.kobj,
+                               attrify(hysteresis_threshold)) < 0) {
+                       dev_err(&fc->dev,
+                               "Failed to create control sysfs files.");
+                       return -ENODEV;
+               }
+       }
+       if (sysfs_create_group(&fc->dev.kobj, &attrs_command) < 0) {
+               dev_err(&fc->dev, "Failed to create command sysfs files.");
+               return -ENODEV;
+       }
+       if (query->configurable) {
+               if (sysfs_create_file(&fc->dev.kobj,
+                                       &sensor_map_attr.attr) < 0) {
+                       dev_err(&fc->dev,
+                               "Failed to create control sysfs files.");
+                       return -ENODEV;
+               }
+       } else {
+               if (sysfs_create_file(&fc->dev.kobj,
+                                       &sensor_map_ro_attr.attr) < 0) {
+                       dev_err(&fc->dev,
+                               "Failed to create control sysfs files.");
+                       return -ENODEV;
+               }
+       }
+       return 0;
+}
+
+static int rmi_f19_config(struct rmi_function_container *fc)
+{
+       struct f19_data *f19 = fc->data;
+       int retval = 0;
+       union f19_0d_query *query = &f19->query;
+       struct f19_0d_control *control = &f19->control;
+
+       retval = rmi_write_block(fc->rmi_dev, control->reg_0->address,
+                       (u8 *)control->reg_0->regs,
+                       sizeof(u8));
+       if (retval < 0) {
+               dev_err(&fc->dev, "Could not write control reg0 to %#06x.\n",
+                               control->reg_0->address);
+               return retval;
+       }
+
+       retval = rmi_write_block(fc->rmi_dev, control->reg_1->address,
+                       (u8 *)control->reg_1->regs, f19->button_bitmask_size);
+       if (retval < 0) {
+               dev_err(&fc->dev, "Could not write control reg1 to %#06x.\n",
+                        control->reg_1->address);
+               return retval;
+       }
+
+       retval = rmi_write_block(fc->rmi_dev, control->reg_2->address,
+                       (u8 *)control->reg_2->regs, f19->button_bitmask_size);
+       if (retval < 0) {
+               dev_err(&fc->dev, "Could not write control reg2 to %#06x.\n",
+                        control->reg_2->address);
+               return retval;
+       }
+
+       retval = rmi_write_block(fc->rmi_dev, control->reg_3->address,
+                       (u8 *)control->reg_3->regs, query->button_count);
+       if (retval < 0) {
+               dev_err(&fc->dev, "Could not write control reg3 to %#06x.\n",
+                        control->reg_3->address);
+               return retval;
+       }
+
+       retval = rmi_write_block(fc->rmi_dev, control->reg_4->address,
+                       (u8 *)control->reg_4->regs, query->button_count);
+       if (retval < 0) {
+               dev_err(&fc->dev, "Could not write control reg3 to %#06x.\n",
+                        control->reg_4->address);
+               return retval;
+       }
+
+       if (query->has_sensitivity_adjust) {
+               retval = rmi_write_block(fc->rmi_dev, control->reg_5->address,
+                               (u8 *)control->reg_5->regs,
+                               sizeof(control->reg_5->regs));
+               if (retval < 0) {
+                       dev_err(&fc->dev,
+                               "Could not write control reg5 to %#06x.\n",
+                               control->reg_5->address);
+                       return retval;
+               }
+       }
+       if (query->has_hysteresis_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,
+                               "Could not write control reg6 to %#06x.\n",
+                               control->reg_6->address);
+                       return retval;
+               }
+       }
+       return 0;
+}
+
+
+static int rmi_f19_reset(struct rmi_function_container *fc)
+{
+       /* we do nnothing here */
+       return 0;
+}
+
+
+static void rmi_f19_remove(struct rmi_function_container *fc)
+{
+       struct f19_data *f19 = fc->data;
+
+       input_unregister_device(f19->input);
+       rmi_f19_free_memory(fc);
+}
+
+static int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+       int error;
+       int button;
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct f19_data *f19 = fc->data;
+       u16 data_base_addr = fc->fd.data_base_addr;
+
+       /* Read the button data. */
+       error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
+                       f19->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. */
+       for (button = 0; button < f19->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 =
+                   ((f19->button_data_buffer[button_reg] >> button_shift)
+                       & 0x01) != 0;
+               /* Generate an event here. */
+               input_report_key(f19->input, f19->button_map[button],
+                                button_status);
+       }
+
+       input_sync(f19->input); /* sync after groups of events */
+       return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+       .func = 0x19,
+       .init = rmi_f19_init,
+       .config = rmi_f19_config,
+       .reset = rmi_f19_reset,
+       .attention = rmi_f19_attention,
+       .remove = rmi_f19_remove
+};
+
+static int __init rmi_f19_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_f19_module_exit(void)
+{
+       rmi_unregister_function_driver(&function_handler);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, configurable)
+simple_show_union_struct_unsigned(query, has_sensitivity_adjust)
+simple_show_union_struct_unsigned(query, has_hysteresis_threshold)
+simple_show_union_struct_unsigned(query, button_count)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_0, button_usage)
+show_store_union_struct_unsigned(control, reg_0, filter_mode)
+show_store_union_struct_unsigned(control, reg_5, sensitivity_adj)
+show_store_union_struct_unsigned(control, reg_6, hysteresis_threshold)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_1, int_enabled_button)
+show_store_repeated_union_struct_unsigned(control, reg_2, single_button)
+show_store_repeated_union_struct_unsigned(control, reg_3, sensor_map_button)
+show_store_repeated_union_struct_unsigned(control, reg_4, sensitivity_button)
+
+/* command */
+static ssize_t f19_rezero_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf,
+                                        size_t count)
+{
+       unsigned int new_value;
+       int len;
+       struct f19_data *f19;
+       struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+       f19 = fc->data;
+       len = sscanf(buf, "%u", &new_value);
+       if (new_value != 0 && new_value != 1) {
+               dev_err(dev, "%s: Error - rezero is not a valid value 0x%x.\n",
+                       __func__, new_value);
+               return -EINVAL;
+       }
+       f19->button_rezero = new_value & 1;
+       len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+               f19->button_rezero);
+
+       if (len < 0) {
+               dev_err(dev, "%s : Could not write rezero to 0x%x\n",
+                               __func__, fc->fd.command_base_addr);
+               return -EINVAL;
+       }
+       return count;
+}
+
+
+
+module_init(rmi_f19_module_init);
+module_exit(rmi_f19_module_exit);
+
+MODULE_AUTHOR("Vivian Ly <v...@synaptics.com>");
+MODULE_DESCRIPTION("RMI F19 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to