From patchwork Fri Aug 17 22:17:52 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christopher Heiny X-Patchwork-Id: 1339861 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 6C0A440212 for ; Fri, 17 Aug 2012 23:26:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759052Ab2HQXWD (ORCPT ); Fri, 17 Aug 2012 19:22:03 -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 S1758918Ab2HQXVC (ORCPT ); Fri, 17 Aug 2012 19:21:02 -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 q7HMI0WF016258; Fri, 17 Aug 2012 15:18:06 -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 12/17] input: RMI4 F1A simple capacitive buttons Date: Fri, 17 Aug 2012 15:17:52 -0700 Message-Id: <1345241877-16200-13-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_f1a.c | 1025 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 1025 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_f1a.c b/drivers/input/rmi4/rmi_f1a.c new file mode 100644 index 0000000..a7cccfa --- /dev/null +++ b/drivers/input/rmi4/rmi_f1a.c @@ -0,0 +1,1025 @@ +/* + * 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 f1a_data +#define FNUM 1a + +#include +#include +#include +#include +#include "rmi_driver.h" + +#define QUERY_BASE_INDEX 1 +#define MAX_NAME_LEN 256 +#define MAX_BUFFER_LEN 80 + +#define FILTER_MODE_MIN 0 +#define FILTER_MODE_MAX 3 +#define MULTI_BUTTON_REPORT_MIN 0 +#define MULTI_BUTTON_REPORT_MAX 3 +#define TX_RX_BUTTON_MIN 0 +#define TX_RX_BUTTON_MAX 255 +#define THREADHOLD_BUTTON_MIN 0 +#define THREADHOLD_BUTTON_MAX 255 +#define RELEASE_THREADHOLD_BUTTON_MIN 0 +#define RELEASE_THREADHOLD_BUTTON_MAX 255 +#define STRONGEST_BUTTON_HYSTERESIS_MIN 0 +#define STRONGEST_BUTTON_HYSTERESIS_MAX 255 +#define FILTER_STRENGTH_MIN 0 +#define FILTER_STRENGTH_MAX 255 + +union f1a_0d_query { + struct { + u8 max_button_count:3; + u8 reserved:5; + + u8 has_general_control:1; + u8 has_interrupt_enable:1; + u8 has_multibutton_select:1; + u8 has_tx_rx_map:1; + u8 has_perbutton_threshold:1; + u8 has_release_threshold:1; + u8 has_strongestbtn_hysteresis:1; + u8 has_filter_strength:1; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + } __attribute__((__packed__)); +}; + +union f1a_0d_control_0 { + struct { + u8 multibutton_report:2; + u8 filter_mode:2; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +struct f1a_0d_control_1n { + u8 interrupt_enabled_button; +}; + +struct f1a_0d_control_1 { + struct f1a_0d_control_1n *regs; + u16 address; + u8 length; +} __attribute__((__packed__)); + +struct f1a_0d_control_2n { + u8 multi_button; +}; + +struct f1a_0d_control_2 { + struct f1a_0d_control_2n *regs; + u16 address; + u8 length; +} __attribute__((__packed__)); + +struct f1a_0d_control_3_4n { + u8 transmitterbtn; + u8 receiverbtn; +} __attribute__((__packed__)); + +struct f1a_0d_control_3_4 { + struct f1a_0d_control_3_4n *regs; + u16 address; + u8 length; +} __attribute__((__packed__)); + +struct f1a_0d_control_5n { + u8 threshold_button; +}; + +struct f1a_0d_control_5 { + struct f1a_0d_control_5n *regs; + u16 address; + u8 length; +} __attribute__((__packed__)); + +union f1a_0d_control_6 { + struct { + u8 button_release_threshold; + }; + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f1a_0d_control_7 { + struct { + u8 strongest_button_hysteresis; + }; + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +union f1a_0d_control_8 { + struct { + u8 filter_strength; + }; + struct { + u8 regs[1]; + u16 address; + } __attribute__((__packed__)); +}; + +struct f1a_0d_control { + union f1a_0d_control_0 *reg_0; + struct f1a_0d_control_1 *reg_1; + struct f1a_0d_control_2 *reg_2; + struct f1a_0d_control_3_4 *reg_3_4; + struct f1a_0d_control_5 *reg_5; + union f1a_0d_control_6 *reg_6; + union f1a_0d_control_7 *reg_7; + union f1a_0d_control_8 *reg_8; +}; + +/* data specific to fn $1a that needs to be kept around */ +struct f1a_data { + struct f1a_0d_control control; + union f1a_0d_query query; + u8 sensor_button_count; + int button_bitmask_size; + u8 *button_data_buffer; + u8 button_map_size; + u8 *button_map; + char input_name[MAX_NAME_LEN]; + char input_phys[MAX_NAME_LEN]; + struct input_dev *input; + struct mutex control_mutex; + struct mutex data_mutex; +}; + + +static int rmi_f1a_alloc_memory(struct rmi_function_container *fc); + +static void rmi_f1a_free_memory(struct rmi_function_container *fc); + +static int rmi_f1a_initialize(struct rmi_function_container *fc); + +static int rmi_f1a_register_device(struct rmi_function_container *fc); + +static int rmi_f1a_create_sysfs(struct rmi_function_container *fc); + +static int rmi_f1a_config(struct rmi_function_container *fc); + +static int rmi_f1a_reset(struct rmi_function_container *fc); + +/* Query sysfs files */ +show_union_struct_prototype(max_button_count) +show_union_struct_prototype(has_general_control) +show_union_struct_prototype(has_interrupt_enable) +show_union_struct_prototype(has_multibutton_select) +show_union_struct_prototype(has_tx_rx_map) +show_union_struct_prototype(has_perbutton_threshold) +show_union_struct_prototype(has_release_threshold) +show_union_struct_prototype(has_strongestbtn_hysteresis) +show_union_struct_prototype(has_filter_strength) + +static struct attribute *attrs[] = { + attrify(max_button_count), + attrify(has_general_control), + attrify(has_interrupt_enable), + attrify(has_multibutton_select), + attrify(has_tx_rx_map), + attrify(has_perbutton_threshold), + attrify(has_release_threshold), + attrify(has_strongestbtn_hysteresis), + attrify(has_filter_strength), + NULL +}; + +static struct attribute_group attrs_query = GROUP(attrs); + +/* Control sysfs files */ +show_store_union_struct_prototype(multibutton_report) +show_store_union_struct_prototype(filter_mode) +show_store_union_struct_prototype(interrupt_enabled_button) +show_store_union_struct_prototype(multi_button) +show_union_struct_prototype(tx_rx_map) +show_store_union_struct_prototype(threshold_button) +show_store_union_struct_prototype(button_release_threshold) +show_store_union_struct_prototype(strongest_button_hysteresis) +show_store_union_struct_prototype(filter_strength) + +static int rmi_f1a_read_control_parameters(struct rmi_device *rmi_dev, + struct f1a_data *f1a) +{ + int retval = 0; + union f1a_0d_query *query = &f1a->query; + struct f1a_0d_control *control = &f1a->control; + + if (query->has_general_control) { + retval = rmi_read_block(rmi_dev, control->reg_0->address, + (u8 *)control->reg_0->regs, + sizeof(control->reg_0->regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg0 to %#06x.\n", + control->reg_0->address); + return retval; + } + } + + if (query->has_interrupt_enable) { + retval = rmi_read_block(rmi_dev, control->reg_1->address, + (u8 *)control->reg_1->regs, f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg1 to %#06x.\n", + control->reg_1->address); + return retval; + } + } + + if (query->has_multibutton_select) { + retval = rmi_read_block(rmi_dev, control->reg_2->address, + (u8 *)control->reg_2->regs, f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg2 to %#06x.\n", + control->reg_2->address); + return retval; + } + } + + if (query->has_tx_rx_map) { + retval = rmi_read_block(rmi_dev, control->reg_3_4->address, + (u8 *)control->reg_3_4->regs, + sizeof(struct f1a_0d_control_3_4n) * + f1a->sensor_button_count); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg 3_4 to %#06x.\n", + control->reg_3_4->address); + return retval; + } + } + + if (query->has_perbutton_threshold) { + retval = rmi_read_block(rmi_dev, control->reg_5->address, + (u8 *)control->reg_5->regs, f1a->sensor_button_count); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg 5 to %#06x.\n", + control->reg_5->address); + return retval; + } + } + + if (query->has_release_threshold) { + retval = rmi_read_block(rmi_dev, control->reg_6->address, + (u8 *)control->reg_6->regs, + sizeof(control->reg_6->regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg 6 to %#06x.\n", + control->reg_6->address); + return retval; + } + } + + if (query->has_strongestbtn_hysteresis) { + retval = rmi_read_block(rmi_dev, control->reg_7->address, + (u8 *)control->reg_7->regs, + sizeof(control->reg_7->regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg 7 to %#06x.\n", + control->reg_7->address); + return retval; + } + } + + if (query->has_filter_strength) { + retval = rmi_read_block(rmi_dev, control->reg_8->address, + (u8 *)control->reg_8->regs, + sizeof(control->reg_8->regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Could not read control reg 8 to %#06x.\n", + control->reg_8->address); + return retval; + } + } + return 0; +} + + +static int rmi_f1a_init(struct rmi_function_container *fc) +{ + int rc; + + rc = rmi_f1a_alloc_memory(fc); + if (rc < 0) + goto err_free_data; + + rc = rmi_f1a_initialize(fc); + if (rc < 0) + goto err_free_data; + + rc = rmi_f1a_register_device(fc); + if (rc < 0) + goto err_free_data; + + rc = rmi_f1a_create_sysfs(fc); + if (rc < 0) + goto err_free_data; + + return 0; + +err_free_data: + rmi_f1a_free_memory(fc); + + return rc; +} + + +static int rmi_f1a_alloc_memory(struct rmi_function_container *fc) +{ + struct f1a_data *f1a; + int rc; + int regSize; + u16 ctrl_base_addr; + + f1a = kzalloc(sizeof(struct f1a_data), GFP_KERNEL); + if (!f1a) { + dev_err(&fc->dev, "Failed to allocate function data.\n"); + return -ENOMEM; + } + fc->data = f1a; + + rc = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr, + f1a->query.regs, sizeof(f1a->query.regs)); + if (rc < 0) { + dev_err(&fc->dev, "Failed to read query register.\n"); + return rc; + } + + f1a->sensor_button_count = f1a->query.max_button_count+1; + + f1a->button_bitmask_size = + sizeof(u8)*(f1a->sensor_button_count + 7) / 8; + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(u8), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(&fc->dev, "Failed to allocate button data buffer.\n"); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->sensor_button_count, + sizeof(unsigned char), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(&fc->dev, "Failed to allocate button map.\n"); + return -ENOMEM; + } + + /* allocate memory for control reg */ + /* reg 0 */ + ctrl_base_addr = fc->fd.control_base_addr; + if (f1a->query.has_general_control) { + f1a->control.reg_0 = + kzalloc(sizeof(f1a->control.reg_0->regs), GFP_KERNEL); + if (!f1a->control.reg_0) { + dev_err(&fc->dev, "Failed to allocate reg_0 control registers."); + return -ENOMEM; + } + f1a->control.reg_0->address = ctrl_base_addr; + ctrl_base_addr += sizeof(f1a->control.reg_0->regs); + } + /* reg 1 */ + if (f1a->query.has_interrupt_enable) { + f1a->control.reg_1 = + kzalloc(sizeof(struct f1a_0d_control_1), GFP_KERNEL); + if (!f1a->control.reg_1) { + dev_err(&fc->dev, "Failed to allocate reg_1 control registers."); + return -ENOMEM; + } + f1a->control.reg_1->regs = + kzalloc(f1a->button_bitmask_size, GFP_KERNEL); + if (!f1a->control.reg_1->regs) { + dev_err(&fc->dev, "Failed to allocate reg_1->regs control registers."); + return -ENOMEM; + } + f1a->control.reg_1->address = ctrl_base_addr; + f1a->control.reg_1->length = f1a->button_bitmask_size; + ctrl_base_addr += f1a->button_bitmask_size; + } + + /* reg 2 */ + if (f1a->query.has_multibutton_select) { + f1a->control.reg_2 = + kzalloc(sizeof(struct f1a_0d_control_2), GFP_KERNEL); + if (!f1a->control.reg_2) { + dev_err(&fc->dev, "Failed to allocate reg_2 control registers."); + return -ENOMEM; + } + f1a->control.reg_2->regs = + kzalloc(f1a->button_bitmask_size, GFP_KERNEL); + if (!f1a->control.reg_2->regs) { + dev_err(&fc->dev, "Failed to allocate reg_2->regs control registers."); + return -ENOMEM; + } + f1a->control.reg_2->address = ctrl_base_addr; + f1a->control.reg_2->length = f1a->button_bitmask_size; + ctrl_base_addr += f1a->button_bitmask_size; + } + + /* reg 3_4*/ + if (f1a->query.has_tx_rx_map) { + f1a->control.reg_3_4 = + kzalloc(sizeof(struct f1a_0d_control_3_4), GFP_KERNEL); + if (!f1a->control.reg_3_4) { + dev_err(&fc->dev, "Failed to allocate reg_3_4 control registers."); + return -ENOMEM; + } + regSize = sizeof(struct f1a_0d_control_3_4n); + f1a->control.reg_3_4->regs = + kzalloc(regSize * f1a->sensor_button_count, GFP_KERNEL); + if (!f1a->control.reg_3_4->regs) { + dev_err(&fc->dev, "Failed to allocate reg_3_4->regs control registers."); + return -ENOMEM; + } + f1a->control.reg_3_4->address = ctrl_base_addr; + f1a->control.reg_3_4->length = + regSize * f1a->sensor_button_count; + ctrl_base_addr += regSize * f1a->sensor_button_count; + } + + /* reg 5 */ + if (f1a->query.has_perbutton_threshold) { + f1a->control.reg_5 = + kzalloc(sizeof(struct f1a_0d_control_5), GFP_KERNEL); + if (!f1a->control.reg_5) { + dev_err(&fc->dev, "Failed to allocate reg_5 control registers."); + return -ENOMEM; + } + f1a->control.reg_5->regs = + kzalloc(f1a->sensor_button_count, GFP_KERNEL); + if (!f1a->control.reg_5->regs) { + dev_err(&fc->dev, "Failed to allocate reg_5->regs control registers."); + return -ENOMEM; + } + f1a->control.reg_5->address = ctrl_base_addr; + f1a->control.reg_5->length = f1a->sensor_button_count; + ctrl_base_addr += f1a->sensor_button_count; + } + + /* reg 6 */ + if (f1a->query.has_release_threshold) { + f1a->control.reg_6 = + kzalloc(sizeof(f1a->control.reg_6->regs), GFP_KERNEL); + if (!f1a->control.reg_6) { + dev_err(&fc->dev, "Failed to allocate reg_6 control registers."); + return -ENOMEM; + } + f1a->control.reg_6->address = ctrl_base_addr; + ctrl_base_addr += sizeof(f1a->control.reg_6->regs); + } + /* reg 7 */ + if (f1a->query.has_strongestbtn_hysteresis) { + f1a->control.reg_7 = + kzalloc(sizeof(f1a->control.reg_7->regs), GFP_KERNEL); + if (!f1a->control.reg_7) { + dev_err(&fc->dev, "Failed to allocate reg_7 control registers."); + return -ENOMEM; + } + f1a->control.reg_7->address = ctrl_base_addr; + ctrl_base_addr += sizeof(f1a->control.reg_7->regs); + } + /* reg 8 */ + if (f1a->query.has_filter_strength) { + f1a->control.reg_8 = + kzalloc(sizeof(f1a->control.reg_8->regs), GFP_KERNEL); + if (!f1a->control.reg_8) { + dev_err(&fc->dev, "Failed to allocate reg_8 control registers."); + return -ENOMEM; + } + f1a->control.reg_8->address = ctrl_base_addr; + ctrl_base_addr += sizeof(f1a->control.reg_8->regs); + } + return 0; +} + + + +static void rmi_f1a_free_memory(struct rmi_function_container *fc) +{ + union f1a_0d_query *query; + struct f1a_data *f1a = fc->data; + + query = &f1a->query; + sysfs_remove_group(&fc->dev.kobj, &attrs_query); + + if (query->has_general_control) { + sysfs_remove_file(&fc->dev.kobj, attrify(multibutton_report)); + sysfs_remove_file(&fc->dev.kobj, attrify(filter_mode)); + } + + if (query->has_interrupt_enable) + sysfs_remove_file(&fc->dev.kobj, + attrify(interrupt_enabled_button)); + + if (query->has_multibutton_select) + sysfs_remove_file(&fc->dev.kobj, attrify(multi_button)); + + if (query->has_tx_rx_map) + sysfs_remove_file(&fc->dev.kobj, attrify(tx_rx_map)); + + if (query->has_perbutton_threshold) + sysfs_remove_file(&fc->dev.kobj, attrify(threshold_button)); + + if (query->has_release_threshold) + sysfs_remove_file(&fc->dev.kobj, + attrify(button_release_threshold)); + + if (query->has_strongestbtn_hysteresis) + sysfs_remove_file(&fc->dev.kobj, + attrify(strongest_button_hysteresis)); + + if (query->has_filter_strength) + sysfs_remove_file(&fc->dev.kobj, attrify(filter_strength)); + + if (f1a) { + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a->control.reg_0); + if (f1a->control.reg_1) { + kfree(f1a->control.reg_1->regs); + kfree(f1a->control.reg_1); + } + if (f1a->control.reg_2) { + kfree(f1a->control.reg_2->regs); + kfree(f1a->control.reg_2); + } + if (f1a->control.reg_3_4) { + kfree(f1a->control.reg_3_4->regs); + kfree(f1a->control.reg_3_4); + } + if (f1a->control.reg_5) { + kfree(f1a->control.reg_5->regs); + kfree(f1a->control.reg_5); + } + kfree(f1a->control.reg_5); + kfree(f1a->control.reg_6); + kfree(f1a->control.reg_7); + kfree(f1a->control.reg_8); + kfree(f1a); + fc->data = NULL; + } +} + + +static int rmi_f1a_initialize(struct rmi_function_container *fc) +{ + int i; + int retval; + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_device_platform_data *pdata; + struct f1a_data *f1a = fc->data; + + dev_dbg(&fc->dev, "Intializing F1A values."); + + /* initial all default values for f1a data here */ + pdata = to_rmi_platform_data(rmi_dev); + if (pdata) { + if (!pdata->f1a_button_map) + dev_warn(&fc->dev, "%s - button_map is NULL", __func__); + else if (!pdata->f1a_button_map->map) + dev_warn(&fc->dev, + "Platformdata button map is missing!\n"); + else { + if (pdata->f1a_button_map->nbuttons != + f1a->sensor_button_count) + dev_warn(&fc->dev, + "Platform data buttonmap size(%d) != number buttons on device(%d)\n", + pdata->f1a_button_map->nbuttons, + f1a->sensor_button_count); + f1a->button_map_size = min(f1a->sensor_button_count, + pdata->f1a_button_map->nbuttons); + for (i = 0; i < f1a->button_map_size; i++) + f1a->button_map[i] = + pdata->f1a_button_map->map[i]; + } + } + + retval = rmi_f1a_read_control_parameters(rmi_dev, f1a); + if (retval < 0) { + dev_err(&fc->dev, + "Failed to initialize F1a control params.\n"); + return retval; + } + + mutex_init(&f1a->control_mutex); + mutex_init(&f1a->data_mutex); + return 0; +} + + + +static int rmi_f1a_register_device(struct rmi_function_container *fc) +{ + int i; + int rc; + struct input_dev *input_dev; + struct rmi_device *rmi_dev = fc->rmi_dev; + struct f1a_data *f1a = fc->data; + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&fc->dev, "Failed to allocate input device.\n"); + return -ENOMEM; + } + + f1a->input = input_dev; + snprintf(f1a->input_name, MAX_NAME_LEN, "%sfn%02x", + dev_name(&rmi_dev->dev), fc->fd.function_number); + input_dev->name = f1a->input_name; + snprintf(f1a->input_phys, MAX_NAME_LEN, "%s/input0", input_dev->name); + input_dev->phys = f1a->input_phys; + input_dev->dev.parent = &rmi_dev->dev; + input_set_drvdata(input_dev, f1a); + + /* Set up any input events. */ + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_KEY, input_dev->evbit); + + /* manage button map using input subsystem */ + input_dev->keycode = f1a->button_map; + input_dev->keycodesize = sizeof(f1a->button_map); + input_dev->keycodemax = f1a->button_map_size; + + /* set bits for each button. */ + for (i = 0; i < f1a->button_map_size; i++) { + set_bit(f1a->button_map[i], input_dev->keybit); + input_set_capability(input_dev, EV_KEY, f1a->button_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_f1a_create_sysfs(struct rmi_function_container *fc) +{ + struct f1a_data *f19 = fc->data; + union f1a_0d_query *query = &f19->query; + + dev_dbg(&fc->dev, "Creating sysfs files.\n"); + /* Set up sysfs device attributes. */ + if (sysfs_create_group(&fc->dev.kobj, &attrs_query) < 0) { + dev_err(&fc->dev, "Failed to create query sysfs files."); + return -ENODEV; + } + + if (query->has_general_control) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(multibutton_report)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + if (sysfs_create_file(&fc->dev.kobj, + attrify(filter_mode)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_interrupt_enable) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(interrupt_enabled_button)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_multibutton_select) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(multi_button)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_tx_rx_map) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(tx_rx_map)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_perbutton_threshold) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(threshold_button)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_release_threshold) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(button_release_threshold)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_strongestbtn_hysteresis) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(strongest_button_hysteresis)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + + if (query->has_filter_strength) { + if (sysfs_create_file(&fc->dev.kobj, + attrify(filter_strength)) < 0) { + dev_err(&fc->dev, "Failed to create control sysfs files."); + return -ENODEV; + } + } + return 0; +} + + + +static int rmi_f1a_config(struct rmi_function_container *fc) +{ + int retval; + struct f1a_data *f1a = fc->data; + union f1a_0d_query *query = &f1a->query; + struct f1a_0d_control *control = &f1a->control; + + if (query->has_general_control) { + retval = rmi_write_block(fc->rmi_dev, control->reg_0->address, + (u8 *)control->reg_0->regs, + sizeof(control->reg_0->regs)); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg0 to 0x%x\n", + __func__, control->reg_0->address); + return retval; + } + } + if (query->has_interrupt_enable) { + retval = rmi_write_block(fc->rmi_dev, control->reg_1->address, + (u8 *)control->reg_1->regs, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg 1 to 0x%x\n", + __func__, control->reg_1->address); + return retval; + } + } + if (query->has_multibutton_select) { + retval = rmi_write_block(fc->rmi_dev, control->reg_2->address, + (u8 *)control->reg_2->regs, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg 2 to 0x%x\n", + __func__, control->reg_2->address); + return -EINVAL; + } + } + if (query->has_tx_rx_map) { + retval = rmi_write_block(fc->rmi_dev, control->reg_3_4->address, + (u8 *)control->reg_3_4->regs, + f1a->sensor_button_count); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg_3_4 to 0x%x\n", + __func__, control->reg_3_4->address); + return -EINVAL; + } + } + if (query->has_perbutton_threshold) { + retval = rmi_write_block(fc->rmi_dev, control->reg_5->address, + (u8 *)control->reg_5->regs, + f1a->sensor_button_count); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write to reg 5 to 0x%x\n", + __func__, control->reg_5->address); + return retval; + } + } + if (query->has_release_threshold) { + retval = rmi_write_block(fc->rmi_dev, control->reg_6->address, + (u8 *)control->reg_6->regs, + sizeof(control->reg_6->regs)); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg 6 to 0x%x\n", + __func__, control->reg_6->address); + return -EINVAL; + } + } + if (query->has_strongestbtn_hysteresis) { + retval = rmi_write_block(fc->rmi_dev, control->reg_7->address, + (u8 *)control->reg_7->regs, + sizeof(control->reg_7->regs)); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg 7 to 0x%x\n", + __func__, control->reg_7->address); + return -EINVAL; + } + } + if (query->has_filter_strength) { + retval = rmi_write_block(fc->rmi_dev, control->reg_8->address, + (u8 *)control->reg_8->regs, + sizeof(control->reg_8->regs)); + if (retval < 0) { + dev_err(&fc->dev, "%s : Could not write reg 8 to 0x%x\n", + __func__, control->reg_8->address); + return -EINVAL; + } + } + return 0; +} + + +static int rmi_f1a_reset(struct rmi_function_container *fc) +{ + /* we do nnothing here */ + return 0; +} + + +static void rmi_f1a_remove(struct rmi_function_container *fc) +{ + struct f1a_data *f1a = fc->data; + + input_unregister_device(f1a->input); + + rmi_f1a_free_memory(fc); +} + +static int rmi_f1a_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + int error; + int button; + struct rmi_device *rmi_dev = fc->rmi_dev; + struct f1a_data *f1a = fc->data; + u16 data_base_addr = fc->fd.data_base_addr; + + /* Read the button data. */ + error = rmi_read_block(rmi_dev, data_base_addr, f1a->button_data_buffer, + f1a->button_bitmask_size); + if (error < 0) { + dev_err(&fc->dev, "%s: Failed to read button data registers.\n", + __func__); + return error; + } + + /* Generate events for buttons that change state. */ + for (button = 0; button < f1a->sensor_button_count; button++) { + int button_reg; + int button_shift; + bool button_status; + + /* determine which data byte the button status is in */ + button_reg = button / 8; + /* bit shift to get button's status */ + button_shift = button % 8; + button_status = + ((f1a->button_data_buffer[button_reg] >> button_shift) + & 0x01) != 0; + + dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n", + __func__, button, f1a->button_map[button], + button_status); + /* Generate an event here. */ + input_report_key(f1a->input, f1a->button_map[button], + button_status); + } + + input_sync(f1a->input); /* sync after groups of events */ + return 0; +} + +static struct rmi_function_handler function_handler = { + .func = 0x1a, + .init = rmi_f1a_init, + .config = rmi_f1a_config, + .reset = rmi_f1a_reset, + .attention = rmi_f1a_attention, + .remove = rmi_f1a_remove +}; + +static int __init rmi_f1a_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_f1a_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + +/* sysfs functions */ +/* Query */ +simple_show_union_struct_unsigned(query, max_button_count) +simple_show_union_struct_unsigned(query, has_general_control) +simple_show_union_struct_unsigned(query, has_interrupt_enable) +simple_show_union_struct_unsigned(query, has_multibutton_select) +simple_show_union_struct_unsigned(query, has_tx_rx_map) +simple_show_union_struct_unsigned(query, has_perbutton_threshold) +simple_show_union_struct_unsigned(query, has_release_threshold) +simple_show_union_struct_unsigned(query, has_strongestbtn_hysteresis) +simple_show_union_struct_unsigned(query, has_filter_strength) + +/* Control */ +show_store_union_struct_unsigned(control, reg_0, multibutton_report) +show_store_union_struct_unsigned(control, reg_0, filter_mode) +show_store_union_struct_unsigned(control, reg_6, button_release_threshold) +show_store_union_struct_unsigned(control, reg_7, strongest_button_hysteresis) +show_store_union_struct_unsigned(control, reg_8, filter_strength) + +/* repeated register functions */ +show_store_repeated_union_struct_unsigned(control, reg_1, + interrupt_enabled_button) +show_store_repeated_union_struct_unsigned(control, reg_2, multi_button) +show_store_repeated_union_struct_unsigned(control, reg_5, threshold_button) + +static ssize_t rmi_fn_1a_tx_rx_map_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct FUNCTION_DATA *data; + struct f1a_0d_control *control; + int reg_length; + int result, size = 0; + char *temp; + int i; + + fc = to_rmi_function_container(dev); + data = fc->data; + control = &data->control; + /* Read current regtype values */ + reg_length = control->reg_3_4->length; + result = rmi_read_block(fc->rmi_dev, control->reg_3_4->address, + (u8 *)control->reg_3_4->regs, + reg_length * sizeof(u8)); + + if (result < 0) { + dev_dbg(dev, "%s : Could not read regtype at 0x%x\n Data may be outdated.", + __func__, control->reg_3_4->address); + } + temp = buf; + for (i = 0; i < reg_length/2; i++) { + result = snprintf(temp, PAGE_SIZE - size, "%u-%u ", + control->reg_3_4->regs[i].transmitterbtn, + control->reg_3_4->regs[i].receiverbtn); + if (result < 0) { + dev_err(dev, "%s : Could not write output.", __func__); + return result; + } + size += result; + temp += result; + } + result = snprintf(temp, PAGE_SIZE - size, "\n"); + if (result < 0) { + dev_err(dev, "%s : Could not write output.", __func__); + return result; + } + return size + result; +} + +module_init(rmi_f1a_module_init); +module_exit(rmi_f1a_module_exit); + +MODULE_AUTHOR("Vivian Ly "); +MODULE_DESCRIPTION("RMI F1a module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION);