![Andrew Duggan](/assets/img/avatar_default.png)
By convention the first 6 bits of F30 Ctrl 2 and 3 are used to signify GPIOs which are connected to buttons. Additional GPIOs may be used as input GPIOs to signal the touch controller of some event (ie disable touchpad). These additional GPIOs may meet the criteria of a button in rmi_f30_is_valid_button() but should not be considered buttons. This patch limits the GPIOs which are mapped to buttons to just the first 6. Signed-off-by: Andrew Duggan <aduggan@synaptics.com> Reported-by: Daniel Martin <consume.noise@gmail.com> Tested-by: Daniel Martin <consume.noise@gmail.com> Acked-By: Benjamin Tissoires <benjamin.tissoires@redhat.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
408 lines
11 KiB
C
408 lines
11 KiB
C
/*
|
|
* Copyright (c) 2012-2016 Synaptics Incorporated
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
* the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/rmi.h>
|
|
#include <linux/input.h>
|
|
#include <linux/slab.h>
|
|
#include "rmi_driver.h"
|
|
|
|
#define RMI_F30_QUERY_SIZE 2
|
|
|
|
/* Defs for Query 0 */
|
|
#define RMI_F30_EXTENDED_PATTERNS 0x01
|
|
#define RMI_F30_HAS_MAPPABLE_BUTTONS BIT(1)
|
|
#define RMI_F30_HAS_LED BIT(2)
|
|
#define RMI_F30_HAS_GPIO BIT(3)
|
|
#define RMI_F30_HAS_HAPTIC BIT(4)
|
|
#define RMI_F30_HAS_GPIO_DRV_CTL BIT(5)
|
|
#define RMI_F30_HAS_MECH_MOUSE_BTNS BIT(6)
|
|
|
|
/* Defs for Query 1 */
|
|
#define RMI_F30_GPIO_LED_COUNT 0x1F
|
|
|
|
/* Defs for Control Registers */
|
|
#define RMI_F30_CTRL_1_GPIO_DEBOUNCE 0x01
|
|
#define RMI_F30_CTRL_1_HALT BIT(4)
|
|
#define RMI_F30_CTRL_1_HALTED BIT(5)
|
|
#define RMI_F30_CTRL_10_NUM_MECH_MOUSE_BTNS 0x03
|
|
|
|
#define RMI_F30_CTRL_MAX_REGS 32
|
|
#define RMI_F30_CTRL_MAX_BYTES DIV_ROUND_UP(RMI_F30_CTRL_MAX_REGS, 8)
|
|
#define RMI_F30_CTRL_MAX_REG_BLOCKS 11
|
|
|
|
#define RMI_F30_CTRL_REGS_MAX_SIZE (RMI_F30_CTRL_MAX_BYTES \
|
|
+ 1 \
|
|
+ RMI_F30_CTRL_MAX_BYTES \
|
|
+ RMI_F30_CTRL_MAX_BYTES \
|
|
+ RMI_F30_CTRL_MAX_BYTES \
|
|
+ 6 \
|
|
+ RMI_F30_CTRL_MAX_REGS \
|
|
+ RMI_F30_CTRL_MAX_REGS \
|
|
+ RMI_F30_CTRL_MAX_BYTES \
|
|
+ 1 \
|
|
+ 1)
|
|
|
|
#define TRACKSTICK_RANGE_START 3
|
|
#define TRACKSTICK_RANGE_END 6
|
|
|
|
struct rmi_f30_ctrl_data {
|
|
int address;
|
|
int length;
|
|
u8 *regs;
|
|
};
|
|
|
|
struct f30_data {
|
|
/* Query Data */
|
|
bool has_extended_pattern;
|
|
bool has_mappable_buttons;
|
|
bool has_led;
|
|
bool has_gpio;
|
|
bool has_haptic;
|
|
bool has_gpio_driver_control;
|
|
bool has_mech_mouse_btns;
|
|
u8 gpioled_count;
|
|
|
|
u8 register_count;
|
|
|
|
/* Control Register Data */
|
|
struct rmi_f30_ctrl_data ctrl[RMI_F30_CTRL_MAX_REG_BLOCKS];
|
|
u8 ctrl_regs[RMI_F30_CTRL_REGS_MAX_SIZE];
|
|
u32 ctrl_regs_size;
|
|
|
|
u8 data_regs[RMI_F30_CTRL_MAX_BYTES];
|
|
u16 *gpioled_key_map;
|
|
|
|
struct input_dev *input;
|
|
|
|
struct rmi_function *f03;
|
|
bool trackstick_buttons;
|
|
};
|
|
|
|
static int rmi_f30_read_control_parameters(struct rmi_function *fn,
|
|
struct f30_data *f30)
|
|
{
|
|
int error;
|
|
|
|
error = rmi_read_block(fn->rmi_dev, fn->fd.control_base_addr,
|
|
f30->ctrl_regs, f30->ctrl_regs_size);
|
|
if (error) {
|
|
dev_err(&fn->dev,
|
|
"%s: Could not read control registers at 0x%x: %d\n",
|
|
__func__, fn->fd.control_base_addr, error);
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rmi_f30_report_button(struct rmi_function *fn,
|
|
struct f30_data *f30, unsigned int button)
|
|
{
|
|
unsigned int reg_num = button >> 3;
|
|
unsigned int bit_num = button & 0x07;
|
|
u16 key_code = f30->gpioled_key_map[button];
|
|
bool key_down = !(f30->data_regs[reg_num] & BIT(bit_num));
|
|
|
|
if (f30->trackstick_buttons &&
|
|
button >= TRACKSTICK_RANGE_START &&
|
|
button <= TRACKSTICK_RANGE_END) {
|
|
rmi_f03_overwrite_button(f30->f03, key_code, key_down);
|
|
} else {
|
|
rmi_dbg(RMI_DEBUG_FN, &fn->dev,
|
|
"%s: call input report key (0x%04x) value (0x%02x)",
|
|
__func__, key_code, key_down);
|
|
|
|
input_report_key(f30->input, key_code, key_down);
|
|
}
|
|
}
|
|
|
|
static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits)
|
|
{
|
|
struct f30_data *f30 = dev_get_drvdata(&fn->dev);
|
|
struct rmi_driver_data *drvdata = dev_get_drvdata(&fn->rmi_dev->dev);
|
|
int error;
|
|
int i;
|
|
|
|
/* Read the gpi led data. */
|
|
if (drvdata->attn_data.data) {
|
|
if (drvdata->attn_data.size < f30->register_count) {
|
|
dev_warn(&fn->dev,
|
|
"F30 interrupted, but data is missing\n");
|
|
return 0;
|
|
}
|
|
memcpy(f30->data_regs, drvdata->attn_data.data,
|
|
f30->register_count);
|
|
drvdata->attn_data.data += f30->register_count;
|
|
drvdata->attn_data.size -= f30->register_count;
|
|
} else {
|
|
error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr,
|
|
f30->data_regs, f30->register_count);
|
|
if (error) {
|
|
dev_err(&fn->dev,
|
|
"%s: Failed to read F30 data registers: %d\n",
|
|
__func__, error);
|
|
return error;
|
|
}
|
|
}
|
|
|
|
if (f30->has_gpio) {
|
|
for (i = 0; i < f30->gpioled_count; i++)
|
|
if (f30->gpioled_key_map[i] != KEY_RESERVED)
|
|
rmi_f30_report_button(fn, f30, i);
|
|
if (f30->trackstick_buttons)
|
|
rmi_f03_commit_buttons(f30->f03);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rmi_f30_config(struct rmi_function *fn)
|
|
{
|
|
struct f30_data *f30 = dev_get_drvdata(&fn->dev);
|
|
struct rmi_driver *drv = fn->rmi_dev->driver;
|
|
const struct rmi_device_platform_data *pdata =
|
|
rmi_get_platform_data(fn->rmi_dev);
|
|
int error;
|
|
|
|
/* can happen if f30_data.disable is set */
|
|
if (!f30)
|
|
return 0;
|
|
|
|
if (pdata->f30_data.trackstick_buttons) {
|
|
/* Try [re-]establish link to F03. */
|
|
f30->f03 = rmi_find_function(fn->rmi_dev, 0x03);
|
|
f30->trackstick_buttons = f30->f03 != NULL;
|
|
}
|
|
|
|
if (pdata->f30_data.disable) {
|
|
drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask);
|
|
} else {
|
|
/* Write Control Register values back to device */
|
|
error = rmi_write_block(fn->rmi_dev, fn->fd.control_base_addr,
|
|
f30->ctrl_regs, f30->ctrl_regs_size);
|
|
if (error) {
|
|
dev_err(&fn->dev,
|
|
"%s: Could not write control registers at 0x%x: %d\n",
|
|
__func__, fn->fd.control_base_addr, error);
|
|
return error;
|
|
}
|
|
|
|
drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl,
|
|
int *ctrl_addr, int len, u8 **reg)
|
|
{
|
|
ctrl->address = *ctrl_addr;
|
|
ctrl->length = len;
|
|
ctrl->regs = *reg;
|
|
*ctrl_addr += len;
|
|
*reg += len;
|
|
}
|
|
|
|
static bool rmi_f30_is_valid_button(int button, struct rmi_f30_ctrl_data *ctrl)
|
|
{
|
|
int byte_position = button >> 3;
|
|
int bit_position = button & 0x07;
|
|
|
|
/*
|
|
* ctrl2 -> dir == 0 -> input mode
|
|
* ctrl3 -> data == 1 -> actual button
|
|
*/
|
|
return !(ctrl[2].regs[byte_position] & BIT(bit_position)) &&
|
|
(ctrl[3].regs[byte_position] & BIT(bit_position));
|
|
}
|
|
|
|
static int rmi_f30_map_gpios(struct rmi_function *fn,
|
|
struct f30_data *f30)
|
|
{
|
|
const struct rmi_device_platform_data *pdata =
|
|
rmi_get_platform_data(fn->rmi_dev);
|
|
struct input_dev *input = f30->input;
|
|
unsigned int button = BTN_LEFT;
|
|
unsigned int trackstick_button = BTN_LEFT;
|
|
bool button_mapped = false;
|
|
int i;
|
|
int button_count = min_t(u8, f30->gpioled_count, TRACKSTICK_RANGE_END);
|
|
|
|
f30->gpioled_key_map = devm_kcalloc(&fn->dev,
|
|
button_count,
|
|
sizeof(f30->gpioled_key_map[0]),
|
|
GFP_KERNEL);
|
|
if (!f30->gpioled_key_map) {
|
|
dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
for (i = 0; i < button_count; i++) {
|
|
if (!rmi_f30_is_valid_button(i, f30->ctrl))
|
|
continue;
|
|
|
|
if (pdata->f30_data.trackstick_buttons &&
|
|
i >= TRACKSTICK_RANGE_START && i < TRACKSTICK_RANGE_END) {
|
|
f30->gpioled_key_map[i] = trackstick_button++;
|
|
} else if (!pdata->f30_data.buttonpad || !button_mapped) {
|
|
f30->gpioled_key_map[i] = button;
|
|
input_set_capability(input, EV_KEY, button++);
|
|
button_mapped = true;
|
|
}
|
|
}
|
|
|
|
input->keycode = f30->gpioled_key_map;
|
|
input->keycodesize = sizeof(f30->gpioled_key_map[0]);
|
|
input->keycodemax = f30->gpioled_count;
|
|
|
|
/*
|
|
* Buttonpad could be also inferred from f30->has_mech_mouse_btns,
|
|
* but I am not sure, so use only the pdata info and the number of
|
|
* mapped buttons.
|
|
*/
|
|
if (pdata->f30_data.buttonpad || (button - BTN_LEFT == 1))
|
|
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rmi_f30_initialize(struct rmi_function *fn, struct f30_data *f30)
|
|
{
|
|
u8 *ctrl_reg = f30->ctrl_regs;
|
|
int control_address = fn->fd.control_base_addr;
|
|
u8 buf[RMI_F30_QUERY_SIZE];
|
|
int error;
|
|
|
|
error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
|
|
buf, RMI_F30_QUERY_SIZE);
|
|
if (error) {
|
|
dev_err(&fn->dev, "Failed to read query register\n");
|
|
return error;
|
|
}
|
|
|
|
f30->has_extended_pattern = buf[0] & RMI_F30_EXTENDED_PATTERNS;
|
|
f30->has_mappable_buttons = buf[0] & RMI_F30_HAS_MAPPABLE_BUTTONS;
|
|
f30->has_led = buf[0] & RMI_F30_HAS_LED;
|
|
f30->has_gpio = buf[0] & RMI_F30_HAS_GPIO;
|
|
f30->has_haptic = buf[0] & RMI_F30_HAS_HAPTIC;
|
|
f30->has_gpio_driver_control = buf[0] & RMI_F30_HAS_GPIO_DRV_CTL;
|
|
f30->has_mech_mouse_btns = buf[0] & RMI_F30_HAS_MECH_MOUSE_BTNS;
|
|
f30->gpioled_count = buf[1] & RMI_F30_GPIO_LED_COUNT;
|
|
|
|
f30->register_count = DIV_ROUND_UP(f30->gpioled_count, 8);
|
|
|
|
if (f30->has_gpio && f30->has_led)
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[0], &control_address,
|
|
f30->register_count, &ctrl_reg);
|
|
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[1], &control_address,
|
|
sizeof(u8), &ctrl_reg);
|
|
|
|
if (f30->has_gpio) {
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[2], &control_address,
|
|
f30->register_count, &ctrl_reg);
|
|
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[3], &control_address,
|
|
f30->register_count, &ctrl_reg);
|
|
}
|
|
|
|
if (f30->has_led) {
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[4], &control_address,
|
|
f30->register_count, &ctrl_reg);
|
|
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[5], &control_address,
|
|
f30->has_extended_pattern ? 6 : 2,
|
|
&ctrl_reg);
|
|
}
|
|
|
|
if (f30->has_led || f30->has_gpio_driver_control) {
|
|
/* control 6 uses a byte per gpio/led */
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[6], &control_address,
|
|
f30->gpioled_count, &ctrl_reg);
|
|
}
|
|
|
|
if (f30->has_mappable_buttons) {
|
|
/* control 7 uses a byte per gpio/led */
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[7], &control_address,
|
|
f30->gpioled_count, &ctrl_reg);
|
|
}
|
|
|
|
if (f30->has_haptic) {
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[8], &control_address,
|
|
f30->register_count, &ctrl_reg);
|
|
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[9], &control_address,
|
|
sizeof(u8), &ctrl_reg);
|
|
}
|
|
|
|
if (f30->has_mech_mouse_btns)
|
|
rmi_f30_set_ctrl_data(&f30->ctrl[10], &control_address,
|
|
sizeof(u8), &ctrl_reg);
|
|
|
|
f30->ctrl_regs_size = ctrl_reg -
|
|
f30->ctrl_regs ?: RMI_F30_CTRL_REGS_MAX_SIZE;
|
|
|
|
error = rmi_f30_read_control_parameters(fn, f30);
|
|
if (error) {
|
|
dev_err(&fn->dev,
|
|
"Failed to initialize F30 control params: %d\n",
|
|
error);
|
|
return error;
|
|
}
|
|
|
|
if (f30->has_gpio) {
|
|
error = rmi_f30_map_gpios(fn, f30);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rmi_f30_probe(struct rmi_function *fn)
|
|
{
|
|
struct rmi_device *rmi_dev = fn->rmi_dev;
|
|
const struct rmi_device_platform_data *pdata =
|
|
rmi_get_platform_data(rmi_dev);
|
|
struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
|
|
struct f30_data *f30;
|
|
int error;
|
|
|
|
if (pdata->f30_data.disable)
|
|
return 0;
|
|
|
|
if (!drv_data->input) {
|
|
dev_info(&fn->dev, "F30: no input device found, ignoring\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
f30 = devm_kzalloc(&fn->dev, sizeof(*f30), GFP_KERNEL);
|
|
if (!f30)
|
|
return -ENOMEM;
|
|
|
|
f30->input = drv_data->input;
|
|
|
|
error = rmi_f30_initialize(fn, f30);
|
|
if (error)
|
|
return error;
|
|
|
|
dev_set_drvdata(&fn->dev, f30);
|
|
return 0;
|
|
}
|
|
|
|
struct rmi_function_handler rmi_f30_handler = {
|
|
.driver = {
|
|
.name = "rmi4_f30",
|
|
},
|
|
.func = 0x30,
|
|
.probe = rmi_f30_probe,
|
|
.config = rmi_f30_config,
|
|
.attention = rmi_f30_attention,
|
|
};
|