new file mode 100644
@@ -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 <axiong@Synaptics.com>");
+MODULE_DESCRIPTION("RMI f30 module");
+MODULE_LICENSE("GPL");