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_f17.c |  713 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 713 insertions(+), 0 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f17.c b/drivers/input/rmi4/rmi_f17.c
new file mode 100644
index 0000000..006a716
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f17.c
@@ -0,0 +1,713 @@
+/*
+ * 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.
+ */
+#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_LENGTH 256
+
+union f17_device_query {
+       struct {
+               u8 number_of_sticks:3;
+       };
+       u8 regs[1];
+};
+
+#define F17_MANUFACTURER_SYNAPTICS 0
+#define F17_MANUFACTURER_NMB 1
+#define F17_MANUFACTURER_ALPS 2
+
+struct f17_stick_query {
+       union {
+               struct {
+                       u8 manufacturer:4;
+                       u8 resistive:1;
+                       u8 ballistics:1;
+                       u8 reserved1:2;
+                       u8 has_relative:1;
+                       u8 has_absolute:1;
+                       u8 has_gestures:1;
+                       u8 has_dribble:1;
+                       u8 reserved2:4;
+               };
+               u8 regs[2];
+       } general;
+
+       union {
+               struct {
+                       u8 has_single_tap:1;
+                       u8 has_tap_and_hold:1;
+                       u8 has_double_tap:1;
+                       u8 has_early_tap:1;
+                       u8 has_press:1;
+               };
+               u8 regs[1];
+       } gestures;
+};
+
+union f17_device_controls {
+       struct {
+               u8 reporting_mode:3;
+               u8 dribble:1;
+       };
+       u8 regs[1];
+};
+
+struct f17_stick_controls {
+       union {
+               struct {
+                       u8 z_force_threshold;
+                       u8 radial_force_threshold;
+               };
+               u8 regs[3];
+       } general;
+
+       union {
+               struct {
+                       u8 motion_sensitivity:4;
+                       u8 antijitter:1;
+               };
+               u8 regs[1];
+       } relative;
+
+       union {
+               struct {
+                       u8 single_tap:1;
+                       u8 tap_and_hold:1;
+
+                       u8 double_tap:1;
+                       u8 early_tap:1;
+                       u8 press:1;
+               };
+               u8 regs[1];
+       } enable;
+
+       u8 maximum_tap_time;
+       u8 minimum_press_time;
+       u8 maximum_radial_force;
+};
+
+
+union f17_device_commands {
+       struct {
+               u8 rezero:1;
+       };
+       u8 regs[1];
+};
+
+struct f17_stick_data {
+       union {
+               struct {
+                       u8 x_force_high:8;
+                       u8 y_force_high:8;
+                       u8 y_force_low:4;
+                       u8 x_force_low:4;
+                       u8 z_force:8;
+               } __attribute__((__packed__));
+               struct {
+                       u8 regs[4];
+                       u16 address;
+               };
+       } abs;
+       union {
+               struct {
+                       s8 x_delta:8;
+                       s8 y_delta:8;
+               } __attribute__((__packed__));
+               struct {
+                       u8 regs[2];
+                       u16 address;
+               };
+       } rel;
+       union {
+               struct {
+                       u8 single_tap:1;
+                       u8 tap_and_hold:1;
+                       u8 double_tap:1;
+                       u8 early_tap:1;
+                       u8 press:1;
+                       u8 reserved:3;
+               } __attribute__((__packed__));
+               struct {
+                       u8 regs[1];
+                       u16 address;
+               };
+       } gestures;
+};
+
+
+/* data specific to f17 that needs to be kept around */
+
+struct rmi_f17_stick_data {
+       struct f17_stick_query query;
+       struct f17_stick_controls controls;
+       struct f17_stick_data data;
+
+       u16 control_address;
+
+       int index;
+
+       char input_name[MAX_NAME_LENGTH];
+       char input_phys[MAX_NAME_LENGTH];
+       struct input_dev *input;
+       char mouse_name[MAX_NAME_LENGTH];
+       char mouse_phys[MAX_NAME_LENGTH];
+       struct input_dev *mouse;
+};
+
+struct rmi_f17_device_data {
+       u16 control_address;
+
+       union f17_device_query query;
+       union f17_device_commands commands;
+       union f17_device_controls controls;
+
+       struct rmi_f17_stick_data *sticks;
+
+};
+
+static ssize_t f17_rezero_show(struct device *dev,
+                                        struct device_attribute *attr,
+                                        char *buf);
+static ssize_t f17_rezero_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf, size_t count);
+
+
+
+static int f17_alloc_memory(struct rmi_function_container *fc);
+
+static void f17_free_memory(struct rmi_function_container *fc);
+
+static int f17_initialize(struct rmi_function_container *fc);
+
+static int f17_register_devices(struct rmi_function_container *fc);
+
+static int f17_create_sysfs(struct rmi_function_container *fc);
+
+static int f17_config(struct rmi_function_container *fc);
+
+
+static struct device_attribute attrs[] = {
+       __ATTR(rezero, RMI_RW_ATTR,
+               f17_rezero_show, f17_rezero_store),
+};
+
+
+int f17_read_control_parameters(struct rmi_device *rmi_dev,
+       struct rmi_f17_device_data *f17)
+{
+       int retval = 0;
+
+       /* TODO: read this or delete the function */
+
+       return retval;
+}
+
+
+static int f17_init(struct rmi_function_container *fc)
+{
+       int retval;
+
+       retval = f17_alloc_memory(fc);
+       if (retval < 0)
+               goto err_free_data;
+
+       retval = f17_initialize(fc);
+       if (retval < 0)
+               goto err_free_data;
+
+       retval = f17_register_devices(fc);
+       if (retval < 0)
+               goto err_free_data;
+
+       retval = f17_create_sysfs(fc);
+       if (retval < 0)
+               goto err_free_data;
+
+       return 0;
+
+err_free_data:
+       f17_free_memory(fc);
+
+       return retval;
+}
+
+static int f17_alloc_memory(struct rmi_function_container *fc)
+{
+       struct rmi_f17_device_data *f17;
+       int retval;
+
+       f17 = kzalloc(sizeof(struct rmi_f17_device_data), GFP_KERNEL);
+       if (!f17) {
+               dev_err(&fc->dev, "Failed to allocate function data.\n");
+               return -ENOMEM;
+       }
+       fc->data = f17;
+
+       retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+                               f17->query.regs, sizeof(f17->query.regs));
+       if (retval < 0) {
+               dev_err(&fc->dev, "Failed to read query register.\n");
+               goto error_exit;
+       }
+
+       f17->sticks = kcalloc(f17->query.number_of_sticks + 1,
+                       sizeof(struct rmi_f17_stick_data), GFP_KERNEL);
+       if (!f17->sticks) {
+               dev_err(&fc->dev, "Failed to allocate per stick data.\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+
+error_exit:
+       kfree(f17);
+       fc->data = NULL;
+       return retval;
+}
+
+static void f17_free_memory(struct rmi_function_container *fc)
+{
+       struct rmi_f17_device_data *f17 = fc->data;
+
+       if (f17) {
+               kfree(f17->sticks);
+               f17->sticks = NULL;
+       }
+       kfree(f17);
+       fc->data = NULL;
+}
+
+static int f17_init_stick(struct rmi_device *rmi_dev,
+                         struct rmi_f17_stick_data *stick,
+                         u16 *next_query_reg, u16 *next_data_reg,
+                         u16 *next_control_reg) {
+       int retval = 0;
+
+       retval = rmi_read_block(rmi_dev, *next_query_reg,
+               stick->query.general.regs,
+               sizeof(stick->query.general.regs));
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev, "Failed to read stick general query.\n");
+               return retval;
+       }
+       *next_query_reg += sizeof(stick->query.general.regs);
+
+       dev_dbg(&rmi_dev->dev, "Stick %d found\n", stick->index);
+       dev_dbg(&rmi_dev->dev, "    Manufacturer: %d.\n",
+                               stick->query.general.manufacturer);
+       dev_dbg(&rmi_dev->dev, "    Resistive:    %d.\n",
+                               stick->query.general.resistive);
+       dev_dbg(&rmi_dev->dev, "    Ballistics:   %d.\n",
+                               stick->query.general.ballistics);
+       dev_dbg(&rmi_dev->dev, "    Manufacturer: %d.\n",
+                               stick->query.general.ballistics);
+       dev_dbg(&rmi_dev->dev, "    Has relative: %d.\n",
+                               stick->query.general.has_relative);
+       dev_dbg(&rmi_dev->dev, "    Has absolute: %d.\n",
+                               stick->query.general.has_absolute);
+       dev_dbg(&rmi_dev->dev, "    Had dribble:  %d.\n",
+                               stick->query.general.has_dribble);
+       dev_dbg(&rmi_dev->dev, "    Has gestures: %d.\n",
+                               stick->query.general.has_gestures);
+
+       if (stick->query.general.has_gestures) {
+               retval = rmi_read_block(rmi_dev, *next_query_reg,
+                       stick->query.gestures.regs,
+                       sizeof(stick->query.gestures.regs));
+               if (retval < 0) {
+                       dev_err(&rmi_dev->dev,
+                               "Failed to read F17 gestures query, code %d.\n",
+                               retval);
+                       return retval;
+               }
+               *next_query_reg += sizeof(stick->query.gestures.regs);
+               dev_dbg(&rmi_dev->dev, "        single tap: %d.\n",
+                                       stick->query.gestures.has_single_tap);
+               dev_dbg(&rmi_dev->dev, "        tap & hold: %d.\n",
+                                       stick->query.gestures.has_tap_and_hold);
+               dev_dbg(&rmi_dev->dev, "        double tap: %d.\n",
+                                       stick->query.gestures.has_double_tap);
+               dev_dbg(&rmi_dev->dev, "        early tap:  %d.\n",
+                                       stick->query.gestures.has_early_tap);
+               dev_dbg(&rmi_dev->dev, "        press:      %d.\n",
+                                       stick->query.gestures.has_press);
+       }
+       if (stick->query.general.has_absolute) {
+               stick->data.abs.address = *next_data_reg;
+               *next_data_reg += sizeof(stick->data.abs.regs);
+       }
+       if (stick->query.general.has_relative) {
+               stick->data.rel.address = *next_data_reg;
+               *next_data_reg += sizeof(stick->data.rel.regs);
+       }
+       if (stick->query.general.has_gestures) {
+               stick->data.gestures.address = *next_data_reg;
+               *next_data_reg += sizeof(stick->data.gestures.regs);
+       }
+
+       return retval;
+}
+
+static int f17_initialize(struct rmi_function_container *fc)
+{
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct rmi_f17_device_data *f17 = fc->data;
+       int i;
+       int retval;
+       u16 next_query_reg = fc->fd.query_base_addr;
+       u16 next_data_reg = fc->fd.data_base_addr;
+       u16 next_control_reg = fc->fd.control_base_addr;
+
+       retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+                               f17->query.regs, sizeof(f17->query.regs));
+       if (retval < 0) {
+               dev_err(&fc->dev, "Failed to read query register.\n");
+               return retval;
+       }
+       dev_info(&fc->dev, "Found F17 with %d sticks.\n",
+                f17->query.number_of_sticks + 1);
+       next_query_reg += sizeof(f17->query.regs);
+
+       retval = rmi_read_block(rmi_dev, fc->fd.command_base_addr,
+               f17->commands.regs, sizeof(f17->commands.regs));
+       if (retval < 0) {
+               dev_err(&fc->dev, "Failed to read command register.\n");
+               return retval;
+       }
+
+       f17->control_address = fc->fd.control_base_addr;
+       retval = f17_read_control_parameters(rmi_dev, f17);
+       if (retval < 0) {
+               dev_err(&fc->dev, "Failed to initialize F17 control params.\n");
+               return retval;
+       }
+
+       for (i = 0; i < f17->query.number_of_sticks + 1; i++) {
+               f17->sticks[i].index = i;
+               retval = f17_init_stick(rmi_dev, &f17->sticks[i],
+                                       &next_query_reg, &next_data_reg,
+                                       &next_control_reg);
+               if (!retval) {
+                       dev_err(&fc->dev, "Failed to init stick %d.\n", i);
+                       return retval;
+               }
+       }
+
+       return retval;
+}
+
+static int f17_register_stick(struct rmi_function_container *fc,
+                             struct rmi_f17_stick_data *stick) {
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       int retval = 0;
+
+       if (stick->query.general.has_absolute) {
+               struct input_dev *input_dev;
+               input_dev = input_allocate_device();
+               if (!input_dev) {
+                       dev_err(&rmi_dev->dev, "Failed to allocate stick device 
%d.\n",
+                               stick->index);
+                       return -ENOMEM;
+               }
+
+               snprintf(stick->input_name, sizeof(stick->input_name),
+                       "RMI F%02x Stick %d", 0x17, stick->index);
+               snprintf(stick->input_phys, sizeof(stick->input_phys),
+                        "sensor00fn%02x/stick%d", 0x17, stick->index);
+               input_dev->name = stick->input_name;
+               input_dev->phys = stick->input_phys;
+               input_dev->dev.parent = &fc->dev;
+               input_set_drvdata(input_dev, stick);
+
+               retval = input_register_device(input_dev);
+               if (retval < 0) {
+                       dev_err(&rmi_dev->dev, "Failed to register stick device 
%d.\n",
+                               stick->index);
+                       goto error_free_device;
+               }
+               stick->input = input_dev;
+       }
+
+       if (stick->query.general.has_relative) {
+               struct input_dev *input_dev_mouse;
+               /*create input device for mouse events  */
+               input_dev_mouse = input_allocate_device();
+               if (!input_dev_mouse) {
+                       retval = -ENOMEM;
+                       goto error_free_device;
+               }
+
+               snprintf(stick->mouse_name, sizeof(stick->mouse_name),
+                       "RMI F%02x Mouse %d", 0x17, stick->index);
+               snprintf(stick->mouse_phys, sizeof(stick->mouse_name),
+                        "sensor00fn%02x/mouse%d", 0x17, stick->index);
+               input_dev_mouse->name = stick->mouse_name;
+               input_dev_mouse->phys = stick->mouse_phys;
+               input_dev_mouse->dev.parent = &fc->dev;
+
+               input_dev_mouse->id.vendor  = 0x18d1;
+               input_dev_mouse->id.product = 0x0210;
+               input_dev_mouse->id.version = 0x0100;
+
+               set_bit(EV_REL, input_dev_mouse->evbit);
+               set_bit(REL_X, input_dev_mouse->relbit);
+               set_bit(REL_Y, input_dev_mouse->relbit);
+
+               set_bit(BTN_MOUSE, input_dev_mouse->evbit);
+               /* Register device's buttons and keys */
+               set_bit(EV_KEY, input_dev_mouse->evbit);
+               set_bit(BTN_LEFT, input_dev_mouse->keybit);
+               set_bit(BTN_MIDDLE, input_dev_mouse->keybit);
+               set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+
+               retval = input_register_device(input_dev_mouse);
+               if (retval < 0)
+                       goto error_free_device;
+               stick->mouse = input_dev_mouse;
+       }
+
+       return 0;
+
+error_free_device:
+       if (stick->input) {
+               input_free_device(stick->input);
+               stick->input = NULL;
+       }
+       if (stick->mouse) {
+               input_free_device(stick->mouse);
+               stick->mouse = NULL;
+       }
+       return retval;
+}
+
+static int f17_register_devices(struct rmi_function_container *fc)
+{
+       struct rmi_f17_device_data *f17 = fc->data;
+       int i;
+       int retval = 0;
+
+       for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++)
+               retval = f17_register_stick(fc, &f17->sticks[i]);
+
+       return retval;
+}
+
+static int f17_create_sysfs(struct rmi_function_container *fc)
+{
+       int attr_count = 0;
+       int rc;
+
+       dev_dbg(&fc->dev, "Creating sysfs files.\n");
+       /* 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:
+       for (attr_count--; attr_count >= 0; attr_count--)
+               sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+       return rc;
+
+}
+
+static int f17_config(struct rmi_function_container *fc)
+{
+       struct rmi_f17_device_data *f17 = fc->data;
+       int retval;
+
+       retval = rmi_write_block(fc->rmi_dev, f17->control_address,
+               f17->controls.regs, sizeof(f17->controls.regs));
+       if (retval < 0) {
+               dev_err(&fc->dev, "Could not write stick controls to 0x%04x\n",
+                               f17->control_address);
+               return retval;
+       }
+
+       return retval;
+}
+
+static void f17_remove(struct rmi_function_container *fc)
+{
+       struct rmi_f17_device_data *f17 = fc->data;
+       int i = 0;
+
+       for (i = 0; i < ARRAY_SIZE(attrs); i++)
+               sysfs_remove_file(&fc->dev.kobj, &attrs[i].attr);
+
+       for (i = 0; i < f17->query.number_of_sticks + 1; i++)
+               input_unregister_device(f17->sticks[i].input);
+
+       f17_free_memory(fc);
+}
+
+static int f17_process_stick(struct rmi_device *rmi_dev,
+                            struct rmi_f17_stick_data *stick) {
+       int retval = 0;
+
+       if (stick->query.general.has_absolute) {
+               retval = rmi_read_block(rmi_dev, stick->data.abs.address,
+                       stick->data.abs.regs, sizeof(stick->data.abs.regs));
+               if (retval < 0) {
+                       dev_err(&rmi_dev->dev,
+                               "Failed to read abs data for stick %d, code 
%d.\n",
+                               stick->index, retval);
+                       goto error_exit;
+               }
+       }
+       if (stick->query.general.has_relative) {
+               retval = rmi_read_block(rmi_dev, stick->data.rel.address,
+                       stick->data.rel.regs, sizeof(stick->data.rel.regs));
+               if (retval < 0) {
+                       dev_err(&rmi_dev->dev,
+                               "Failed to read rel data for stick %d, code 
%d.\n",
+                               stick->index, retval);
+                       goto error_exit;
+               }
+               dev_dbg(&rmi_dev->dev, "Reporting dX: %d, dy: %d\n",
+                       stick->data.rel.x_delta, stick->data.rel.y_delta);
+               input_report_rel(stick->mouse, REL_X, stick->data.rel.x_delta);
+               input_report_rel(stick->mouse, REL_Y, stick->data.rel.y_delta);
+       }
+       if (stick->query.general.has_gestures) {
+               retval = rmi_read_block(rmi_dev, stick->data.gestures.address,
+                       stick->data.gestures.regs,
+                       sizeof(stick->data.gestures.regs));
+               if (retval < 0) {
+                       dev_err(&rmi_dev->dev,
+                               "Failed to read gestures for stick %d, code 
%d.\n",
+                               stick->index, retval);
+                       goto error_exit;
+               }
+       }
+       retval = 0;
+
+error_exit:
+       if (stick->input)
+               input_sync(stick->input);
+       if (stick->mouse)
+               input_sync(stick->mouse);
+       return retval;
+}
+
+static int f17_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct rmi_f17_device_data *f17 = fc->data;
+       int i;
+       int retval = 0;
+
+       for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++)
+               retval = f17_process_stick(rmi_dev, &f17->sticks[i]);
+
+       return retval;
+}
+
+static struct rmi_function_handler function_handler = {
+       .func = 0x17,
+       .init = f17_init,
+       .config = f17_config,
+       .attention = f17_attention,
+       .remove = f17_remove
+};
+
+static int __init f17_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 f17_module_exit(void)
+{
+       rmi_unregister_function_driver(&function_handler);
+}
+
+
+static ssize_t f17_rezero_show(struct device *dev,
+                               struct device_attribute *attr,
+                               char *buf)
+{
+       struct rmi_function_container *fc;
+       struct rmi_f17_device_data *f17;
+
+       fc = to_rmi_function_container(dev);
+       f17 = fc->data;
+
+       return snprintf(buf, PAGE_SIZE, "%u\n",
+                       f17->commands.rezero);
+
+}
+
+static ssize_t f17_rezero_store(struct device *dev,
+                                        struct device_attribute *attr,
+                                        const char *buf,
+                                        size_t count)
+{
+       struct rmi_function_container *fc;
+       struct rmi_f17_device_data *data;
+       unsigned int new_value;
+       int len;
+
+       fc = to_rmi_function_container(dev);
+       data = 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;
+       }
+       data->commands.rezero = new_value;
+       len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+               data->commands.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(f17_module_init);
+module_exit(f17_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <che...@synaptics.com>");
+MODULE_DESCRIPTION("RMI F17 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