From patchwork Fri Aug 17 22:17:50 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Christopher Heiny X-Patchwork-Id: 1339731 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 88DB93FC33 for ; Fri, 17 Aug 2012 23:26:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758929Ab2HQXVk (ORCPT ); Fri, 17 Aug 2012 19:21:40 -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 S1758180Ab2HQXUx (ORCPT ); Fri, 17 Aug 2012 19:20:53 -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 q7HMI0WD016258; 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 10/17] input: RM4 F17 Pointing sticks Date: Fri, 17 Aug 2012 15:17:50 -0700 Message-Id: <1345241877-16200-11-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_f17.c | 713 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 713 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_f17.c b/drivers/input/rmi4/rmi_f17.c new file mode 100644 index 0000000..006a716 --- /dev/null +++ b/drivers/input/rmi4/rmi_f17.c @@ -0,0 +1,713 @@ +/* + * Copyright (c) 2012 Synaptics Incorporated + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include "rmi_driver.h" + +#define QUERY_BASE_INDEX 1 +#define MAX_NAME_LENGTH 256 + +union f17_device_query { + struct { + u8 number_of_sticks:3; + }; + u8 regs[1]; +}; + +#define F17_MANUFACTURER_SYNAPTICS 0 +#define F17_MANUFACTURER_NMB 1 +#define F17_MANUFACTURER_ALPS 2 + +struct f17_stick_query { + union { + struct { + u8 manufacturer:4; + u8 resistive:1; + u8 ballistics:1; + u8 reserved1:2; + u8 has_relative:1; + u8 has_absolute:1; + u8 has_gestures:1; + u8 has_dribble:1; + u8 reserved2:4; + }; + u8 regs[2]; + } general; + + union { + struct { + u8 has_single_tap:1; + u8 has_tap_and_hold:1; + u8 has_double_tap:1; + u8 has_early_tap:1; + u8 has_press:1; + }; + u8 regs[1]; + } gestures; +}; + +union f17_device_controls { + struct { + u8 reporting_mode:3; + u8 dribble:1; + }; + u8 regs[1]; +}; + +struct f17_stick_controls { + union { + struct { + u8 z_force_threshold; + u8 radial_force_threshold; + }; + u8 regs[3]; + } general; + + union { + struct { + u8 motion_sensitivity:4; + u8 antijitter:1; + }; + u8 regs[1]; + } relative; + + union { + struct { + u8 single_tap:1; + u8 tap_and_hold:1; + + u8 double_tap:1; + u8 early_tap:1; + u8 press:1; + }; + u8 regs[1]; + } enable; + + u8 maximum_tap_time; + u8 minimum_press_time; + u8 maximum_radial_force; +}; + + +union f17_device_commands { + struct { + u8 rezero:1; + }; + u8 regs[1]; +}; + +struct f17_stick_data { + union { + struct { + u8 x_force_high:8; + u8 y_force_high:8; + u8 y_force_low:4; + u8 x_force_low:4; + u8 z_force:8; + } __attribute__((__packed__)); + struct { + u8 regs[4]; + u16 address; + }; + } abs; + union { + struct { + s8 x_delta:8; + s8 y_delta:8; + } __attribute__((__packed__)); + struct { + u8 regs[2]; + u16 address; + }; + } rel; + union { + struct { + u8 single_tap:1; + u8 tap_and_hold:1; + u8 double_tap:1; + u8 early_tap:1; + u8 press:1; + u8 reserved:3; + } __attribute__((__packed__)); + struct { + u8 regs[1]; + u16 address; + }; + } gestures; +}; + + +/* data specific to f17 that needs to be kept around */ + +struct rmi_f17_stick_data { + struct f17_stick_query query; + struct f17_stick_controls controls; + struct f17_stick_data data; + + u16 control_address; + + int index; + + char input_name[MAX_NAME_LENGTH]; + char input_phys[MAX_NAME_LENGTH]; + struct input_dev *input; + char mouse_name[MAX_NAME_LENGTH]; + char mouse_phys[MAX_NAME_LENGTH]; + struct input_dev *mouse; +}; + +struct rmi_f17_device_data { + u16 control_address; + + union f17_device_query query; + union f17_device_commands commands; + union f17_device_controls controls; + + struct rmi_f17_stick_data *sticks; + +}; + +static ssize_t f17_rezero_show(struct device *dev, + struct device_attribute *attr, + char *buf); +static ssize_t f17_rezero_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + + + +static int f17_alloc_memory(struct rmi_function_container *fc); + +static void f17_free_memory(struct rmi_function_container *fc); + +static int f17_initialize(struct rmi_function_container *fc); + +static int f17_register_devices(struct rmi_function_container *fc); + +static int f17_create_sysfs(struct rmi_function_container *fc); + +static int f17_config(struct rmi_function_container *fc); + + +static struct device_attribute attrs[] = { + __ATTR(rezero, RMI_RW_ATTR, + f17_rezero_show, f17_rezero_store), +}; + + +int f17_read_control_parameters(struct rmi_device *rmi_dev, + struct rmi_f17_device_data *f17) +{ + int retval = 0; + + /* TODO: read this or delete the function */ + + return retval; +} + + +static int f17_init(struct rmi_function_container *fc) +{ + int retval; + + retval = f17_alloc_memory(fc); + if (retval < 0) + goto err_free_data; + + retval = f17_initialize(fc); + if (retval < 0) + goto err_free_data; + + retval = f17_register_devices(fc); + if (retval < 0) + goto err_free_data; + + retval = f17_create_sysfs(fc); + if (retval < 0) + goto err_free_data; + + return 0; + +err_free_data: + f17_free_memory(fc); + + return retval; +} + +static int f17_alloc_memory(struct rmi_function_container *fc) +{ + struct rmi_f17_device_data *f17; + int retval; + + f17 = kzalloc(sizeof(struct rmi_f17_device_data), GFP_KERNEL); + if (!f17) { + dev_err(&fc->dev, "Failed to allocate function data.\n"); + return -ENOMEM; + } + fc->data = f17; + + retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr, + f17->query.regs, sizeof(f17->query.regs)); + if (retval < 0) { + dev_err(&fc->dev, "Failed to read query register.\n"); + goto error_exit; + } + + f17->sticks = kcalloc(f17->query.number_of_sticks + 1, + sizeof(struct rmi_f17_stick_data), GFP_KERNEL); + if (!f17->sticks) { + dev_err(&fc->dev, "Failed to allocate per stick data.\n"); + return -ENOMEM; + } + + return 0; + +error_exit: + kfree(f17); + fc->data = NULL; + return retval; +} + +static void f17_free_memory(struct rmi_function_container *fc) +{ + struct rmi_f17_device_data *f17 = fc->data; + + if (f17) { + kfree(f17->sticks); + f17->sticks = NULL; + } + kfree(f17); + fc->data = NULL; +} + +static int f17_init_stick(struct rmi_device *rmi_dev, + struct rmi_f17_stick_data *stick, + u16 *next_query_reg, u16 *next_data_reg, + u16 *next_control_reg) { + int retval = 0; + + retval = rmi_read_block(rmi_dev, *next_query_reg, + stick->query.general.regs, + sizeof(stick->query.general.regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Failed to read stick general query.\n"); + return retval; + } + *next_query_reg += sizeof(stick->query.general.regs); + + dev_dbg(&rmi_dev->dev, "Stick %d found\n", stick->index); + dev_dbg(&rmi_dev->dev, " Manufacturer: %d.\n", + stick->query.general.manufacturer); + dev_dbg(&rmi_dev->dev, " Resistive: %d.\n", + stick->query.general.resistive); + dev_dbg(&rmi_dev->dev, " Ballistics: %d.\n", + stick->query.general.ballistics); + dev_dbg(&rmi_dev->dev, " Manufacturer: %d.\n", + stick->query.general.ballistics); + dev_dbg(&rmi_dev->dev, " Has relative: %d.\n", + stick->query.general.has_relative); + dev_dbg(&rmi_dev->dev, " Has absolute: %d.\n", + stick->query.general.has_absolute); + dev_dbg(&rmi_dev->dev, " Had dribble: %d.\n", + stick->query.general.has_dribble); + dev_dbg(&rmi_dev->dev, " Has gestures: %d.\n", + stick->query.general.has_gestures); + + if (stick->query.general.has_gestures) { + retval = rmi_read_block(rmi_dev, *next_query_reg, + stick->query.gestures.regs, + sizeof(stick->query.gestures.regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, + "Failed to read F17 gestures query, code %d.\n", + retval); + return retval; + } + *next_query_reg += sizeof(stick->query.gestures.regs); + dev_dbg(&rmi_dev->dev, " single tap: %d.\n", + stick->query.gestures.has_single_tap); + dev_dbg(&rmi_dev->dev, " tap & hold: %d.\n", + stick->query.gestures.has_tap_and_hold); + dev_dbg(&rmi_dev->dev, " double tap: %d.\n", + stick->query.gestures.has_double_tap); + dev_dbg(&rmi_dev->dev, " early tap: %d.\n", + stick->query.gestures.has_early_tap); + dev_dbg(&rmi_dev->dev, " press: %d.\n", + stick->query.gestures.has_press); + } + if (stick->query.general.has_absolute) { + stick->data.abs.address = *next_data_reg; + *next_data_reg += sizeof(stick->data.abs.regs); + } + if (stick->query.general.has_relative) { + stick->data.rel.address = *next_data_reg; + *next_data_reg += sizeof(stick->data.rel.regs); + } + if (stick->query.general.has_gestures) { + stick->data.gestures.address = *next_data_reg; + *next_data_reg += sizeof(stick->data.gestures.regs); + } + + return retval; +} + +static int f17_initialize(struct rmi_function_container *fc) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_f17_device_data *f17 = fc->data; + int i; + int retval; + u16 next_query_reg = fc->fd.query_base_addr; + u16 next_data_reg = fc->fd.data_base_addr; + u16 next_control_reg = fc->fd.control_base_addr; + + retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr, + f17->query.regs, sizeof(f17->query.regs)); + if (retval < 0) { + dev_err(&fc->dev, "Failed to read query register.\n"); + return retval; + } + dev_info(&fc->dev, "Found F17 with %d sticks.\n", + f17->query.number_of_sticks + 1); + next_query_reg += sizeof(f17->query.regs); + + retval = rmi_read_block(rmi_dev, fc->fd.command_base_addr, + f17->commands.regs, sizeof(f17->commands.regs)); + if (retval < 0) { + dev_err(&fc->dev, "Failed to read command register.\n"); + return retval; + } + + f17->control_address = fc->fd.control_base_addr; + retval = f17_read_control_parameters(rmi_dev, f17); + if (retval < 0) { + dev_err(&fc->dev, "Failed to initialize F17 control params.\n"); + return retval; + } + + for (i = 0; i < f17->query.number_of_sticks + 1; i++) { + f17->sticks[i].index = i; + retval = f17_init_stick(rmi_dev, &f17->sticks[i], + &next_query_reg, &next_data_reg, + &next_control_reg); + if (!retval) { + dev_err(&fc->dev, "Failed to init stick %d.\n", i); + return retval; + } + } + + return retval; +} + +static int f17_register_stick(struct rmi_function_container *fc, + struct rmi_f17_stick_data *stick) { + struct rmi_device *rmi_dev = fc->rmi_dev; + int retval = 0; + + if (stick->query.general.has_absolute) { + struct input_dev *input_dev; + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&rmi_dev->dev, "Failed to allocate stick device %d.\n", + stick->index); + return -ENOMEM; + } + + snprintf(stick->input_name, sizeof(stick->input_name), + "RMI F%02x Stick %d", 0x17, stick->index); + snprintf(stick->input_phys, sizeof(stick->input_phys), + "sensor00fn%02x/stick%d", 0x17, stick->index); + input_dev->name = stick->input_name; + input_dev->phys = stick->input_phys; + input_dev->dev.parent = &fc->dev; + input_set_drvdata(input_dev, stick); + + retval = input_register_device(input_dev); + if (retval < 0) { + dev_err(&rmi_dev->dev, "Failed to register stick device %d.\n", + stick->index); + goto error_free_device; + } + stick->input = input_dev; + } + + if (stick->query.general.has_relative) { + struct input_dev *input_dev_mouse; + /*create input device for mouse events */ + input_dev_mouse = input_allocate_device(); + if (!input_dev_mouse) { + retval = -ENOMEM; + goto error_free_device; + } + + snprintf(stick->mouse_name, sizeof(stick->mouse_name), + "RMI F%02x Mouse %d", 0x17, stick->index); + snprintf(stick->mouse_phys, sizeof(stick->mouse_name), + "sensor00fn%02x/mouse%d", 0x17, stick->index); + input_dev_mouse->name = stick->mouse_name; + input_dev_mouse->phys = stick->mouse_phys; + input_dev_mouse->dev.parent = &fc->dev; + + input_dev_mouse->id.vendor = 0x18d1; + input_dev_mouse->id.product = 0x0210; + input_dev_mouse->id.version = 0x0100; + + set_bit(EV_REL, input_dev_mouse->evbit); + set_bit(REL_X, input_dev_mouse->relbit); + set_bit(REL_Y, input_dev_mouse->relbit); + + set_bit(BTN_MOUSE, input_dev_mouse->evbit); + /* Register device's buttons and keys */ + set_bit(EV_KEY, input_dev_mouse->evbit); + set_bit(BTN_LEFT, input_dev_mouse->keybit); + set_bit(BTN_MIDDLE, input_dev_mouse->keybit); + set_bit(BTN_RIGHT, input_dev_mouse->keybit); + + retval = input_register_device(input_dev_mouse); + if (retval < 0) + goto error_free_device; + stick->mouse = input_dev_mouse; + } + + return 0; + +error_free_device: + if (stick->input) { + input_free_device(stick->input); + stick->input = NULL; + } + if (stick->mouse) { + input_free_device(stick->mouse); + stick->mouse = NULL; + } + return retval; +} + +static int f17_register_devices(struct rmi_function_container *fc) +{ + struct rmi_f17_device_data *f17 = fc->data; + int i; + int retval = 0; + + for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++) + retval = f17_register_stick(fc, &f17->sticks[i]); + + return retval; +} + +static int f17_create_sysfs(struct rmi_function_container *fc) +{ + int attr_count = 0; + int rc; + + dev_dbg(&fc->dev, "Creating sysfs files.\n"); + /* Set up sysfs device attributes. */ + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + if (sysfs_create_file + (&fc->dev.kobj, &attrs[attr_count].attr) < 0) { + dev_err(&fc->dev, + "Failed to create sysfs file for %s.", + attrs[attr_count].attr.name); + rc = -ENODEV; + goto err_remove_sysfs; + } + } + + return 0; + +err_remove_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr); + return rc; + +} + +static int f17_config(struct rmi_function_container *fc) +{ + struct rmi_f17_device_data *f17 = fc->data; + int retval; + + retval = rmi_write_block(fc->rmi_dev, f17->control_address, + f17->controls.regs, sizeof(f17->controls.regs)); + if (retval < 0) { + dev_err(&fc->dev, "Could not write stick controls to 0x%04x\n", + f17->control_address); + return retval; + } + + return retval; +} + +static void f17_remove(struct rmi_function_container *fc) +{ + struct rmi_f17_device_data *f17 = fc->data; + int i = 0; + + for (i = 0; i < ARRAY_SIZE(attrs); i++) + sysfs_remove_file(&fc->dev.kobj, &attrs[i].attr); + + for (i = 0; i < f17->query.number_of_sticks + 1; i++) + input_unregister_device(f17->sticks[i].input); + + f17_free_memory(fc); +} + +static int f17_process_stick(struct rmi_device *rmi_dev, + struct rmi_f17_stick_data *stick) { + int retval = 0; + + if (stick->query.general.has_absolute) { + retval = rmi_read_block(rmi_dev, stick->data.abs.address, + stick->data.abs.regs, sizeof(stick->data.abs.regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, + "Failed to read abs data for stick %d, code %d.\n", + stick->index, retval); + goto error_exit; + } + } + if (stick->query.general.has_relative) { + retval = rmi_read_block(rmi_dev, stick->data.rel.address, + stick->data.rel.regs, sizeof(stick->data.rel.regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, + "Failed to read rel data for stick %d, code %d.\n", + stick->index, retval); + goto error_exit; + } + dev_dbg(&rmi_dev->dev, "Reporting dX: %d, dy: %d\n", + stick->data.rel.x_delta, stick->data.rel.y_delta); + input_report_rel(stick->mouse, REL_X, stick->data.rel.x_delta); + input_report_rel(stick->mouse, REL_Y, stick->data.rel.y_delta); + } + if (stick->query.general.has_gestures) { + retval = rmi_read_block(rmi_dev, stick->data.gestures.address, + stick->data.gestures.regs, + sizeof(stick->data.gestures.regs)); + if (retval < 0) { + dev_err(&rmi_dev->dev, + "Failed to read gestures for stick %d, code %d.\n", + stick->index, retval); + goto error_exit; + } + } + retval = 0; + +error_exit: + if (stick->input) + input_sync(stick->input); + if (stick->mouse) + input_sync(stick->mouse); + return retval; +} + +static int f17_attention(struct rmi_function_container *fc, u8 *irq_bits) +{ + struct rmi_device *rmi_dev = fc->rmi_dev; + struct rmi_f17_device_data *f17 = fc->data; + int i; + int retval = 0; + + for (i = 0; i < f17->query.number_of_sticks + 1 && !retval; i++) + retval = f17_process_stick(rmi_dev, &f17->sticks[i]); + + return retval; +} + +static struct rmi_function_handler function_handler = { + .func = 0x17, + .init = f17_init, + .config = f17_config, + .attention = f17_attention, + .remove = f17_remove +}; + +static int __init f17_module_init(void) +{ + int error; + + error = rmi_register_function_driver(&function_handler); + if (error < 0) { + pr_err("%s: register failed!\n", __func__); + return error; + } + + return 0; +} + +static void f17_module_exit(void) +{ + rmi_unregister_function_driver(&function_handler); +} + + +static ssize_t f17_rezero_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct rmi_function_container *fc; + struct rmi_f17_device_data *f17; + + fc = to_rmi_function_container(dev); + f17 = fc->data; + + return snprintf(buf, PAGE_SIZE, "%u\n", + f17->commands.rezero); + +} + +static ssize_t f17_rezero_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct rmi_function_container *fc; + struct rmi_f17_device_data *data; + unsigned int new_value; + int len; + + fc = to_rmi_function_container(dev); + data = fc->data; + len = sscanf(buf, "%u", &new_value); + if (new_value != 0 && new_value != 1) { + dev_err(dev, + "%s: Error - rezero is not a valid value 0x%x.\n", + __func__, new_value); + return -EINVAL; + } + data->commands.rezero = new_value; + len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr, + data->commands.rezero); + + if (len < 0) { + dev_err(dev, "%s : Could not write rezero to 0x%x\n", + __func__, fc->fd.command_base_addr); + return -EINVAL; + } + return count; +} + +module_init(f17_module_init); +module_exit(f17_module_exit); + +MODULE_AUTHOR("Christopher Heiny "); +MODULE_DESCRIPTION("RMI F17 module"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION);