From patchwork Fri Aug 17 22:17:54 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christopher Heiny X-Patchwork-Id: 1339871 Return-Path: X-Original-To: patchwork-linux-input@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 8749340214 for ; Fri, 17 Aug 2012 23:26:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759066Ab2HQXWI (ORCPT ); Fri, 17 Aug 2012 19:22:08 -0400 Received: from [12.239.217.82] ([12.239.217.82]:28612 "EHLO venom.synaptics.com" rhost-flags-FAIL-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1758919Ab2HQXVD (ORCPT ); Fri, 17 Aug 2012 19:21:03 -0400 X-Greylist: delayed 3724 seconds by postgrey-1.27 at vger.kernel.org; Fri, 17 Aug 2012 19:20:45 EDT Received: from venom.synaptics.com (venom.synaptics.com [127.0.0.1]) by venom.synaptics.com (8.14.4/8.14.4) with ESMTP id q7HMI0WH016258; Fri, 17 Aug 2012 15:18:07 -0700 From: Christopher Heiny To: Dmitry Torokhov Cc: Jean Delvare , Linux Kernel , Linux Input , Christopher Heiny , Allie Xiong , William Manson , Peichen Chang , Joerie de Gram , Wolfram Sang , Mathieu Poirier , Linus Walleij , Naveen Kumar Gaddipati Subject: [RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control Date: Fri, 17 Aug 2012 15:17:54 -0700 Message-Id: <1345241877-16200-15-git-send-email-cheiny@synaptics.com> X-Mailer: git-send-email 1.7.4.4 In-Reply-To: <1345241877-16200-1-git-send-email-cheiny@synaptics.com> References: <1345241877-16200-1-git-send-email-cheiny@synaptics.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Signed-off-by: Christopher Heiny Cc: Dmitry Torokhov Cc: Linus Walleij Cc: Naveen Kumar Gaddipati Cc: Joeri de Gram Acked-by: Jean Delvare --- drivers/input/rmi4/rmi_f30.c | 1247 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1247 insertions(+), 0 deletions(-) -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --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 +#include +#include +#include +#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 "); +MODULE_DESCRIPTION("RMI f30 module"); +MODULE_LICENSE("GPL");