lkml.org 
[lkml]   [2012]   [Aug]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 14/17] input: RMI4 F30 GPIO/LED control
    Date
    Signed-off-by: Christopher Heiny <cheiny@synaptics.com>

    Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
    Cc: Linus Walleij <linus.walleij@stericsson.com>
    Cc: Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>
    Cc: Joeri de Gram <j.de.gram@gmail.com>

    Acked-by: Jean Delvare <khali@linux-fr.org>

    ---

    drivers/input/rmi4/rmi_f30.c | 1247 ++++++++++++++++++++++++++++++++++++++++++
    1 files changed, 1247 insertions(+), 0 deletions(-)

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

    \
     
     \ /
      Last update: 2012-08-18 02:41    [W:0.088 / U:1.764 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site