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

diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c
new file mode 100644
index 0000000..4bc2932
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f30.c
@@ -0,0 +1,1247 @@
+/*
+ * 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 rmi_fn_30_data
+#define FNUM 30
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+
+#define MAX_LEN 256
+
+/* data specific to fn $30 that needs to be kept around */
+union f30_query {
+       struct {
+               u8 extended_patterns:1;
+               u8 has_mappable_buttons:1;
+               u8 has_led:1;
+               u8 has_gpio:1;
+               u8 has_haptic:1;
+               u8 has_gpio_driver_control:1;
+               u8 gpio_led_count:5;
+       };
+       struct {
+               u8 regs[2];
+               u16 address;
+       };
+};
+
+struct f30_gpio_ctrl_0n {
+       u8 led_sel;
+};
+
+struct f30_gpio_ctrl_0 {
+       struct f30_gpio_ctrl_0n *regs;
+       u16 address;
+       u8 length;
+};
+
+union f30_gpio_ctrl_1 {
+       struct {
+               u8 gpio_debounce:1;
+               u8 reserved:3;
+               u8 halt:1;
+               u8 halted:1;
+               u8 reserved2:2;
+       };
+       struct {
+               u8 regs[1];
+               u16 address;
+       };
+};
+
+struct f30_gpio_ctrl_2n {
+       u8 dir;
+};
+
+struct f30_gpio_ctrl_2 {
+       struct f30_gpio_ctrl_2n *regs;
+       u16 address;
+       u8 length;
+};
+
+struct f30_gpio_ctrl_3n {
+       u8 gpiodata;
+};
+
+struct f30_gpio_ctrl_3 {
+       struct f30_gpio_ctrl_3n *regs;
+       u16 address;
+       u8 length;
+};
+
+struct f30_gpio_ctrl_4n {
+       u8 led_act;
+};
+
+struct f30_gpio_ctrl_4 {
+       struct f30_gpio_ctrl_4n *regs;
+       u16 address;
+       u8 length;
+};
+
+struct f30_gpio_ctrl_5n {
+       u8 ramp_period_a;
+       u8 ramp_period_b;
+};
+
+struct f30_gpio_ctrl_5 {
+       struct f30_gpio_ctrl_5n *regs;
+       u16 address;
+       u8 length;
+};
+
+union f30_gpio_ctrl_6n {
+       struct {
+               u8 reserved:1;
+               u8 SPCTRL:3;
+               u8 STRPD:1;
+               u8 reserved2:1;
+               u8 STRPU:1;
+               u8 reserved3:1;
+       };
+       struct {
+               u8 brightness:4;
+               u8 pattern:4;
+       };
+};
+
+struct f30_gpio_ctrl_6 {
+       union f30_gpio_ctrl_6n *regs;
+       u16 address;
+       u8 length;
+};
+
+struct f30_gpio_ctrl_7n {
+       u8 capacity_btn_nbr:5;
+       u8 valid:1;
+       u8 invert:1;
+       u8 open_drain:1;
+};
+
+struct f30_gpio_ctrl_7 {
+       struct f30_gpio_ctrl_7n *regs;
+       u16 address;
+       u8 length;
+};
+
+struct f30_gpio_ctrl_8n {
+       u8 gpio_ctrl8_0;
+       u8 gpio_ctrl8_1;
+};
+
+struct f30_gpio_ctrl_8 {
+       struct f30_gpio_ctrl_8n *regs;
+       u16 address;
+       u8 length;
+};
+
+union f30_gpio_ctrl_9 {
+       struct {
+               u8 haptic_duration;
+       };
+       struct {
+               u8 regs[1];
+               u16 address;
+       };
+};
+
+struct f30_control {
+       struct f30_gpio_ctrl_0 *reg_0;
+       union f30_gpio_ctrl_1 *reg_1;
+       struct f30_gpio_ctrl_2 *reg_2;
+       struct f30_gpio_ctrl_3 *reg_3;
+       struct f30_gpio_ctrl_4 *reg_4;
+       struct f30_gpio_ctrl_5 *reg_5;
+       struct f30_gpio_ctrl_6 *reg_6;
+       struct f30_gpio_ctrl_7 *reg_7;
+       struct f30_gpio_ctrl_8 *reg_8;
+       union f30_gpio_ctrl_9 *reg_9;
+};
+
+struct f30_data_0n {
+       u8 gpi_led_data:1;
+};
+
+struct f30_data_0 {
+       struct f30_data_0n *regs;
+       u16 address;
+       u8 length;
+};
+
+struct f30_data {
+       struct f30_data_0 *datareg_0;
+       u16 address;
+       u8 length;
+};
+
+struct rmi_fn_30_data {
+       union f30_query query;
+       struct f30_data data;
+       struct f30_control control;
+       unsigned char gpioled_count;
+       unsigned char gpioled_bitmask_size;
+       unsigned char gpioled_byte_size;
+       unsigned char *button_data_buffer;
+       unsigned char button_bitmask_size;
+       unsigned short *gpioled_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_f30_alloc_memory(struct rmi_function_container *fc);
+
+static void rmi_f30_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f30_initialize(struct rmi_function_container *fc);
+
+static int rmi_f30_register_device(struct rmi_function_container *fc);
+
+static int rmi_f30_config(struct rmi_function_container *fc);
+
+static int rmi_f30_create_sysfs(struct rmi_function_container *fc);
+
+
+/* Query sysfs files */
+
+show_union_struct_prototype(extended_patterns)
+show_union_struct_prototype(has_mappable_buttons)
+show_union_struct_prototype(has_led)
+show_union_struct_prototype(has_gpio)
+show_union_struct_prototype(has_haptic)
+show_union_struct_prototype(has_gpio_driver_control)
+show_union_struct_prototype(gpio_led_count)
+
+static struct attribute *attrs1[] = {
+       attrify(extended_patterns),
+       attrify(has_mappable_buttons),
+       attrify(has_led),
+       attrify(has_gpio),
+       attrify(has_haptic),
+       attrify(has_gpio_driver_control),
+       attrify(gpio_led_count),
+       NULL
+};
+
+static struct attribute_group attrs_query = GROUP(attrs1);
+
+/* Control sysfs files */
+
+show_store_union_struct_prototype(led_sel)
+show_store_union_struct_prototype(gpio_debounce)
+show_store_union_struct_prototype(halt)
+show_store_union_struct_prototype(halted)
+show_store_union_struct_prototype(dir)
+show_store_union_struct_prototype(gpiodata)
+show_store_union_struct_prototype(led_act)
+show_store_union_struct_prototype(ramp_period_a)
+show_store_union_struct_prototype(ramp_period_b)
+show_store_union_struct_prototype(SPCTRL)
+show_store_union_struct_prototype(STRPD)
+show_store_union_struct_prototype(STRPU)
+show_store_union_struct_prototype(brightness)
+show_store_union_struct_prototype(pattern)
+
+show_store_union_struct_prototype(capacity_btn_nbr)
+show_store_union_struct_prototype(valid)
+show_store_union_struct_prototype(invert)
+show_store_union_struct_prototype(open_drain)
+show_store_union_struct_prototype(gpio_ctrl8_0)
+show_store_union_struct_prototype(gpio_ctrl8_1)
+show_store_union_struct_prototype(haptic_duration)
+
+/* Data sysfs files */
+show_store_union_struct_prototype(gpi_led_data)
+
+static struct attribute *attrs_ctrl_reg_0[] = {
+       attrify(led_sel),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_1[] = {
+       attrify(gpio_debounce),
+       attrify(halt),
+       attrify(halted),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_2[] = {
+       attrify(dir),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_3[] = {
+       attrify(gpiodata),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_4[] = {
+       attrify(led_act),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_5[] = {
+       attrify(ramp_period_a),
+       attrify(ramp_period_b),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_6_gpio[] = {
+       attrify(SPCTRL),
+       attrify(STRPD),
+       attrify(STRPU),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_6_led[] = {
+       attrify(brightness),
+       attrify(pattern),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_7[] = {
+       attrify(capacity_btn_nbr),
+       attrify(valid),
+       attrify(invert),
+       attrify(open_drain),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_8[] = {
+       attrify(gpio_ctrl8_0),
+       attrify(gpio_ctrl8_1),
+       NULL
+};
+
+static struct attribute *attrs_ctrl_reg_9[] = {
+       attrify(haptic_duration),
+       NULL
+};
+
+static struct attribute_group attrs_ctrl_regs[] = {
+       GROUP(attrs_ctrl_reg_0),
+       GROUP(attrs_ctrl_reg_1),
+       GROUP(attrs_ctrl_reg_2),
+       GROUP(attrs_ctrl_reg_3),
+       GROUP(attrs_ctrl_reg_4),
+       GROUP(attrs_ctrl_reg_5),
+       GROUP(attrs_ctrl_reg_6_gpio),
+       GROUP(attrs_ctrl_reg_6_led),
+       GROUP(attrs_ctrl_reg_7),
+       GROUP(attrs_ctrl_reg_8),
+       GROUP(attrs_ctrl_reg_9),
+};
+
+bool f30_attrs_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)];
+
+static struct attribute *attrs_gpileddata[] = {
+       attrify(gpi_led_data),
+       NULL
+};
+
+static struct attribute_group attrs_data = GROUP(attrs_gpileddata);
+
+int rmi_f30_read_control_parameters(struct rmi_device *rmi_dev,
+       struct rmi_fn_30_data *f30)
+{
+       int retval = 0;
+       struct f30_control *control = &f30->control;
+
+       retval = rmi_read_block(rmi_dev, control->reg_0->address,
+                       (u8 *)control->reg_0->regs,
+                       control->reg_0->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg0 to 0x%x\n",
+                       __func__, control->reg_0->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_1->address,
+                       (u8 *)control->reg_1->regs,
+                       sizeof(control->reg_1->regs));
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg1 to 0x%x\n",
+                        __func__, control->reg_1->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_2->address,
+                       (u8 *)control->reg_2->regs, control->reg_2->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg_2 to 0x%x\n",
+                        __func__, control->reg_2->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_3->address,
+                       (u8 *)control->reg_3->regs, control->reg_3->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg_3 to 0x%x\n",
+                        __func__, control->reg_3->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_4->address,
+                       (u8 *)control->reg_4->regs, control->reg_4->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg4 to 0x%x\n",
+                        __func__, control->reg_4->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_5->address,
+                       (u8 *)control->reg_5->regs,
+                       control->reg_5->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg5 to 0x%x\n", __func__,
+                       control->reg_5->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_6->address,
+                       (u8 *)control->reg_6->regs,
+                       control->reg_6->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg6 to 0x%x\n", __func__,
+               control->reg_6->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_7->address,
+               (u8 *)control->reg_7->regs,
+               control->reg_7->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg7 to 0x%x\n", __func__,
+               control->reg_7->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_8->address,
+               (u8 *)control->reg_8->regs,
+               control->reg_8->length);
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg8 to 0x%x\n", __func__,
+               control->reg_8->address);
+               return retval;
+       }
+
+       retval = rmi_read_block(rmi_dev, control->reg_9->address,
+               (u8 *)control->reg_9->regs,
+               sizeof(control->reg_9->regs));
+       if (retval < 0) {
+               dev_err(&rmi_dev->dev,
+                       "%s : Could not read control reg9 to 0x%x\n", __func__,
+               control->reg_9->address);
+               return retval;
+       }
+       return 0;
+}
+
+static int rmi_f30_init(struct rmi_function_container *fc)
+{
+       int rc;
+       struct rmi_fn_30_data *f30 = fc->data;
+
+       rc = rmi_f30_alloc_memory(fc);
+       if (rc < 0)
+               goto error_exit;
+
+       rc = rmi_f30_initialize(fc);
+       if (rc < 0)
+               goto error_exit;
+
+       rc = rmi_f30_register_device(fc);
+       if (rc < 0)
+               goto error_exit;
+
+       rc = rmi_f30_create_sysfs(fc);
+       if (rc < 0)
+               goto error_uregister_exit;
+       return 0;
+
+error_uregister_exit:
+       input_unregister_device(f30->input);
+
+error_exit:
+       rmi_f30_free_memory(fc);
+
+       return rc;
+
+}
+
+static inline int rmi_f30_alloc_memory(struct rmi_function_container *fc)
+{
+       struct rmi_fn_30_data *f30;
+       int retval;
+
+       f30 = kzalloc(sizeof(struct rmi_fn_30_data), GFP_KERNEL);
+       if (!f30) {
+               dev_err(&fc->dev, "Failed to allocate rmi_fn_30_data.\n");
+               return -ENOMEM;
+       }
+       fc->data = f30;
+
+       retval = rmi_read_block(fc->rmi_dev,
+                                               fc->fd.query_base_addr,
+                                               f30->query.regs,
+                                       ARRAY_SIZE(f30->query.regs));
+
+       if (retval < 0) {
+               dev_err(&fc->dev, "Failed to read query register.\n");
+               return retval;
+       }
+
+       f30->gpioled_count = f30->query.gpio_led_count;
+       f30->button_bitmask_size = sizeof(u8)*(f30->gpioled_count + 7) / 8;
+       f30->button_data_buffer =
+           kcalloc(f30->button_bitmask_size,
+                   sizeof(unsigned char), GFP_KERNEL);
+       if (!f30->button_data_buffer) {
+               dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+               return -ENOMEM;
+       }
+
+       f30->gpioled_map = kcalloc(f30->gpioled_count,
+                               sizeof(unsigned short), GFP_KERNEL);
+       if (!f30->gpioled_map) {
+               dev_err(&fc->dev, "Failed to allocate button map.\n");
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+static inline void rmi_f30_free_memory(struct rmi_function_container *fc)
+{
+       struct rmi_fn_30_data *f30 = fc->data;
+       u8 reg_num = 0;
+       sysfs_remove_group(&fc->dev.kobj, &attrs_query);
+       sysfs_remove_group(&fc->dev.kobj, &attrs_data);
+       for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++)
+               sysfs_remove_group(&fc->dev.kobj, &attrs_ctrl_regs[reg_num]);
+       if (f30) {
+               if (f30->control.reg_0)
+                       kfree(f30->control.reg_0->regs);
+               kfree(f30->control.reg_0);
+
+               if (f30->control.reg_1)
+                       kfree(f30->control.reg_1->regs);
+               kfree(f30->control.reg_1);
+
+               if (f30->control.reg_2)
+                       kfree(f30->control.reg_2->regs);
+               kfree(f30->control.reg_2);
+
+               if (f30->control.reg_3)
+                       kfree(f30->control.reg_3->regs);
+               kfree(f30->control.reg_3);
+
+               if (f30->control.reg_4)
+                       kfree(f30->control.reg_4->regs);
+               kfree(f30->control.reg_4);
+
+               if (f30->control.reg_5)
+                       kfree(f30->control.reg_5->regs);
+               kfree(f30->control.reg_5);
+
+               if (f30->control.reg_6)
+                       kfree(f30->control.reg_6->regs);
+               kfree(f30->control.reg_6);
+
+               if (f30->control.reg_7)
+                       kfree(f30->control.reg_7->regs);
+               kfree(f30->control.reg_7);
+
+               if (f30->control.reg_8)
+                       kfree(f30->control.reg_8->regs);
+               kfree(f30->control.reg_8);
+
+               if (f30->control.reg_9)
+                       kfree(f30->control.reg_9->regs);
+               kfree(f30->control.reg_9);
+
+               if (!f30->data.datareg_0)
+                       kfree(f30->data.datareg_0->regs);
+               kfree(f30->data.datareg_0);
+
+               kfree(f30->button_data_buffer);
+               kfree(f30->gpioled_map);
+               kfree(f30);
+               fc->data = NULL;
+       }
+}
+
+int rmi_f30_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       int data_base_addr = fc->fd.data_base_addr;
+       struct rmi_fn_30_data *f30 = fc->data;
+       int error;
+       int gpiled;
+       bool gpiled_status = false;
+       int status = 0;
+
+       /* Read the button data. */
+
+       error = rmi_read_block(rmi_dev, data_base_addr,
+                       f30->button_data_buffer,
+                       f30->button_bitmask_size);
+       if (error < 0) {
+               dev_err(&fc->dev,
+                       "%s: Failed to read button data registers.\n",
+                       __func__);
+               return error;
+       }
+
+       /* Read the gpi led data. */
+       f30->data.address = fc->fd.data_base_addr;
+       error = rmi_read_block(fc->rmi_dev, f30->data.address,
+               (u8 *)&f30->data, f30->gpioled_count);
+
+       if (error < 0) {
+               dev_err(&fc->dev, "%s: Failed to read f30 data registers.\n",
+                       __func__);
+               return error;
+       }
+       /* Generate events for buttons that change state. */
+       for (gpiled = 0; gpiled < f30->gpioled_count; gpiled++) {
+               status = f30->data.datareg_0->regs[gpiled].gpi_led_data;
+               dev_warn(&fc->dev,
+                       "rmi_f30 attention gpiled=%d data status=%d\n",
+                       gpiled,
+                       f30->data.datareg_0->regs[gpiled].gpi_led_data);
+               /* check if gpio */
+               if (!(f30->control.reg_0->regs[gpiled].led_sel)) {
+                       if (f30->control.reg_2->regs[gpiled].dir == 0) {
+                               gpiled_status = status != 0;
+
+               /* if the gpiled data state changed from the
+               * last time report it and store the new state */
+               /* Generate an event here. */
+                       dev_warn(&fc->dev,
+                       "rmi_f30 attention call input_report_key\n");
+                       input_report_key(f30->input,
+                               f30->data.datareg_0->regs[gpiled].gpi_led_data,
+                               gpiled_status);
+                       }
+               }
+       }
+       input_sync(f30->input); /* sync after groups of events */
+       return 0;
+}
+
+static int rmi_f30_register_device(struct rmi_function_container *fc)
+{
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct input_dev *input_dev;
+       struct rmi_fn_30_data *f30 = fc->data;
+       int i;
+       int rc;
+
+       input_dev = input_allocate_device();
+       if (!input_dev) {
+               dev_err(&fc->dev, "Failed to allocate input device.\n");
+               return -ENOMEM;
+       }
+
+       f30->input = input_dev;
+       snprintf(f30->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+               fc->fd.function_number);
+       input_dev->name = f30->input_name;
+       snprintf(f30->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+       input_dev->phys = f30->input_phys;
+       input_dev->dev.parent = &rmi_dev->dev;
+       input_set_drvdata(input_dev, f30);
+
+       /* Set up any input events. */
+       set_bit(EV_SYN, input_dev->evbit);
+       set_bit(EV_KEY, input_dev->evbit);
+       input_dev->keycode = f30->gpioled_map;
+       input_dev->keycodesize = 1;
+       input_dev->keycodemax = f30->gpioled_count;
+       /* set bits for each qpio led pin... */
+       for (i = 0; i < f30->gpioled_count; i++) {
+               set_bit(f30->gpioled_map[i], input_dev->keybit);
+               input_set_capability(input_dev, EV_KEY, f30->gpioled_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_f30_config(struct rmi_function_container *fc)
+{
+       struct rmi_fn_30_data *data = fc->data;
+       int gpio_led_cnt = data->query.gpio_led_count;
+       int bytecnt = gpio_led_cnt / 7 + 1;
+       int regs_size = 0;
+       int rc;
+       /* repeated register functions */
+
+       /* Write Control Register values back to device */
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_0->address,
+                               (u8 *)data->control.reg_0,
+                               bytecnt * sizeof(struct f30_gpio_ctrl_0n));
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 0 to 
0x%x\n",
+                               __func__, rc, data->control.reg_0->address);
+               return rc;
+       }
+
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_1->address,
+                       (u8 *) data->control.reg_1->regs,
+                       sizeof(union f30_gpio_ctrl_1));
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 1 to 
0x%x\n",
+                               __func__, rc, data->control.reg_1->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_2->length;
+
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_2->address,
+                       (u8 *) data->control.reg_2->regs,
+                       regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 2 to 
0x%x\n",
+                               __func__, rc, data->control.reg_2->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_3->length;
+
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_3->address,
+                       (u8 *) data->control.reg_3->regs,
+                       regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 3 to 
0x%x\n",
+                               __func__, rc, data->control.reg_3->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_4->length;
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_4->address,
+                       (u8 *) data->control.reg_4->regs,
+                       regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 4 to 
0x%x\n",
+                               __func__, rc, data->control.reg_4->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_5->length;
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_5->address,
+                       (u8 *) data->control.reg_5->regs,
+                       regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 5 to 
0x%x\n",
+                               __func__, rc, data->control.reg_5->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_6->length;
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_6->address,
+                       (u8 *) data->control.reg_6->regs,
+                       regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 6 to 
0x%x\n",
+                               __func__, rc, data->control.reg_6->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_7->length;
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_7->address,
+                       (u8 *) data->control.reg_7->regs,
+                       regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 7 to 
0x%x\n",
+                       __func__, rc, data->control.reg_7->address);
+               return rc;
+       }
+
+       regs_size = data->control.reg_8->length;
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_8->address,
+                       (u8 *) data->control.reg_8->regs, regs_size);
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 9 to 
0x%x\n",
+                       __func__, rc, data->control.reg_8->address);
+               return rc;
+       }
+
+       rc = rmi_write_block(fc->rmi_dev, data->control.reg_9->address,
+                       (u8 *) data->control.reg_9->regs,
+                       sizeof(union f30_gpio_ctrl_9));
+       if (rc < 0) {
+               dev_err(&fc->dev, "%s error %d: Could not write control 9 to 
0x%x\n",
+                               __func__, rc, data->control.reg_9->address);
+               return rc;
+       }
+
+       return 0;
+}
+
+static inline int rmi_f30_initialize(struct rmi_function_container *fc)
+{
+       struct rmi_device *rmi_dev = fc->rmi_dev;
+       struct rmi_device_platform_data *pdata;
+       struct rmi_fn_30_data *instance_data = fc->data;
+
+       int retval = 0;
+       u16 next_loc;
+       int  gpio_led_cnt = 0;
+       int regs_size = 0;
+       u8 reg_num = 0;
+       int reg_flg;
+       int hasgpio, hasled, hasmbtn, hashaptic;
+       struct f30_control *control = &instance_data->control;
+
+       /* Read F30 Query Data */
+       instance_data->query.address = fc->fd.query_base_addr;
+       retval = rmi_read_block(fc->rmi_dev, instance_data->query.address,
+               (u8 *)&instance_data->query, sizeof(instance_data->query));
+       if (retval < 0) {
+               dev_err(&fc->dev,
+               "Could not read query registers from 0x%04x\n",
+               instance_data->query.address);
+               return retval;
+       }
+
+       /* initialize gpioled_map data */
+       hasgpio = instance_data->query.has_gpio;
+       hasled = instance_data->query.has_led;
+       hasmbtn = instance_data->query.has_mappable_buttons;
+       hashaptic = instance_data->query.has_haptic ;
+       gpio_led_cnt = instance_data->query.gpio_led_count;
+
+       pdata = to_rmi_platform_data(rmi_dev);
+       if (pdata) {
+               if (!pdata->gpioled_map) {
+                       dev_warn(&fc->dev,
+                       "%s - gpioled_map is NULL", __func__);
+               } else if (pdata->gpioled_map->ngpioleds != gpio_led_cnt) {
+                       dev_warn(&fc->dev,
+                               "Platformdata gpioled map size (%d) != number "
+                               "of buttons on device (%d) - ignored.\n",
+                               pdata->gpioled_map->ngpioleds,
+                               gpio_led_cnt);
+               } else if (!pdata->gpioled_map->map) {
+                       dev_warn(&fc->dev,
+                                "Platformdata button map is missing!\n");
+               } else {
+                       int i;
+                       for (i = 0; i < pdata->gpioled_map->ngpioleds; i++)
+                               instance_data->gpioled_map[i] =
+                                       pdata->gpioled_map->map[i];
+               }
+       }
+
+       /* Initialize Control Data */
+
+       next_loc = fc->fd.control_base_addr;
+
+       /* calculate reg size */
+
+       instance_data->gpioled_bitmask_size = sizeof(u8)*(gpio_led_cnt + 7) / 8;
+       instance_data->gpioled_byte_size = sizeof(u8)*gpio_led_cnt;
+
+       /* reg_0 */
+       control->reg_0 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_0), GFP_KERNEL);
+       if (!control->reg_0) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+
+       if (hasgpio && hasled)
+               reg_flg = 1;
+
+       f30_attrs_regs_exist[reg_num] = true;
+       regs_size = max(sizeof(struct f30_gpio_ctrl_0n) * reg_flg *
+                                       instance_data->gpioled_bitmask_size, 1);
+       control->reg_0->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+
+       if (!control->reg_0->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_0->address = next_loc;
+       control->reg_0->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_1 */
+       control->reg_1 =
+                       kzalloc(sizeof(union f30_gpio_ctrl_1), GFP_KERNEL);
+       if (!control->reg_1) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       f30_attrs_regs_exist[reg_num] = true;
+       reg_num++;
+       instance_data->control.reg_1->address = next_loc;
+       next_loc += regs_size;
+
+       /* reg_2 */
+       instance_data->control.reg_2 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_2), GFP_KERNEL);
+       if (!control->reg_2) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+
+       reg_flg = hasgpio;
+       f30_attrs_regs_exist[reg_num] = true;
+       regs_size = max(sizeof(struct f30_gpio_ctrl_2n)*reg_flg*
+                                       instance_data->gpioled_bitmask_size, 1);
+       control->reg_2->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+
+       if (!control->reg_2->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_2->address = next_loc;
+       control->reg_2->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_3 */
+       instance_data->control.reg_3 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_3), GFP_KERNEL);
+       if (!control->reg_3) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+
+       reg_flg = hasgpio;
+       f30_attrs_regs_exist[reg_num] = true;
+       regs_size = max(sizeof(struct f30_gpio_ctrl_3n) * reg_flg *
+                                       instance_data->gpioled_bitmask_size, 1);
+       control->reg_3->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+
+       if (!control->reg_3->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_3->address = next_loc;
+       control->reg_3->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_4 */
+       control->reg_4 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_4), GFP_KERNEL);
+       if (!control->reg_4) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+
+       reg_flg = hasled;
+       f30_attrs_regs_exist[reg_num] = true;
+       regs_size = max(sizeof(struct f30_gpio_ctrl_4n)*reg_flg*
+                                       instance_data->gpioled_bitmask_size,
+                                       sizeof(struct f30_gpio_ctrl_4n));
+       control->reg_4->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+       if (!control->reg_4->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_4->address = next_loc;
+       control->reg_4->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_5 */
+       control->reg_5 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_5), GFP_KERNEL);
+       if (!control->reg_5) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+
+       reg_flg = hasled;
+       f30_attrs_regs_exist[reg_num] = true;
+       regs_size = max(6 * reg_flg, 2);
+       control->reg_5->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+       if (!control->reg_5->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_5->address = next_loc;
+       control->reg_5->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_6 */
+       control->reg_6 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_6), GFP_KERNEL);
+       if (!control->reg_6) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       reg_flg = hasled || (!hasled
+               && instance_data->query.has_gpio_driver_control);
+
+       regs_size = max(sizeof(union f30_gpio_ctrl_6n)*reg_flg*gpio_led_cnt,
+                                       sizeof(union f30_gpio_ctrl_6n));
+       if (!hasled
+               && instance_data->query.has_gpio_driver_control)
+               f30_attrs_regs_exist[reg_num] = true;
+
+       reg_num++;
+       if (hasled)
+               f30_attrs_regs_exist[reg_num] = true;
+
+       reg_num++;
+
+       control->reg_6->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+       if (!control->reg_6->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_6->address = next_loc;
+       control->reg_6->length = regs_size;
+       next_loc += regs_size;
+
+       /* reg_7 */
+       reg_flg = hasmbtn;
+       control->reg_7 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_7), GFP_KERNEL);
+       if (!control->reg_7) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       if (hasmbtn)
+               regs_size = sizeof(struct f30_gpio_ctrl_7n)*gpio_led_cnt;
+       else
+               regs_size = sizeof(struct f30_gpio_ctrl_7n);
+
+       regs_size = max(sizeof(struct f30_gpio_ctrl_7n)*reg_flg*
+                                       gpio_led_cnt,
+                                       sizeof(struct f30_gpio_ctrl_7n));
+       f30_attrs_regs_exist[reg_num] = true;
+       control->reg_7->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+       if (!control->reg_7->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_7->address = next_loc;
+       control->reg_7->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_8 */
+       control->reg_8 =
+                       kzalloc(sizeof(struct f30_gpio_ctrl_8), GFP_KERNEL);
+       if (!control->reg_8) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+
+       regs_size = max(sizeof(struct f30_gpio_ctrl_8n)*hashaptic*
+                                       gpio_led_cnt,
+                                       sizeof(struct f30_gpio_ctrl_8n));
+       f30_attrs_regs_exist[reg_num] = true;
+       control->reg_8->regs =
+                       kzalloc(regs_size, GFP_KERNEL);
+       if (!control->reg_8->regs) {
+               dev_err(&fc->dev, "Failed to allocate control registers.");
+               return -ENOMEM;
+       }
+       control->reg_8->address = next_loc;
+       control->reg_8->length = regs_size;
+       next_loc += regs_size;
+       reg_num++;
+
+       /* reg_9 */
+       control->reg_9 =
+                       kzalloc(sizeof(union f30_gpio_ctrl_9), GFP_KERNEL);
+       if (!control->reg_9) {
+               dev_err(&fc->dev, "Failed to allocate control register.");
+               return -ENOMEM;
+       }
+       if (instance_data->query.has_haptic)
+               f30_attrs_regs_exist[reg_num] = true;
+       control->reg_9->address = next_loc;
+       next_loc += sizeof(control->reg_9->regs);
+
+       /* data reg_0 */
+       instance_data->data.datareg_0 =
+                       kzalloc(sizeof(struct f30_data_0), GFP_KERNEL);
+       if (!instance_data->data.datareg_0) {
+               dev_err(&fc->dev, "Failed to allocate control register.");
+               return -ENOMEM;
+       }
+
+       regs_size = sizeof(struct f30_data_0n)*
+                               instance_data->gpioled_byte_size;
+       instance_data->data.datareg_0->address = fc->fd.data_base_addr;
+       next_loc += sizeof(instance_data->data.datareg_0->regs);
+
+       retval = rmi_f30_read_control_parameters(rmi_dev, instance_data);
+       if (retval < 0) {
+               dev_err(&fc->dev,
+                       "Failed to initialize F19 control params.\n");
+               return retval;
+       }
+
+       mutex_init(&instance_data->control_mutex);
+       mutex_init(&instance_data->data_mutex);
+       return 0;
+}
+
+static int rmi_f30_create_sysfs(struct rmi_function_container *fc)
+{
+       u8 reg_num;
+
+       dev_dbg(&fc->dev, "Creating sysfs files.");
+
+       /* Set up sysfs device attributes. */
+       if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) {
+               dev_err(&fc->dev, "Failed to create query sysfs files.");
+               return -ENODEV;
+       }
+       if (sysfs_create_group(&fc->dev.kobj, &attrs_data) < 0) {
+               dev_err(&fc->dev, "Failed to create data sysfs files.");
+               return -ENODEV;
+       }
+
+       for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs);
+               reg_num++) {
+               if (f30_attrs_regs_exist[reg_num]) {
+                       if (sysfs_create_group(&fc->dev.kobj,
+                                       &attrs_ctrl_regs[reg_num]) < 0) {
+                               dev_err(&fc->dev,
+                                       "Failed to create "
+                                       "sysfs file group for reg"
+                                       "group %d.",
+                                       reg_num);
+                               return -ENODEV;
+                       }
+
+               }
+       }
+
+       return 0;
+}
+
+static void rmi_f30_remove(struct rmi_function_container *fc)
+{
+       dev_info(&fc->dev, "Removing F30.");
+       rmi_f30_free_memory(fc);
+}
+
+static struct rmi_function_handler function_handler = {
+       .func = 0x30,
+       .init = rmi_f30_init,
+       .config = rmi_f30_config,
+       .attention = rmi_f30_attention,
+       .remove = rmi_f30_remove
+};
+
+static int __init rmi_f30_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_f30_module_exit(void)
+{
+       rmi_unregister_function_driver(&function_handler);
+}
+
+/* sysfs functions */
+/* Query */
+simple_show_union_struct_unsigned(query, extended_patterns)
+simple_show_union_struct_unsigned(query, has_mappable_buttons)
+simple_show_union_struct_unsigned(query, has_led)
+simple_show_union_struct_unsigned(query, has_gpio)
+simple_show_union_struct_unsigned(query, has_haptic)
+simple_show_union_struct_unsigned(query, has_gpio_driver_control)
+simple_show_union_struct_unsigned(query, gpio_led_count)
+
+/* Control */
+show_store_union_struct_unsigned(control, reg_1, gpio_debounce)
+show_store_union_struct_unsigned(control, reg_1, halt)
+show_store_union_struct_unsigned(control, reg_1, halted)
+show_store_union_struct_unsigned(control, reg_9, haptic_duration)
+
+/* repeated register functions */
+show_store_repeated_union_struct_unsigned(control, reg_0, led_sel)
+show_store_repeated_union_struct_unsigned(control, reg_2, dir)
+show_store_repeated_union_struct_unsigned(control, reg_3, gpiodata)
+show_store_repeated_union_struct_unsigned(control, reg_4, led_act)
+show_store_repeated_union_struct_unsigned(control, reg_5, ramp_period_a)
+show_store_repeated_union_struct_unsigned(control, reg_5, ramp_period_b)
+show_store_repeated_union_struct_unsigned(control, reg_6, SPCTRL)
+show_store_repeated_union_struct_unsigned(control, reg_6, STRPD)
+show_store_repeated_union_struct_unsigned(control, reg_6, STRPU)
+show_store_repeated_union_struct_unsigned(control, reg_6, brightness)
+show_store_repeated_union_struct_unsigned(control, reg_6, pattern)
+show_store_repeated_union_struct_unsigned(control, reg_7, capacity_btn_nbr)
+show_store_repeated_union_struct_unsigned(control, reg_7, valid)
+show_store_repeated_union_struct_unsigned(control, reg_7, invert)
+show_store_repeated_union_struct_unsigned(control, reg_7, open_drain)
+show_store_repeated_union_struct_unsigned(control, reg_8, gpio_ctrl8_0)
+show_store_repeated_union_struct_unsigned(control, reg_8, gpio_ctrl8_1)
+
+/* Data */
+show_store_repeated_union_struct_unsigned(data, datareg_0, gpi_led_data)
+
+module_init(rmi_f30_module_init);
+module_exit(rmi_f30_module_exit);
+
+MODULE_AUTHOR("Allie Xiong <axi...@synaptics.com>");
+MODULE_DESCRIPTION("RMI f30 module");
+MODULE_LICENSE("GPL");
--
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