This patch implements a driver supporting Synaptics ClearPad and other
touchscreen sensors that use the RMI4 protocol, as defined here:
http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
as well as successor documents that haven't made their way through to
publication yet.
This code supersedes the patch submitted on 2012-08-17. For all files
included in this patch, we believe that all outstanding issues arising
from the previous submissions have been addressed, except for looking into
using irq_chip to manage chip interrupt dispatch - was passed on that mainly
to limit the amount of major structural change since the previous patch. We
will continue to pursue the irq_chip request, however.
Also, at the request of several maintainers, we have reduced the number of
files included in this patch. Hopefully this is much less of a patch bomb
(perhaps just a patch firecracker). The files provided are sufficient to
fully drive an RMI4 2D sensor, handling full multifinger reporting and device
and power management.
This patch is against the v3.6 tag of Linus' kernel tree, commit
a0d271cbfed1dd50278c6b06bead3d00ba0a88f9. It should work fine with kernels
back to 3.1.
Included in this patch are:
- full support for an RMI virtual bus as a standard kernel bus
- physical layer implementation for I2C
- device driver for general RMI4 sensor functionality
- function implementations for the following RMI4 functions:
* F01 device control
* F11 multifinger pointing
The driver supports a system having one or more RMI sensors attached to it.
Most devices have just a single touch sensor, but some have more than one.
An example is the Fuse concept phone, which has 4 RMI sensors in it.
Each sensor is presented as a device on the RMI logical bus (/sys/bus/rmi).
Devices are named/numbered in the order they are discovered on the bus,
starting with /sys/bus/rmi/devices/sensor00 for the first once, .../sensor01
for the second one, and so on.
Individual RMI functions are presented as child devices of the sensor device.
For example, sensor00.fn01, sensor00.fn11, and so on. Control of an RMI
function's operating parameters is implemented via sysfs or debugfs (depending
on whether the parameters are used during normal operation or system
development/prototyping).
The amount of feedback received on previous patches precludes addressing each
item individually. However, major changes for this patch are:
- elimination of ad-hoc bus matching/binding and reliance on standard bus
implementation.
- elimination of out-of-tree features such as EARLY_SUSPEND
- use of devm_kzalloc where appropriate
- partition of control parameters between sysfs and debugfs
- simplified configuration and control of debugging related features
- IRQ management consolidated in once place
We've broken this patch into 6 parts, as follows:
01 - public header files and documentation
02 - core sensor and bus implementation
03 - I2C physical layer driver
04 - Kconfigs and Makefiles
05..06 - drivers for individual RMI functions
Comments and other feedback on this driver are welcomed.
Christopher Heiny and the Synaptics RMI4 driver team
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Jean Delvare <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
---
The I2C physical driver is not extensively changed in terms of functionality
since the previous patch. Management of the attention GPIO has been moved to
rmi_driver.c (see previous email), and most of the debug related interfaces
have been moved from sysfs to debugfs. Control of the debug features has been
moved from compile-time to runtime switches available via debugfs.
The core I2C functionality was previously ACKed by Jean Delvare. I don't
believe that portion of the code has changed much since then, but we'd
appreciate a second glance at this.
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
Cc: Jean Delvare <[email protected]>
Acked-by: Jean Delvare <[email protected]>
---
drivers/input/rmi4/rmi_i2c.c | 455 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 455 insertions(+), 0 deletions(-)
diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
new file mode 100644
index 0000000..c21a27c
--- /dev/null
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -0,0 +1,455 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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 <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kconfig.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+struct rmi_i2c_data {
+ struct mutex page_mutex;
+ int page;
+ struct rmi_phys_device *phys;
+
+ bool comms_debug;
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_comms;
+#endif
+};
+
+#ifdef CONFIG_RMI4_DEBUG
+
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+
+static int setup_debugfs(struct rmi_device *rmi_dev, struct rmi_i2c_data *data);
+static void teardown_debugfs(struct rmi_i2c_data *data);
+
+struct i2c_debugfs_data {
+ bool done;
+ struct rmi_i2c_data *i2c_data;
+};
+
+static int debug_open(struct inode *inodep, struct file *filp)
+{
+ struct i2c_debugfs_data *data;
+
+ data = kzalloc(sizeof(struct i2c_debugfs_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->i2c_data = inodep->i_private;
+ filp->private_data = data;
+ return 0;
+}
+
+static int debug_release(struct inode *inodep, struct file *filp)
+{
+ kfree(filp->private_data);
+ return 0;
+}
+
+static ssize_t comms_debug_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct i2c_debugfs_data *dfs = filp->private_data;
+ struct rmi_i2c_data *data = dfs->i2c_data;
+
+ if (dfs->done)
+ return 0;
+
+ dfs->done = 1;
+
+ retval = snprintf(local_buf, PAGE_SIZE, "%u\n", data->comms_debug);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t comms_debug_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ unsigned int new_value;
+ struct i2c_debugfs_data *dfs = filp->private_data;
+ struct rmi_i2c_data *data = dfs->i2c_data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ retval = sscanf(local_buf, "%u", &new_value);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ data->comms_debug = new_value;
+
+ return size;
+}
+
+
+static const struct file_operations comms_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .release = debug_release,
+ .read = comms_debug_read,
+ .write = comms_debug_write,
+};
+
+static int setup_debugfs(struct rmi_device *rmi_dev, struct rmi_i2c_data *data)
+{
+ if (!rmi_dev->debugfs_root)
+ return -ENODEV;
+
+ data->debugfs_comms = debugfs_create_file("comms_debug", RMI_RW_ATTR,
+ rmi_dev->debugfs_root, data, &comms_debug_fops);
+ if (!data->debugfs_comms || IS_ERR(data->debugfs_comms)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs comms_debug.\n");
+ data->debugfs_comms = NULL;
+ }
+
+ return 0;
+}
+
+static void teardown_debugfs(struct rmi_i2c_data *data)
+{
+ if (data->debugfs_comms)
+ debugfs_remove(data->debugfs_comms);
+}
+#endif
+
+#define COMMS_DEBUG(data) (IS_ENABLED(CONFIG_RMI4_DEBUG) && data->comms_debug)
+
+#define RMI_PAGE_SELECT_REGISTER 0xff
+#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static char *phys_proto_name = "i2c";
+
+/*
+ * rmi_set_page - Set RMI page
+ * @phys: The pointer to the rmi_phys_device struct
+ * @page: The new page address.
+ *
+ * RMI devices have 16-bit addressing, but some of the physical
+ * implementations (like SMBus) only have 8-bit addressing. So RMI implements
+ * a page address at 0xff of every page so we can reliable page addresses
+ * every 256 registers.
+ *
+ * The page_mutex lock must be held when this function is entered.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int rmi_set_page(struct rmi_phys_device *phys, u8 page)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
+ int retval;
+
+ if (COMMS_DEBUG(data))
+ dev_dbg(&client->dev, "writes 3 bytes: %02x %02x\n",
+ txbuf[0], txbuf[1]);
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ phys->info.tx_errs++;
+ dev_err(&client->dev,
+ "%s: set page failed: %d.", __func__, retval);
+ return (retval < 0) ? retval : -EIO;
+ }
+ data->page = page;
+ return 0;
+}
+
+static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[len + 1];
+ int retval;
+
+ txbuf[0] = addr & 0xff;
+ memcpy(txbuf + 1, buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (COMMS_DEBUG(data)) {
+ char debug_buf[len*3 + 1];
+ int i;
+ int n = 0;
+ char *temp = debug_buf;
+
+ for (i = 0; i < len; i++) {
+ n = sprintf(temp, " %02x", buf[i]);
+ temp += n;
+ }
+ dev_dbg(&client->dev, "writes %d bytes at %#06x:%s\n",
+ len, addr, debug_buf);
+ }
+
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval < 0)
+ phys->info.tx_errs++;
+ else
+ retval--; /* don't count the address byte */
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+
+static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[1] = {addr & 0xff};
+ int retval;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (COMMS_DEBUG(data))
+ dev_dbg(&client->dev, "writes 1 bytes: %02x\n", txbuf[0]);
+
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ phys->info.tx_errs++;
+ retval = (retval < 0) ? retval : -EIO;
+ goto exit;
+ }
+
+ retval = i2c_master_recv(client, buf, len);
+
+ phys->info.rx_count++;
+ phys->info.rx_bytes += len;
+ if (retval < 0)
+ phys->info.rx_errs++;
+ else if (COMMS_DEBUG(data)) {
+ char debug_buf[len*3 + 1];
+ char *temp = debug_buf;
+ int i;
+ int n = 0;
+
+ for (i = 0; i < len; i++) {
+ n = sprintf(temp, " %02x", buf[i]);
+ temp += n;
+ }
+ dev_dbg(&client->dev, "read %d bytes at %#06x:%s\n",
+ len, addr, debug_buf);
+ }
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+static int __devinit rmi_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rmi_phys_device *rmi_phys;
+ struct rmi_i2c_data *data;
+ struct rmi_device_platform_data *pdata = client->dev.platform_data;
+ int retval;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+ dev_info(&client->dev, "Probing %s at %#02x (IRQ %d).\n",
+ pdata->sensor_name ? pdata->sensor_name : "-no name-",
+ client->addr, pdata->attn_gpio);
+
+ if (pdata->gpio_config) {
+ dev_info(&client->dev, "Configuring GPIOs.\n");
+ retval = pdata->gpio_config(pdata->gpio_data, true);
+ if (retval < 0) {
+ dev_err(&client->dev, "Failed to configure GPIOs, code: %d.\n",
+ retval);
+ return retval;
+ }
+ dev_info(&client->dev, "Done with GPIO configuration.\n");
+ }
+
+ retval = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+ if (!retval) {
+ dev_err(&client->dev, "i2c_check_functionality error %d.\n",
+ retval);
+ return retval;
+ }
+
+ rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
+ if (!rmi_phys)
+ return -ENOMEM;
+
+ data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);
+ if (!data) {
+ retval = -ENOMEM;
+ goto err_phys;
+ }
+
+ data->phys = rmi_phys;
+
+ rmi_phys->data = data;
+ rmi_phys->dev = &client->dev;
+
+ rmi_phys->write_block = rmi_i2c_write_block;
+ rmi_phys->read_block = rmi_i2c_read_block;
+ rmi_phys->info.proto = phys_proto_name;
+
+ mutex_init(&data->page_mutex);
+
+ /* Setting the page to zero will (a) make sure the PSR is in a
+ * known state, and (b) make sure we can talk to the device.
+ */
+ retval = rmi_set_page(rmi_phys, 0);
+ if (retval) {
+ dev_err(&client->dev, "Failed to set page select to 0.\n");
+ goto err_data;
+ }
+
+ retval = rmi_register_phys_device(rmi_phys);
+ if (retval) {
+ dev_err(&client->dev,
+ "failed to register physical driver at 0x%.2X.\n",
+ client->addr);
+ goto err_gpio;
+ }
+ i2c_set_clientdata(client, rmi_phys);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ retval = setup_debugfs(rmi_phys->rmi_dev, data);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEV)) {
+ retval = gpio_export(pdata->attn_gpio, false);
+ if (retval) {
+ dev_warn(&client->dev,
+ "WARNING: Failed to export ATTN gpio!\n");
+ retval = 0;
+ } else {
+ retval = gpio_export_link(&client->dev,
+ "attn", pdata->attn_gpio);
+ if (retval) {
+ dev_warn(&client->dev,
+ "WARNING: Failed to symlink ATTN gpio!\n");
+ retval = 0;
+ } else {
+ dev_info(&client->dev, "Exported ATTN GPIO %d.",
+ pdata->attn_gpio);
+ }
+ }
+ }
+
+ dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n",
+ client->addr);
+ return 0;
+
+err_gpio:
+ if (pdata->gpio_config)
+ pdata->gpio_config(pdata->gpio_data, false);
+err_data:
+ kfree(data);
+err_phys:
+ kfree(rmi_phys);
+ return retval;
+}
+
+static int __devexit rmi_i2c_remove(struct i2c_client *client)
+{
+ struct rmi_phys_device *phys = i2c_get_clientdata(client);
+ struct rmi_device_platform_data *pd = client->dev.platform_data;
+
+ /* Can I remove this disable_device */
+ /*disable_device(phys); */
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ teardown_debugfs(phys->data);
+
+ rmi_unregister_phys_device(phys);
+ kfree(phys->data);
+ kfree(phys);
+
+ if (pd->gpio_config)
+ pd->gpio_config(&pd->gpio_data, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id rmi_id[] = {
+ { "rmi_i2c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rmi_id);
+
+static struct i2c_driver rmi_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_i2c"
+ },
+ .id_table = rmi_id,
+ .probe = rmi_i2c_probe,
+ .remove = __devexit_p(rmi_i2c_remove),
+};
+
+static int __init rmi_i2c_init(void)
+{
+ return i2c_add_driver(&rmi_i2c_driver);
+}
+
+static void __exit rmi_i2c_exit(void)
+{
+ i2c_del_driver(&rmi_i2c_driver);
+}
+
+module_init(rmi_i2c_init);
+module_exit(rmi_i2c_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]>");
+MODULE_DESCRIPTION("RMI I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
As requested in the feedback from the previous patch, we've documented the
debugfs and sysfs attributes in files in Documentation/ABI/testing. There's
two files, one for debugfs and one for sysfs.
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
---
Documentation/ABI/testing/debugfs-rmi4 | 99 +++++
Documentation/ABI/testing/sysfs-rmi4 | 103 +++++
include/linux/rmi.h | 696 ++++++++++++++++++++++++++++++++
3 files changed, 898 insertions(+), 0 deletions(-)
diff --git a/Documentation/ABI/testing/debugfs-rmi4 b/Documentation/ABI/testing/debugfs-rmi4
new file mode 100644
index 0000000..cf1aa2d
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-rmi4
@@ -0,0 +1,99 @@
+What: /sys/kernel/debug/rmi/devices
+Date: October 2012
+KernelVersion: 3.x
+Contact: Christopher Heiny <[email protected]>
+Description:
+
+ The RMI4 driver implementation exposes a set of informational and control
+ parameters via debugs. These parameters are those that typically are only
+ viewed or adjusted during product development, tuning, and debug.
+ For parameters that are referenced and/or adjusted during normal operation,
+ please see sysfs-rmi4 in this directory.
+
+ General debugging parameters for a particular RMI4 sensor are found in
+ /sys/kernel/debug/rmi/sensorXX/, where XX is a the device's ID as a two
+ digit number (padded with leading zeros). Function specific parameters
+ for an RMI4 sensor are found in /sys/kernel/debug/rmi/devices/FYY/, where
+ XX is a the device's ID as a two digit number (padded with leading zeros)
+ and YY is the hexdecimal function number (for example, F11 for RMI function
+ F11).
+
+ For RMI4 functions that support multiple sensor instances (such as F11),
+ the parameters for individual sensors have .Z appended to them, where Z is
+ the index of the sensor instance (for example, clip.0, clip.1, clip.2, and
+ so on).
+
+ Some of the parameters exposed here are described in detail in the
+ RMI4 Specification, which is found here:
+ http://www.synaptics.com/sites/default/files/511-000136-01_revD.pdf
+ For such parameters, we'll reference you to that document, rather than
+ copying the contents here.
+
+ /sys/kernel/debug/rmi/
+ /sensorXX/
+ attn_count - (ro) Shows the number of ATTN interrupts handled so far.
+ comms_debug - (rw) Write 1 to this dump information about register
+ reads and writes to the console. Write 0 to this to turn
+ this feature off. WARNING: Imposes a major performance
+ penalty when turned on.
+ irq_debug - (rw) Write 1 to this dump information about interrupts
+ to the console. Write 0 to this to turn this feature off.
+ WARNIG: Imposes a major performance penalty when turned on.
+ phys - (ro) Presents information about the physical connection of
+ this device. It has one line, with the format:
+
+ prot tx_count tx_bytes tx_errors rx_count rx_bytes rx_errors attn
+
+ Where
+ prot is one of i2c, spi1, or spi2
+ tx_count is the number of write operations
+ tx_bytes is the number of bytes written
+ tx_errors is the number of write operations that encountered errors
+ rx_count is the number of read operations
+ rx_bytes is the total number of bytes read
+ rx_errors is the number of read operations that encountered errors
+
+ All counts are 64-bit unsigned values, and are set to zero
+ when the physical layer driver is initialized.
+
+ /sensorXX/F01/
+ interrupt_enable - (rw) allows you to read or modify the F01
+ interrupt enable mask (the F01_RMI_Ctrl1 register(s)).
+
+ /sensorXX/F11/
+ clip.Z - (rw) Controls in-driver coordinate clipping for the 2D
+ sensor Z. This is a set of four unsigned values in the
+ range [0..65535], representing the lower bounds on X, the
+ upper bounds on X, the lower bounds on Y, and the upper
+ bounds on Y. Coordinates will be clipped to these ranges.
+ If enabled, clip is the final transformation to be applied
+ to the coordinates. The default upper and lower bounds for
+ clip are 0 and 65535 respectively for both axes.
+ delta_threshold.Z - (rw) Controls the F11 distance thresholds. This
+ contains two values, corresponding to F11_2D_Ctrl2 and
+ F11_2D_Ctrl3. Se the spec for more details.
+ flip.Z - (rw) This parameter is a pair of single binary digits (for
+ example, "0 0" or "1 0"), corresponding to the X and Y axis.
+ Writing a 1 for a particular axis will invert the coordinates
+ reported by the device along that axis. For example writing
+ "0 1" to this parameter will flip the Y axis top to bottom,
+ but leave the X axis unchanged. If enabled, flip is applied
+ after swap and before offset.
+ offset.Z - (rw) This is a pair of values that will be SUBTRACTED
+ from the X and Y coordinates, respectively. If non-zero,
+ offset will be applied after flip and before clip. The
+ default value for offset is 0 for both X and Y.
+ rezero_wait - (rw) If non-zero, F11 will wait this many milliseconds
+ after exiting suspend mode before recalibrating the sensor(s).
+ This is useful in systems were there may be unusual
+ electrical conditions during the resume process, allowing
+ you to delay recalibration until the electrical environment
+ has stabilized.
+ swap.Z - (rw) Writing 1 to this parameter swaps the X and Y axis as
+ reported by the sensor instance Z, rotating the reported
+ coordinates by 90 degrees. This can be useful when
+ installing a landscape sensor over a portrait display, for
+ example. The default state for this parameter is 0. If
+ enabled, swap is applied before any other transformation.
+ type_a - (rw) Most RMI4 F11 implementations support MT-B reporting.
+ You can write 1 to this parameter to force MT-A reporting.
diff --git a/Documentation/ABI/testing/sysfs-rmi4 b/Documentation/ABI/testing/sysfs-rmi4
new file mode 100644
index 0000000..3354d10
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-rmi4
@@ -0,0 +1,103 @@
+What: /sys/bus/rmi/devices
+Date: October 2012
+KernelVersion: 3.x
+Contact: Christopher Heiny <[email protected]>
+Description:
+
+ The RMI4 driver implementation exposes a set of informational and control
+ parameters via sysfs. These parameters are those that typically will be
+ referenced and/or adjusted during normal operation of products containing
+ RMI sensors. For paramters that are only viewed or adjusted during product
+ development and debug, please see debugfs-rmi4 in this directory.
+
+ General parameters for a particular RMI4 sensor are found in
+ /sys/bus/rmi/devices/sensorXX/, where XX is a the device's ID as a two
+ digit number (padded with leading zeros). Function specific parameters
+ for an RMI4 sensor are found in /sys/bus/rmi/devices/sensorXX.fnYY/, where
+ XX is a the device's ID as a two digit number (padded with leading zeros)
+ and YY is the hexdecimal function number (for example, fn11 for RMI function
+ F11).
+
+ Many of the parameters exposed here are described in detail in the
+ RMI4 Specification, which is found here:
+ http://www.synaptics.com/sites/default/files/511-000136-01_revD.pdf
+ For such parameters, we'll reference you to that document, rather than
+ copying the contents here.
+
+ /sys/bus/rmi/devices
+ /sensorXX/
+ bsr ... (rw) bus select register, if supported (see spec)
+ enabled ... (rw) enable/disable interrupt management [deprecated]
+
+ /sensor00.fn01/
+ chargerinput ... (rw) User space programs can use this to tell the
+ sensor that the system is plugged into an external power
+ source (as opposed to running on batteries). This allows
+ the sensor firmware to make necessary adjustments for the
+ current capacitence regime. Write 1 to this when the
+ system is using external power, write 0 to this when the
+ system is running on batteries. See spec for full details.
+ configured ... (ro) Shows the current state of the configured bit.
+ This will be 1 most of the time (indicating the device has
+ been appropriately configured), but will switch to 0 briefly
+ if the sensor experiences a firmware or ASIC reset event.
+ See spec for full details.
+ datecode ... (ro) The date on which the module was manufactured.
+ See spec for full details.
+ doze_holdoff ... (rw) Controls how long the sensor will wait before
+ entering the doze state when no fingers are present on the
+ device. The time is in terms of 10 milliseconds - a
+ doze_holdoff value of 3 corresponds to a time period of 30
+ milliseconds. See spec for full details.
+ flashprog ... (ro) Defines the current device operating mode. The
+ flashprog flag is set if the normal operation of the device
+ is suspended because the device is in a flash programming
+ enabled state. See spec for full details.
+ interrupt_enable ... (ro) This represents the current RMI4 interrupt
+ mask (F01_RMI_Ctrl1 registers). See spec for full details.
+ manufacturer ... (ro) This is the identity of the manufacturer of
+ the device, as obtained from F01_RMI_Query0. See spec for
+ full details.
+ nosleep ... (rw) Writing 1 to this parameter disables all normal
+ firmware powersaving behaviors and forces the device to run
+ at full power without sleeping. See spec for full details.
+ productid ... (ro) The product info bytes, as determined from
+ F01_RMI_Query2 and F01_RMI_Query3 registers. See spec for
+ full details.
+ productinfo ... (ro) A string of up to 10 characters, identifying
+ the product. See spec for full details.
+ reportrate ... (rw) This is the current value of the RMI4 ReportRate
+ bit (F01_RMI_Ctrl0, bit 6). The meaning of this bit is very
+ much device-dependent. Please see both the RMI4 spec and the
+ sensor spec sheet for details.
+ reset ... (wo) Writing a 1 to this write only register forces the
+ device to reset.
+ sleepmode ... (rw) Controls power management on the device. Writing
+ 0 to this parameter puts the device into its normal operating
+ mode. Writing 1 to this parameter fully disables touch
+ sensors and similar inputs - no touch data will be reported
+ from the device in this mode. Writing 2 or 3 to this device
+ may or may not have an effect, depending on the particular
+ device - see the product specification for your sensor for
+ details.
+ statuscode ... (ro) Reports the most recent device status, such as
+ invalid configuration, device reset, CRC failure, and so on.
+ Please se the RMI4 specification for details.
+ unconfigured ... (ro) This is the opposite of the configured bit,
+ described above.
+ wakeup_threshold ... (rw) This controls the change in capacitive
+ signal needed to wake the device from the doze state. Please
+ see the RMI4 specification for the F01_RMI_Ctrl3 register
+ for more details.
+
+ /sensor00.fn11/
+ abs_pos_filt ... (rw) Enables or disables the absolute position
+ filter feature. See spec for full details.
+ maxPos ... (rw) Adjusts the maximum X and Y position control
+ registers (F11_2D_Ctrl6 through F11_2D_Ctrl9). See spec for
+ full details.
+ relreport ... (rw) Enables or disabled relative finger motion
+ reporting. See spec for full details.
+ rezero ... (wo) Force recalibration of the F11 2D sensor(s). See
+ spec for full details.
+
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
new file mode 100644
index 0000000..1bdedc0
--- /dev/null
+++ b/include/linux/rmi.h
@@ -0,0 +1,696 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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.
+ */
+
+#ifndef _RMI_H
+#define _RMI_H
+#include <linux/kernel.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/stat.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#endif
+
+extern struct bus_type rmi_bus_type;
+
+extern struct device_type rmi_function_type;
+extern struct device_type rmi_sensor_type;
+
+
+/* Permissions for sysfs attributes. Since the permissions policy will change
+ * on a global basis in the future, rather than edit all sysfs attrs everywhere
+ * in the driver (and risk screwing that up in the process), we use this handy
+ * set of #defines. That way when we change the policy for sysfs permissions,
+ * we only need to change them here.
+ */
+#define RMI_RO_ATTR S_IRUGO
+#define RMI_RW_ATTR (S_IRUGO | S_IWUGO)
+#define RMI_WO_ATTR S_IWUGO
+
+enum rmi_attn_polarity {
+ RMI_ATTN_ACTIVE_LOW = 0,
+ RMI_ATTN_ACTIVE_HIGH = 1
+};
+
+/**
+ * struct rmi_f11_axis_alignment - target axis alignment
+ * @swap_axes: set to TRUE if desired to swap x- and y-axis
+ * @flip_x: set to TRUE if desired to flip direction on x-axis
+ * @flip_y: set to TRUE if desired to flip direction on y-axis
+ * @clip_X_low - reported X coordinates below this setting will be clipped to
+ * the specified value
+ * @clip_X_high - reported X coordinates above this setting will be clipped to
+ * the specified value
+ * @clip_Y_low - reported Y coordinates below this setting will be clipped to
+ * the specified value
+ * @clip_Y_high - reported Y coordinates above this setting will be clipped to
+ * the specified value
+ * @offset_X - this value will be added to all reported X coordinates
+ * @offset_Y - this value will be added to all reported Y coordinates
+ * @rel_report_enabled - if set to true, the relative reporting will be
+ * automatically enabled for this sensor.
+ */
+struct rmi_f11_2d_axis_alignment {
+ bool swap_axes;
+ bool flip_x;
+ bool flip_y;
+ int clip_X_low;
+ int clip_Y_low;
+ int clip_X_high;
+ int clip_Y_high;
+ int offset_X;
+ int offset_Y;
+ int rel_report_enabled;
+ u8 delta_x_threshold;
+ u8 delta_y_threshold;
+};
+
+/**
+ * struct virtualbutton_map - describes rectangular areas of a 2D sensor that
+ * will be used by the driver to generate button events.
+ *
+ * @x - the x position of the low order corner of the rectangle, in RMI4
+ * position units.
+ * @y - the y position of the low order corner of the rectangle, in RMI4
+ * position units.
+ * @width - the width of the rectangle, in RMI4 position units.
+ * @height - the height of the rectangle, in RMI4 position units.
+ * @code - the input subsystem key event code that will be generated when a
+ * tap occurs within the rectangle.
+ */
+struct virtualbutton_map {
+ u16 x;
+ u16 y;
+ u16 width;
+ u16 height;
+ u16 code;
+};
+
+/**
+ * struct rmi_f11_virtualbutton_map - provides a list of virtual buttons for
+ * a 2D sensor.
+ *
+ * @buttons - the number of entries in the map.
+ * @map - an array of virtual button descriptions.
+ */
+struct rmi_f11_virtualbutton_map {
+ u8 buttons;
+ struct virtualbutton_map *map;
+};
+
+/**
+ * struct rmi_f11_sensor_data - overrides defaults for a single F11 2D sensor.
+ * @axis_align - provides axis alignment overrides (see above).
+ * @virtual_buttons - describes areas of the touch sensor that will be treated
+ * as buttons.
+ * @type_a - all modern RMI F11 firmwares implement Multifinger Type B
+ * protocol. Set this to true to force MF Type A behavior, in case you find
+ * an older sensor.
+ */
+struct rmi_f11_sensor_data {
+ struct rmi_f11_2d_axis_alignment axis_align;
+ struct rmi_f11_virtualbutton_map virtual_buttons;
+ bool type_a;
+};
+
+/**
+ * struct rmi_f01_power - override default power management settings.
+ *
+ */
+enum rmi_f01_nosleep {
+ RMI_F01_NOSLEEP_DEFAULT = 0,
+ RMI_F01_NOSLEEP_OFF = 1,
+ RMI_F01_NOSLEEP_ON = 2
+};
+
+/**
+ * struct rmi_f01_power_management -When non-zero, these values will be written
+ * to the touch sensor to override the default firmware settigns. For a
+ * detailed explanation of what each field does, see the corresponding
+ * documention in the RMI4 specification.
+ *
+ * @nosleep - specifies whether the device is permitted to sleep or doze (that
+ * is, enter a temporary low power state) when no fingers are touching the
+ * sensor.
+ * @wakeup_threshold - controls the capacitance threshold at which the touch
+ * sensor will decide to wake up from that low power state.
+ * @doze_holdoff - controls how long the touch sensor waits after the last
+ * finger lifts before entering the doze state, in units of 100ms.
+ * @doze_interval - controls the interval between checks for finger presence
+ * when the touch sensor is in doze mode, in units of 10ms.
+ */
+struct rmi_f01_power_management {
+ enum rmi_f01_nosleep nosleep;
+ u8 wakeup_threshold;
+ u8 doze_holdoff;
+ u8 doze_interval;
+};
+
+/**
+ * struct rmi_button_map - used to specify the initial input subsystem key
+ * event codes to be generated by buttons (or button like entities) on the
+ * touch sensor.
+ * @nbuttons - length of the button map.
+ * @map - the key event codes for the corresponding buttons on the touch
+ * sensor.
+ */
+struct rmi_button_map {
+ u8 nbuttons;
+ u8 *map;
+};
+
+struct rmi_f30_gpioled_map {
+ u8 ngpioleds;
+ u8 *map;
+};
+
+/**
+ * struct rmi_device_platform_data_spi - provides paramters used in SPI
+ * communitcations. All Synaptics SPI products support a standard SPI
+ * interface; some also support what is called SPI V2 mode, depending on
+ * firmware and/or ASIC limitations. In V2 mode, the touch sensor can
+ * support shorter delays during certain operations, and these are specified
+ * separately from the standard mode delays.
+ *
+ * @block_delay - for standard SPI transactions consisting of both a read and
+ * write operation, the delay (in microseconds) between the read and write
+ * operations.
+ * @split_read_block_delay_us - for V2 SPI transactions consisting of both a
+ * read and write operation, the delay (in microseconds) between the read and
+ * write operations.
+ * @read_delay_us - the delay between each byte of a read operation in normal
+ * SPI mode.
+ * @write_delay_us - the delay between each byte of a write operation in normal
+ * SPI mode.
+ * @split_read_byte_delay_us - the delay between each byte of a read operation
+ * in V2 mode.
+ * @pre_delay_us - the delay before the start of a SPI transaction. This is
+ * typically useful in conjunction with custom chip select assertions (see
+ * below).
+ * @post_delay_us - the delay after the completion of an SPI transaction. This is
+ * typically useful in conjunction with custom chip select assertions (see
+ * below).
+ * @cs_assert - For systems where the SPI subsystem does not control the CS/SSB
+ * line, or where such control is broken, you can provide a custom routine to
+ * handle a GPIO as CS/SSB. This routine will be called at the beginning and
+ * end of each SPI transaction. The RMI SPI implementation will wait
+ * pre_delay_us after this routine returns before starting the SPI transfer;
+ * and post_delay_us after completion of the SPI transfer(s) before calling it
+ * with assert==FALSE.
+ */
+struct rmi_device_platform_data_spi {
+ int block_delay_us;
+ int split_read_block_delay_us;
+ int read_delay_us;
+ int write_delay_us;
+ int split_read_byte_delay_us;
+ int pre_delay_us;
+ int post_delay_us;
+
+ void *cs_assert_data;
+ int (*cs_assert) (const void *cs_assert_data, const bool assert);
+};
+
+/**
+ * struct rmi_device_platform_data - system specific configuration info.
+ *
+ * @driver_name
+ * @sensor_name - this is used for various diagnostic messages.
+ *
+ * @firmware_name - if specified will override default firmware name,
+ * for reflashing.
+ *
+ * @attn_gpio - the index of a GPIO that will be used to provide the ATTN
+ * interrupt from the touch sensor.
+ * @attn_polarity - indicates whether ATTN is active high or low.
+ * @level_triggered - by default, the driver uses edge triggered interrupts.
+ * However, this can cause problems with suspend/resume on some platforms. In
+ * that case, set this to 1 to use level triggered interrupts.
+ * @gpio_config - a routine that will be called when the driver is loaded to
+ * perform any platform specific GPIO configuration, and when it is unloaded
+ * for GPIO de-configuration. This is typically used to configure the ATTN
+ * GPIO and the I2C or SPI pins, if necessary.
+ * @gpio_data - platform specific data to be passed to the GPIO configuration
+ * function.
+ *
+ * @reset_delay_ms - after issuing a reset command to the touch sensor, the
+ * driver waits a few milliseconds to give the firmware a chance to
+ * to re-initialize. You can override the default wait period here.
+ *
+ * @spi_data - override default settings for SPI delays and SSB management (see
+ * above).
+ *
+ * @f11_sensor_data - an array of platform data for individual F11 2D sensors.
+ * @f11_sensor_count - the length of f11_sensor_data array. Extra entries will
+ * be ignored; if there are too few entries, all settings for the additional
+ * sensors will be defaulted.
+ * @f11_rezero_wait - if non-zero, this is how may milliseconds the F11 2D
+ * sensor(s) will wait before being be rezeroed on exit from suspend. If
+ * this value is zero, the F11 2D sensor(s) will not be rezeroed on resume.
+ * @pre_suspend - this will be called before any other suspend operations are
+ * done.
+ * @power_management - overrides default touch sensor doze mode settings (see
+ * above)
+ * @f19_button_map - provide initial input subsystem key mappings for F19.
+ * @f1a_button_map - provide initial input subsystem key mappings for F1A.
+ * @gpioled_map - provides initial settings for GPIOs and LEDs controlled by
+ * F30.
+ * @f41_button_map - provide initial input subsystem key mappings for F41.
+ *
+ * @post_suspend - this will be called after all suspend operations are
+ * completed. This is the ONLY safe place to power off an RMI sensor
+ * during the suspend process.
+ * @pre_resume - this is called before any other resume operations. If you
+ * powered off the RMI4 sensor in post_suspend(), then you MUST power it back
+ * here, and you MUST wait an appropriate time for the ASIC to come up
+ * (100ms to 200ms, depending on the sensor) before returning.
+ * @pm_data - this will be passed to the various (pre|post)_(suspend/resume)
+ * functions.
+ */
+struct rmi_device_platform_data {
+ char *driver_name;
+ char *sensor_name; /* Used for diagnostics. */
+
+ int attn_gpio;
+ enum rmi_attn_polarity attn_polarity;
+ bool level_triggered;
+ void *gpio_data;
+ int (*gpio_config)(void *gpio_data, bool configure);
+
+ int reset_delay_ms;
+
+ struct rmi_device_platform_data_spi spi_data;
+
+ /* function handler pdata */
+ struct rmi_f11_sensor_data *f11_sensor_data;
+ u8 f11_sensor_count;
+ u16 f11_rezero_wait;
+ struct rmi_f01_power_management power_management;
+ struct rmi_button_map *f19_button_map;
+ struct rmi_button_map *f1a_button_map;
+ struct rmi_f30_gpioled_map *gpioled_map;
+ struct rmi_button_map *f41_button_map;
+
+#ifdef CONFIG_RMI4_FWLIB
+ char *firmware_name;
+#endif
+
+#ifdef CONFIG_PM
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_suspend) (const void *pm_data);
+ int (*pre_resume) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+};
+
+/**
+ * struct rmi_function_descriptor - RMI function base addresses
+ *
+ * @query_base_addr: The RMI Query base address
+ * @command_base_addr: The RMI Command base address
+ * @control_base_addr: The RMI Control base address
+ * @data_base_addr: The RMI Data base address
+ * @interrupt_source_count: The number of irqs this RMI function needs
+ * @function_number: The RMI function number
+ *
+ * This struct is used when iterating the Page Description Table. The addresses
+ * are 16-bit values to include the current page address.
+ *
+ */
+struct rmi_function_descriptor {
+ u16 query_base_addr;
+ u16 command_base_addr;
+ u16 control_base_addr;
+ u16 data_base_addr;
+ u8 interrupt_source_count;
+ u8 function_number;
+ u8 function_version;
+};
+
+struct rmi_function_container;
+struct rmi_device;
+
+/**
+ * struct rmi_function_handler - driver routines for a particular RMI function.
+ *
+ * This struct describes the interface of an RMI function. These are
+ * registered to the bus using the rmi_register_function_driver() call.
+ *
+ * @func: The RMI function number
+ * @reset: Called when a reset of the touch sensor is detected. The routine
+ * should perform any out-of-the-ordinary reset handling that might be
+ * necessary. Restoring of touch sensor configuration registers should be
+ * handled in the config() callback, below.
+ * @config: Called when the function container is first initialized, and
+ * after a reset is detected. This routine should write any necessary
+ * configuration settings to the device.
+ * @attention: Called when the IRQ(s) for the function are set by the touch
+ * sensor.
+ * @suspend: Should perform any required operations to suspend the particular
+ * function.
+ * @resume: Should perform any required operations to resume the particular
+ * function.
+ *
+ * All callbacks are expected to return 0 on success, error code on failure.
+ */
+struct rmi_function_handler {
+ struct device_driver driver;
+
+ u8 func;
+ int (*config)(struct rmi_function_container *fc);
+ int (*reset)(struct rmi_function_container *fc);
+ int (*attention)(struct rmi_function_container *fc, u8 *irq_bits);
+#ifdef CONFIG_PM
+ int (*suspend)(struct rmi_function_container *fc);
+ int (*resume)(struct rmi_function_container *fc);
+#endif
+};
+
+#define to_rmi_function_handler(d) \
+ container_of(d, struct rmi_function_handler, driver);
+
+/**
+ * struct rmi_function_container - represents the implementation of an RMI4
+ * function for a particular device (basically, a driver for that RMI4 function)
+ *
+ * @fd: The function descriptor of the RMI function
+ * @rmi_dev: Pointer to the RMI device associated with this function container
+ * @dev: The device associated with this particular function.
+ *
+ * @num_of_irqs: The number of irqs needed by this function
+ * @irq_pos: The position in the irq bitfield this function holds
+ * @irq_mask: For convience, can be used to mask IRQ bits off during ATTN
+ * interrupt handling.
+ * @data: Private data pointer
+ *
+ * @list: Used to create a list of function containers.
+ * @debugfs_root: used during debugging
+ *
+ */
+struct rmi_function_container {
+
+ struct rmi_function_descriptor fd;
+ struct rmi_device *rmi_dev;
+ struct device dev;
+
+ int num_of_irqs;
+ int irq_pos;
+ u8 *irq_mask;
+
+ void *data;
+
+ struct list_head list;
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_root;
+#endif
+};
+
+#define to_rmi_function_container(d) \
+ container_of(d, struct rmi_function_container, dev);
+
+/**
+ * struct rmi_driver - driver for an RMI4 sensor on the RMI bus.
+ *
+ * @driver: Device driver model driver
+ * @irq_handler: Callback for handling irqs
+ * @reset_handler: Called when a reset is detected.
+ * @get_func_irq_mask: Callback for calculating interrupt mask
+ * @store_irq_mask: Callback for storing and replacing interrupt mask
+ * @restore_irq_mask: Callback for restoring previously stored interrupt mask
+ * @data: Private data pointer
+ *
+ */
+struct rmi_driver {
+ struct device_driver driver;
+
+ int (*irq_handler)(struct rmi_device *rmi_dev, int irq);
+ int (*reset_handler)(struct rmi_device *rmi_dev);
+ u8* (*get_func_irq_mask)(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc);
+ int (*store_irq_mask)(struct rmi_device *rmi_dev, u8* new_interupts);
+ int (*restore_irq_mask)(struct rmi_device *rmi_dev);
+ void *data;
+};
+
+#define to_rmi_driver(d) \
+ container_of(d, struct rmi_driver, driver);
+
+/** struct rmi_phys_info - diagnostic information about the RMI physical
+ * device, used in the phys debugfs file.
+ *
+ * @proto String indicating the protocol being used.
+ * @tx_count Number of transmit operations.
+ * @tx_bytes Number of bytes transmitted.
+ * @tx_errs Number of errors encountered during transmit operations.
+ * @rx_count Number of receive operations.
+ * @rx_bytes Number of bytes received.
+ * @rx_errs Number of errors encountered during receive operations.
+ * @att_count Number of times ATTN assertions have been handled.
+ */
+struct rmi_phys_info {
+ char *proto;
+ long tx_count;
+ long tx_bytes;
+ long tx_errs;
+ long rx_count;
+ long rx_bytes;
+ long rx_errs;
+};
+
+/**
+ * struct rmi_phys_device - represent an RMI physical device
+ *
+ * @dev: Pointer to the communication device, e.g. i2c or spi
+ * @rmi_dev: Pointer to the RMI device
+ * @write_block: Writing a block of data to the specified address
+ * @read_block: Read a block of data from the specified address.
+ * @irq_thread: if not NULL, the sensor driver will use this instead of the
+ * default irq_thread implementation.
+ * @hard_irq: if not NULL, the sensor driver will use this for the hard IRQ
+ * handling
+ * @data: Private data pointer
+ *
+ * The RMI physical device implements the glue between different communication
+ * buses such as I2C and SPI.
+ *
+ */
+struct rmi_phys_device {
+ struct device *dev;
+ struct rmi_device *rmi_dev;
+
+ int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len);
+ int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len);
+
+ int (*enable_device) (struct rmi_phys_device *phys);
+ void (*disable_device) (struct rmi_phys_device *phys);
+
+ irqreturn_t (*irq_thread)(int irq, void *p);
+ irqreturn_t (*hard_irq)(int irq, void *p);
+
+
+ void *data;
+
+ struct rmi_phys_info info;
+};
+
+/**
+ * struct rmi_device - represents an RMI4 sensor device on the RMI bus.
+ *
+ * @dev: The device created for the RMI bus
+ * @number: Unique number for the device on the bus.
+ * @driver: Pointer to associated driver
+ * @phys: Pointer to the physical interface
+ * @debugfs_root: base for this particular sensor device.
+ *
+ */
+struct rmi_device {
+ struct device dev;
+ int number;
+
+ struct rmi_driver *driver;
+ struct rmi_phys_device *phys;
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_root;
+#endif
+};
+
+#define to_rmi_device(d) container_of(d, struct rmi_device, dev);
+#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data);
+
+/**
+ * rmi_read - read a single byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to read from
+ * @buf: The read buffer
+ *
+ * Reads a byte of data using the underlaying physical protocol in to buf. It
+ * returns zero or a negative error code.
+ */
+static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf)
+{
+ return d->phys->read_block(d->phys, addr, buf, 1);
+}
+
+/**
+ * rmi_read_block - read a block of bytes
+ * @d: Pointer to an RMI device
+ * @addr: The start address to read from
+ * @buf: The read buffer
+ * @len: Length of the read buffer
+ *
+ * Reads a block of byte data using the underlaying physical protocol in to buf.
+ * It returns the amount of bytes read or a negative error code.
+ */
+static inline int rmi_read_block(struct rmi_device *d, u16 addr, u8 *buf,
+ int len)
+{
+ return d->phys->read_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_write - write a single byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to write to
+ * @data: The data to write
+ *
+ * Writes a byte from buf using the underlaying physical protocol. It
+ * returns zero or a negative error code.
+ */
+static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data)
+{
+ return d->phys->write_block(d->phys, addr, &data, 1);
+}
+
+/**
+ * rmi_write_block - write a block of bytes
+ * @d: Pointer to an RMI device
+ * @addr: The start address to write to
+ * @buf: The write buffer
+ * @len: Length of the write buffer
+ *
+ * Writes a block of byte data from buf using the underlaying physical protocol.
+ * It returns the amount of bytes written or a negative error code.
+ */
+static inline int rmi_write_block(struct rmi_device *d, u16 addr, u8 *buf,
+ int len)
+{
+ return d->phys->write_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_register_phys_device - register a physical device connection on the RMI
+ * bus. Physical drivers provide communication from the devices on the bus to
+ * the RMI4 sensor on a bus such as SPI, I2C, and so on.
+ *
+ * @phys: the physical device to register
+ */
+int rmi_register_phys_device(struct rmi_phys_device *phys);
+
+/**
+ * rmi_unregister_phys_device - unregister a physical device connection
+ * @phys: the physical driver to unregister
+ *
+ */
+void rmi_unregister_phys_device(struct rmi_phys_device *phys);
+
+
+/**
+ * rmi_for_each_dev - provides a way for other parts of the system to enumerate
+ * the devices on the RMI bus.
+ *
+ * @data - will be passed into the callback function.
+ * @func - will be called for each device.
+ */
+int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
+
+
+/**
+ * Helper fn to convert a byte array representing a 16 bit value in the RMI
+ * endian-ness to a 16-bit value in the native processor's specific endianness.
+ * We don't use ntohs/htons here because, well, we're not dealing with
+ * a pair of 16 bit values. Casting dest to u16* wouldn't work, because
+ * that would imply knowing the byte order of u16 in the first place. The
+ * same applies for using shifts and masks.
+ */
+static inline u16 batohs(u8 *src)
+{
+ return src[1] * 0x100 + src[0];
+}
+
+/**
+ * Helper function to convert a 16 bit value (in host processor endianess) to
+ * a byte array in the RMI endianess for u16s. See above comment for
+ * why we dont us htons or something like that.
+ */
+static inline void hstoba(u8 *dest, u16 src)
+{
+ dest[0] = src % 0x100;
+ dest[1] = src / 0x100;
+}
+
+#ifdef CONFIG_RMI4_DEBUG
+/**
+ * Utility routine to handle writes to read-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we don't
+ * want to accept it quietly (which is what can happen if you just put NULL
+ * for the attribute's store function).
+ */
+static inline ssize_t rmi_store_error(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev_warn(dev,
+ "WARNING: Attempt to write %d characters to read-only attribute %s.",
+ count, attr->attr.name);
+ return -EPERM;
+}
+
+/**
+ * Utility routine to handle reads of write-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we don't
+ * want to accept it quietly (which is what can happen if you just put NULL
+ * for the attribute's show function).
+ */
+static inline ssize_t rmi_show_error(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ dev_warn(dev,
+ "WARNING: Attempt to read from write-only attribute %s.",
+ attr->attr.name);
+ return -EPERM;
+}
+#else
+#define rmi_store_error NULL
+#define rmi_show_error NULL
+#endif
+
+#endif
RMI Function 01 implements basic device control and power management
behaviors for the RMI4 sensor. Since the last patch, we've decoupled rmi_f01.c
implementation from rmi_driver.c, so rmi_f01.c acts as a standard driver
module to handle F01 devices on the RMI bus.
Like other modules, a number of attributes have been moved from sysfs to
debugfs, depending on their expected use.
rmi_f01.h exports definitions that we expect to be used by other functionality
in the future (such as firmware reflash).
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
---
drivers/input/rmi4/rmi_f01.c | 1463 ++++++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/rmi_f01.h | 136 ++++
2 files changed, 1599 insertions(+), 0 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
new file mode 100644
index 0000000..d734f46
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -0,0 +1,1463 @@
+/*
+ * Copyright (c) 2011-2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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 <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/kconfig.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+/**
+ * @reset - set this bit to force a firmware reset of the sensor.
+ */
+union f01_device_commands {
+ struct {
+ bool reset:1;
+ u8 reserved:7;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @ctrl0 - see documentation in rmi_f01.h.
+ * @interrupt_enable - A mask of per-function interrupts on the touch sensor.
+ * @doze_interval - controls the interval between checks for finger presence
+ * when the touch sensor is in doze mode, in units of 10ms.
+ * @wakeup_threshold - controls the capacitance threshold at which the touch
+ * sensor will decide to wake up from that low power state.
+ * @doze_holdoff - controls how long the touch sensor waits after the last
+ * finger lifts before entering the doze state, in units of 100ms.
+ */
+struct f01_device_control {
+ union f01_device_control_0 ctrl0;
+ u8 *interrupt_enable;
+ u8 doze_interval;
+ u8 wakeup_threshold;
+ u8 doze_holdoff;
+};
+
+/**
+ * @has_ds4_queries - if true, the query registers relating to Design Studio 4
+ * features are present.
+ * @has_multi_phy - if true, multiple physical communications interfaces are
+ * supported.
+ * @has_guest - if true, a "guest" device is supported.
+ */
+union f01_query_42 {
+ struct {
+ bool has_ds4_queries:1;
+ bool has_multi_phy:1;
+ bool has_guest:1;
+ u8 reserved:5;
+ } __attribute__((__packed__));
+ u8 regs[1];
+};
+
+/**
+ * @length - the length of the remaining Query43.* register block, not
+ * including the first register.
+ * @has_package_id_query - the package ID query data will be accessible from
+ * inside the ProductID query registers.
+ * @has_packrat_query - the packrat query data will be accessible from inside
+ * the ProductID query registers.
+ * @has_reset_query - the reset pin related registers are valid.
+ * @has_maskrev_query - the silicon mask revision number will be reported.
+ * @has_i2c_control - the register F01_RMI_Ctrl6 will exist.
+ * @has_spi_control - the register F01_RMI_Ctrl7 will exist.
+ * @has_attn_control - the register F01_RMI_Ctrl8 will exist.
+ * @reset_enabled - the hardware reset pin functionality has been enabled
+ * for this device.
+ * @reset_polarity - If this bit reports as ‘0’, it means that the reset state
+ * is active low. A ‘1’ means that the reset state is active high.
+ * @pullup_enabled - If set, it indicates that a built-in weak pull up has
+ * been enabled on the Reset pin; clear means that no pull-up is present.
+ * @reset_pin_number - This field represents which GPIO pin number has been
+ * assigned the reset functionality.
+ */
+union f01_ds4_queries {
+ struct {
+ u8 length:4;
+ u8 reserved_1:4;
+
+ bool has_package_id_query:1;
+ bool has_packrat_query:1;
+ bool has_reset_query:1;
+ bool has_maskrev_query:1;
+ u8 reserved_2:4;
+
+ bool has_i2c_control:1;
+ bool has_spi_control:1;
+ bool has_attn_control:1;
+ u8 reserved_3:5;
+
+ bool reset_enabled:1;
+ bool reset_polarity:1;
+ bool pullup_enabled:1;
+ u8 reserved_4:1;
+ u8 reset_pin_number:4;
+ } __attribute__((__packed__));
+ u8 regs[4];
+};
+
+struct f01_data {
+ struct f01_device_control device_control;
+ union f01_basic_queries basic_queries;
+ union f01_device_status device_status;
+ u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+
+ u16 interrupt_enable_addr;
+ u16 doze_interval_addr;
+ u16 wakeup_threshold_addr;
+ u16 doze_holdoff_addr;
+
+ int irq_count;
+ int num_of_irq_regs;
+
+#ifdef CONFIG_PM
+ bool suspended;
+ bool old_nosleep;
+#endif
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_interrupt_enable;
+#endif
+};
+
+#ifdef CONFIG_RMI4_DEBUG
+struct f01_debugfs_data {
+ bool done;
+ struct rmi_function_container *fc;
+};
+
+static int f01_debug_open(struct inode *inodep, struct file *filp)
+{
+ struct f01_debugfs_data *data;
+ struct rmi_function_container *fc = inodep->i_private;
+
+ data = devm_kzalloc(&fc->dev, sizeof(struct f01_debugfs_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->fc = fc;
+ filp->private_data = data;
+ return 0;
+}
+
+static ssize_t interrupt_enable_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int i;
+ int len;
+ int total_len = 0;
+ char local_buf[size];
+ char *current_buf = local_buf;
+ struct f01_debugfs_data *data = filp->private_data;
+ struct f01_data *f01 = data->fc->data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ /* loop through each irq value and copy its
+ * string representation into buf */
+ for (i = 0; i < f01->irq_count; i++) {
+ int irq_reg;
+ int irq_shift;
+ int interrupt_enable;
+
+ irq_reg = i / 8;
+ irq_shift = i % 8;
+ interrupt_enable =
+ ((f01->device_control.interrupt_enable[irq_reg]
+ >> irq_shift) & 0x01);
+
+ /* get next irq value and write it to buf */
+ len = snprintf(current_buf, size - total_len,
+ "%u ", interrupt_enable);
+ /* bump up ptr to next location in buf if the
+ * snprintf was valid. Otherwise issue an error
+ * and return. */
+ if (len > 0) {
+ current_buf += len;
+ total_len += len;
+ } else {
+ dev_err(&data->fc->dev, "Failed to build interrupt_enable buffer, code = %d.\n",
+ len);
+ return snprintf(local_buf, size, "unknown\n");
+ }
+ }
+ len = snprintf(current_buf, size - total_len, "\n");
+ if (len > 0)
+ total_len += len;
+ else
+ dev_warn(&data->fc->dev, "%s: Failed to append carriage return.\n",
+ __func__);
+
+ if (copy_to_user(buffer, local_buf, total_len))
+ return -EFAULT;
+
+ return total_len;
+}
+
+static ssize_t interrupt_enable_write(struct file *filp,
+ const char __user *buffer, size_t size, loff_t *offset) {
+ int retval;
+ char buf[size];
+ char *local_buf = buf;
+ int i;
+ int irq_count = 0;
+ int irq_reg = 0;
+ struct f01_debugfs_data *data = filp->private_data;
+ struct f01_data *f01 = data->fc->data;
+
+ retval = copy_from_user(buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ for (i = 0; i < f01->irq_count && *local_buf != 0;
+ i++, local_buf += 2) {
+ int irq_shift;
+ int interrupt_enable;
+ int result;
+
+ irq_reg = i / 8;
+ irq_shift = i % 8;
+
+ /* get next interrupt mapping value and store and bump up to
+ * point to next item in local_buf */
+ result = sscanf(local_buf, "%u", &interrupt_enable);
+ if ((result != 1) ||
+ (interrupt_enable != 0 && interrupt_enable != 1)) {
+ dev_err(&data->fc->dev, "Interrupt enable[%d] is not a valid value 0x%x.\n",
+ i, interrupt_enable);
+ return -EINVAL;
+ }
+ if (interrupt_enable == 0) {
+ f01->device_control.interrupt_enable[irq_reg] &=
+ (1 << irq_shift) ^ 0xFF;
+ } else
+ f01->device_control.interrupt_enable[irq_reg] |=
+ (1 << irq_shift);
+ irq_count++;
+ }
+
+ /* Make sure the irq count matches */
+ if (irq_count != f01->irq_count) {
+ dev_err(&data->fc->dev, "Interrupt enable count of %d doesn't match device count of %d.\n",
+ irq_count, f01->irq_count);
+ return -EINVAL;
+ }
+
+ /* write back to the control register */
+ retval = rmi_write_block(data->fc->rmi_dev, f01->interrupt_enable_addr,
+ f01->device_control.interrupt_enable,
+ f01->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(&data->fc->dev, "Could not write interrupt_enable mask to %#06x\n",
+ f01->interrupt_enable_addr);
+ return retval;
+ }
+
+ return size;
+}
+
+static const struct file_operations interrupt_enable_fops = {
+ .owner = THIS_MODULE,
+ .open = f01_debug_open,
+ .read = interrupt_enable_read,
+ .write = interrupt_enable_write,
+};
+
+static int setup_debugfs(struct rmi_function_container *fc)
+{
+ struct f01_data *data = fc->data;
+
+ if (!fc->debugfs_root)
+ return -ENODEV;
+
+ data->debugfs_interrupt_enable = debugfs_create_file("interrupt_enable",
+ RMI_RW_ATTR, fc->debugfs_root, fc, &interrupt_enable_fops);
+ if (!data->debugfs_interrupt_enable)
+ dev_warn(&fc->dev,
+ "Failed to create debugfs interrupt_enable.\n");
+
+ return 0;
+}
+
+static void teardown_debugfs(struct f01_data *f01)
+{
+ if (f01->debugfs_interrupt_enable)
+ debugfs_remove(f01->debugfs_interrupt_enable);
+}
+#endif
+
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
+ int num_of_irq_regs);
+
+static int rmi_f01_initialize(struct rmi_function_container *fc);
+
+static int rmi_f01_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f01_config(struct rmi_function_container *fc);
+
+
+static struct device_attribute fn_01_attrs[] = {
+ __ATTR(productinfo, RMI_RO_ATTR,
+ rmi_fn_01_productinfo_show, rmi_store_error),
+ __ATTR(productid, RMI_RO_ATTR,
+ rmi_fn_01_productid_show, rmi_store_error),
+ __ATTR(manufacturer, RMI_RO_ATTR,
+ rmi_fn_01_manufacturer_show, rmi_store_error),
+ __ATTR(datecode, RMI_RO_ATTR,
+ rmi_fn_01_datecode_show, rmi_store_error),
+
+ /* control register access */
+ __ATTR(sleepmode, RMI_RW_ATTR,
+ rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store),
+ __ATTR(nosleep, RMI_RW_ATTR,
+ rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store),
+ __ATTR(chargerinput, RMI_RW_ATTR,
+ rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store),
+ __ATTR(reportrate, RMI_RW_ATTR,
+ rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store),
+ /* We don't want arbitrary callers changing the interrupt enable mask,
+ * so it's read only.
+ */
+ __ATTR(interrupt_enable, RMI_RO_ATTR,
+ rmi_fn_01_interrupt_enable_show, rmi_store_error),
+ __ATTR(doze_interval, RMI_RW_ATTR,
+ rmi_fn_01_doze_interval_show, rmi_fn_01_doze_interval_store),
+ __ATTR(wakeup_threshold, RMI_RW_ATTR,
+ rmi_fn_01_wakeup_threshold_show,
+ rmi_fn_01_wakeup_threshold_store),
+ __ATTR(doze_holdoff, RMI_RW_ATTR,
+ rmi_fn_01_doze_holdoff_show, rmi_fn_01_doze_holdoff_store),
+
+ /* We make report rate RO, since the driver uses that to look for
+ * resets. We don't want someone faking us out by changing that
+ * bit.
+ */
+ __ATTR(configured, RMI_RO_ATTR,
+ rmi_fn_01_configured_show, rmi_store_error),
+
+ /* Command register access. */
+ __ATTR(reset, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_01_reset_store),
+
+ /* STatus register access. */
+ __ATTR(unconfigured, RMI_RO_ATTR,
+ rmi_fn_01_unconfigured_show, rmi_store_error),
+ __ATTR(flashprog, RMI_RO_ATTR,
+ rmi_fn_01_flashprog_show, rmi_store_error),
+ __ATTR(statuscode, RMI_RO_ATTR,
+ rmi_fn_01_statuscode_show, rmi_store_error),
+};
+
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_mask_and_set(struct rmi_device *rmi_dev,
+ u16 address,
+ u8 mask,
+ u8 set)
+{
+ u8 reg_contents;
+ int retval;
+
+ retval = rmi_read(rmi_dev, address, ®_contents);
+ if (retval < 0)
+ return retval;
+ reg_contents = (reg_contents & ~mask) | set;
+ retval = rmi_write(rmi_dev, address, reg_contents);
+ if (retval == 1)
+ return 0;
+ else if (retval == 0)
+ return -EIO;
+ return retval;
+}
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
+ data->basic_queries.productinfo_1,
+ data->basic_queries.productinfo_2);
+}
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id);
+}
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+ data->basic_queries.manufacturer_id);
+}
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
+ data->basic_queries.year,
+ data->basic_queries.month,
+ data->basic_queries.day);
+}
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc = NULL;
+ unsigned int reset;
+ int retval = 0;
+ /* Command register always reads as 0, so we can just use a local. */
+ union f01_device_commands commands = {};
+
+ fc = to_rmi_function_container(dev);
+
+ if (sscanf(buf, "%u", &reset) != 1)
+ return -EINVAL;
+ if (reset < 0 || reset > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (reset) {
+ commands.reset = 1;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &commands.reg, sizeof(commands.reg));
+ if (retval < 0) {
+ dev_err(dev, "Failed to issue reset command, code = %d.",
+ retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE,
+ "%d\n", data->device_control.ctrl0.sleep_mode);
+}
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
+ dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Setting sleep mode to %ld.", new_value);
+ data->device_control.ctrl0.sleep_mode = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write sleep mode, code %d.\n", retval);
+ return retval;
+}
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.ctrl0.nosleep);
+}
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.ctrl0.nosleep = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write nosleep bit.\n");
+ return retval;
+}
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.ctrl0.charger_input);
+}
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.ctrl0.charger_input = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write chargerinput bit.\n");
+ return retval;
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.ctrl0.report_rate);
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.ctrl0.report_rate = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write reportrate bit.\n");
+ return retval;
+}
+
+static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f01_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each irq value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->irq_count; i++) {
+ int irq_reg;
+ int irq_shift;
+ int interrupt_enable;
+
+ irq_reg = i / 8;
+ irq_shift = i % 8;
+ interrupt_enable =
+ ((data->device_control.interrupt_enable[irq_reg]
+ >> irq_shift) & 0x01);
+
+ /* get next irq value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", interrupt_enable);
+ /* bump up ptr to next location in buf if the
+ * snprintf was valid. Otherwise issue an error
+ * and return. */
+ if (len > 0) {
+ current_buf += len;
+ total_len += len;
+ } else {
+ dev_err(dev, "Failed to build interrupt_enable buffer, code = %d.\n",
+ len);
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ }
+ len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+ if (len > 0)
+ total_len += len;
+ else
+ dev_warn(dev, "%s: Failed to append carriage return.\n",
+ __func__);
+ return total_len;
+
+}
+
+static ssize_t rmi_fn_01_doze_interval_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.doze_interval);
+
+}
+
+static ssize_t rmi_fn_01_doze_interval_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ u16 ctrl_base_addr;
+
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 255) {
+ dev_err(dev, "%s: Invalid doze interval %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.doze_interval = new_value;
+ ctrl_base_addr = fc->fd.control_base_addr + sizeof(u8) +
+ (sizeof(u8)*(data->num_of_irq_regs));
+ dev_dbg(dev, "doze_interval store address %x, value %d",
+ ctrl_base_addr, data->device_control.doze_interval);
+
+ retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
+ &data->device_control.doze_interval,
+ sizeof(u8));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write doze interval.\n");
+ return retval;
+
+}
+
+static ssize_t rmi_fn_01_wakeup_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.wakeup_threshold);
+}
+
+static ssize_t rmi_fn_01_wakeup_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 255) {
+ dev_err(dev, "%s: Invalid wakeup threshold %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.doze_interval = new_value;
+ retval = rmi_write_block(fc->rmi_dev, data->wakeup_threshold_addr,
+ &data->device_control.wakeup_threshold,
+ sizeof(u8));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write wakeup threshold.\n");
+ return retval;
+
+}
+
+static ssize_t rmi_fn_01_doze_holdoff_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.doze_holdoff);
+
+}
+
+
+static ssize_t rmi_fn_01_doze_holdoff_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 255) {
+ dev_err(dev, "%s: Invalid doze holdoff %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.doze_interval = new_value;
+ retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
+ &data->device_control.doze_holdoff,
+ sizeof(u8));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write doze holdoff.\n");
+ return retval;
+
+}
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.ctrl0.configured);
+}
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_status.unconfigured);
+}
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_status.flash_prog);
+}
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+ data->device_status.status_code);
+}
+
+static int f01_device_init(struct rmi_function_container *fc)
+{
+ struct rmi_driver_data *driver_data =
+ dev_get_drvdata(&fc->rmi_dev->dev);
+ int error;
+
+ error = rmi_f01_alloc_memory(fc, driver_data->num_of_irq_regs);
+ if (error < 0)
+ return error;
+
+ error = rmi_f01_initialize(fc);
+ if (error < 0)
+ return error;
+
+ error = rmi_f01_create_sysfs(fc);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int rmi_f01_alloc_memory(struct rmi_function_container *fc,
+ int num_of_irq_regs)
+{
+ struct f01_data *f01;
+
+ f01 = devm_kzalloc(&fc->dev, sizeof(struct f01_data), GFP_KERNEL);
+ if (!f01) {
+ dev_err(&fc->dev, "Failed to allocate fn_01_data.\n");
+ return -ENOMEM;
+ }
+
+ f01->device_control.interrupt_enable = devm_kzalloc(&fc->dev,
+ sizeof(u8)*(num_of_irq_regs),
+ GFP_KERNEL);
+ if (!f01->device_control.interrupt_enable) {
+ dev_err(&fc->dev, "Failed to allocate interrupt enable.\n");
+ return -ENOMEM;
+ }
+ fc->data = f01;
+
+ return 0;
+}
+
+static int rmi_f01_initialize(struct rmi_function_container *fc)
+{
+ u8 temp;
+ int retval;
+ u16 ctrl_base_addr;
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+ struct f01_data *data = fc->data;
+ struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+ /* Set the configured bit and (optionally) other important stuff
+ * in the device control register. */
+ ctrl_base_addr = fc->fd.control_base_addr;
+ retval = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read F01 control.\n");
+ return retval;
+ }
+ switch (pdata->power_management.nosleep) {
+ case RMI_F01_NOSLEEP_DEFAULT:
+ break;
+ case RMI_F01_NOSLEEP_OFF:
+ data->device_control.ctrl0.nosleep = 0;
+ break;
+ case RMI_F01_NOSLEEP_ON:
+ data->device_control.ctrl0.nosleep = 1;
+ break;
+ }
+ /* Sleep mode might be set as a hangover from a system crash or
+ * reboot without power cycle. If so, clear it so the sensor
+ * is certain to function.
+ */
+ if (data->device_control.ctrl0.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
+ dev_warn(&fc->dev,
+ "WARNING: Non-zero sleep mode found. Clearing...\n");
+ data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+ }
+
+ data->device_control.ctrl0.configured = 1;
+ retval = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write F01 control.\n");
+ return retval;
+ }
+
+ data->irq_count = driver_data->irq_count;
+ data->num_of_irq_regs = driver_data->num_of_irq_regs;
+ ctrl_base_addr += sizeof(union f01_device_control_0);
+
+ data->interrupt_enable_addr = ctrl_base_addr;
+ retval = rmi_read_block(rmi_dev, ctrl_base_addr,
+ data->device_control.interrupt_enable,
+ sizeof(u8)*(driver_data->num_of_irq_regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read F01 control interrupt enable register.\n");
+ goto error_exit;
+ }
+ ctrl_base_addr += driver_data->num_of_irq_regs;
+
+ /* dummy read in order to clear irqs */
+ retval = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
+ return retval;
+ }
+
+ retval = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+ data->basic_queries.regs,
+ sizeof(data->basic_queries.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read device query registers.\n");
+ return retval;
+ }
+
+ retval = rmi_read_block(rmi_dev,
+ fc->fd.query_base_addr + sizeof(data->basic_queries.regs),
+ data->product_id, RMI_PRODUCT_ID_LENGTH);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read product ID.\n");
+ return retval;
+ }
+ data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+ dev_info(&fc->dev, "found RMI device, manufacturer: %s, product: %s\n",
+ data->basic_queries.manufacturer_id == 1 ?
+ "synaptics" : "unknown",
+ data->product_id);
+
+ /* read control register */
+ if (data->basic_queries.has_adjustable_doze) {
+ data->doze_interval_addr = ctrl_base_addr;
+ ctrl_base_addr++;
+
+ if (pdata->power_management.doze_interval) {
+ data->device_control.doze_interval =
+ pdata->power_management.doze_interval;
+ retval = rmi_write(rmi_dev, data->doze_interval_addr,
+ data->device_control.doze_interval);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to configure F01 doze interval register.\n");
+ goto error_exit;
+ }
+ } else {
+ retval = rmi_read(rmi_dev, data->doze_interval_addr,
+ &data->device_control.doze_interval);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read F01 doze interval register.\n");
+ goto error_exit;
+ }
+ }
+
+ data->wakeup_threshold_addr = ctrl_base_addr;
+ ctrl_base_addr++;
+
+ if (pdata->power_management.wakeup_threshold) {
+ data->device_control.wakeup_threshold =
+ pdata->power_management.wakeup_threshold;
+ retval = rmi_write(rmi_dev, data->wakeup_threshold_addr,
+ data->device_control.wakeup_threshold);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to configure F01 wakeup threshold register.\n");
+ goto error_exit;
+ }
+ } else {
+ retval = rmi_read(rmi_dev, data->wakeup_threshold_addr,
+ &data->device_control.wakeup_threshold);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read F01 wakeup threshold register.\n");
+ goto error_exit;
+ }
+ }
+ }
+
+ if (data->basic_queries.has_adjustable_doze_holdoff) {
+ data->doze_holdoff_addr = ctrl_base_addr;
+ ctrl_base_addr++;
+
+ if (pdata->power_management.doze_holdoff) {
+ data->device_control.doze_holdoff =
+ pdata->power_management.doze_holdoff;
+ retval = rmi_write(rmi_dev, data->doze_holdoff_addr,
+ data->device_control.doze_holdoff);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to configure F01 doze holdoff register.\n");
+ goto error_exit;
+ }
+ } else {
+ retval = rmi_read(rmi_dev, data->doze_holdoff_addr,
+ &data->device_control.doze_holdoff);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read F01 doze holdoff register.\n");
+ goto error_exit;
+ }
+ }
+ }
+
+ retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ data->device_status.regs, ARRAY_SIZE(data->device_status.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read device status.\n");
+ goto error_exit;
+ }
+
+ if (data->device_status.unconfigured) {
+ dev_err(&fc->dev, "Device reset during configuration process, status: %#02x!\n",
+ data->device_status.status_code);
+ retval = -EINVAL;
+ goto error_exit;
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ retval = setup_debugfs(fc);
+ if (retval < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
+ retval);
+ }
+
+ return retval;
+
+ error_exit:
+ kfree(data);
+ return retval;
+}
+
+static int rmi_f01_create_sysfs(struct rmi_function_container *fc)
+{
+ int attr_count = 0;
+ int retval = 0;
+ struct f01_data *data = fc->data;
+
+ dev_dbg(&fc->dev, "Creating sysfs files.");
+ for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+ attr_count++) {
+ if (!strcmp("doze_interval", fn_01_attrs[attr_count].attr.name)
+ && !data->basic_queries.has_lts) {
+ continue;
+ }
+ if (!strcmp("wakeup_threshold",
+ fn_01_attrs[attr_count].attr.name)
+ && !data->basic_queries.has_adjustable_doze) {
+ continue;
+ }
+ if (!strcmp("doze_holdoff", fn_01_attrs[attr_count].attr.name)
+ && !data->basic_queries.has_adjustable_doze_holdoff) {
+ continue;
+ }
+ retval = sysfs_create_file(&fc->dev.kobj,
+ &fn_01_attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ fn_01_attrs[attr_count].attr.name);
+ goto err_remove_sysfs;
+ }
+ }
+
+ return 0;
+
+err_remove_sysfs:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &fn_01_attrs[attr_count].attr);
+
+ return retval;
+}
+
+static int rmi_f01_config(struct rmi_function_container *fc)
+{
+ struct f01_data *data = fc->data;
+ int retval;
+
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write device_control.reg.\n");
+ return retval;
+ }
+
+ retval = rmi_write_block(fc->rmi_dev, data->interrupt_enable_addr,
+ data->device_control.interrupt_enable,
+ sizeof(u8)*(data->num_of_irq_regs));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write interrupt enable.\n");
+ return retval;
+ }
+ if (data->basic_queries.has_lts) {
+ retval = rmi_write_block(fc->rmi_dev, data->doze_interval_addr,
+ &data->device_control.doze_interval,
+ sizeof(u8));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write doze interval.\n");
+ return retval;
+ }
+ }
+
+ if (data->basic_queries.has_adjustable_doze) {
+ retval = rmi_write_block(
+ fc->rmi_dev, data->wakeup_threshold_addr,
+ &data->device_control.wakeup_threshold,
+ sizeof(u8));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write wakeup threshold.\n");
+ return retval;
+ }
+ }
+
+ if (data->basic_queries.has_adjustable_doze_holdoff) {
+ retval = rmi_write_block(fc->rmi_dev, data->doze_holdoff_addr,
+ &data->device_control.doze_holdoff,
+ sizeof(u8));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write doze holdoff.\n");
+ return retval;
+ }
+ }
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rmi_f01_suspend(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+ struct f01_data *data = driver_data->f01_container->data;
+ int retval = 0;
+
+ if (data->suspended)
+ return 0;
+
+ data->old_nosleep = data->device_control.ctrl0.nosleep;
+ data->device_control.ctrl0.nosleep = 0;
+ data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+ retval = rmi_write_block(rmi_dev,
+ driver_data->f01_container->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write sleep mode. Code: %d.\n",
+ retval);
+ data->device_control.ctrl0.nosleep = data->old_nosleep;
+ data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+ } else {
+ data->suspended = true;
+ retval = 0;
+ }
+
+ return retval;
+}
+
+static int rmi_f01_resume(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+ struct f01_data *data = driver_data->f01_container->data;
+ int retval = 0;
+
+ if (!data->suspended)
+ return 0;
+
+ data->device_control.ctrl0.nosleep = data->old_nosleep;
+ data->device_control.ctrl0.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+ retval = rmi_write_block(rmi_dev,
+ driver_data->f01_container->fd.control_base_addr,
+ data->device_control.ctrl0.regs,
+ ARRAY_SIZE(data->device_control.ctrl0.regs));
+ if (retval < 0)
+ dev_err(&fc->dev,
+ "Failed to restore normal operation. Code: %d.\n",
+ retval);
+ else {
+ data->suspended = false;
+ retval = 0;
+ }
+
+ return retval;
+}
+#endif /* CONFIG_PM */
+
+static int f01_remove_device(struct device *dev)
+{
+ int attr_count;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ teardown_debugfs(fc->data);
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+ attr_count++) {
+ sysfs_remove_file(&fc->dev.kobj, &fn_01_attrs[attr_count].attr);
+ }
+ return 0;
+}
+
+static int rmi_f01_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f01_data *data = fc->data;
+ int retval;
+
+ retval = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ data->device_status.regs, ARRAY_SIZE(data->device_status.regs));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to read device status, code: %d.\n",
+ retval);
+ return retval;
+ }
+ if (data->device_status.unconfigured) {
+ dev_warn(&fc->dev, "Device reset detected.\n");
+ retval = rmi_dev->driver->reset_handler(rmi_dev);
+ if (retval < 0)
+ return retval;
+ }
+ return 0;
+}
+
+static int f01_probe(struct device *dev);
+
+static struct rmi_function_handler function_handler = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_f01",
+ .bus = &rmi_bus_type,
+ .probe = f01_probe,
+ .remove = f01_remove_device,
+ },
+ .func = 0x01,
+ .config = rmi_f01_config,
+ .attention = rmi_f01_attention,
+
+#ifdef CONFIG_PM
+ .suspend = rmi_f01_suspend,
+ .resume = rmi_f01_resume,
+#endif /* CONFIG_PM */
+};
+
+static __devinit int f01_probe(struct device *dev)
+{
+ struct rmi_function_container *fc;
+
+ if (dev->type != &rmi_function_type) {
+ dev_dbg(dev, "Not a function device.\n");
+ return 1;
+ }
+ fc = to_rmi_function_container(dev);
+ if (fc->fd.function_number != function_handler.func) {
+ dev_dbg(dev, "Device is F%02X, not F%02X.\n",
+ fc->fd.function_number, function_handler.func);
+ return 1;
+ }
+
+ dev_dbg(dev, "Yay! It is F01!\n");
+ return f01_device_init(fc);
+}
+
+static int __init rmi_f01_module_init(void)
+{
+ int error;
+
+ error = driver_register(&function_handler.driver);
+ if (error < 0) {
+ pr_err("%s: register failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit rmi_f01_module_exit(void)
+{
+ driver_unregister(&function_handler.driver);
+}
+
+module_init(rmi_f01_module_init);
+module_exit(rmi_f01_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]>");
+MODULE_DESCRIPTION("RMI F01 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_f01.h b/drivers/input/rmi4/rmi_f01.h
new file mode 100644
index 0000000..34db09f
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.h
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+
+#ifndef _RMI_F01_H
+#define _RMI_F01_H
+
+#define RMI_PRODUCT_ID_LENGTH 10
+
+/**
+ * @manufacturer_id - reports the identity of the manufacturer of the RMI
+ * device. Synaptics RMI devices report a Manufacturer ID of $01.
+ * @custom_map - at least one custom, non
+ * RMI-compatible register exists in the register address map for this device.
+ * @non-compliant - the device implements a register map that is not compliant
+ * with the RMI specification.
+ * @has_lts - the device uses Synaptics' LTS hardware architecture.
+ * @has_sensor_id - the SensorID query register (F01_RMI_Query22) exists.
+ * @has_charger_input - the ChargerConnected bit (F01_RMI_Ctrl0, bit 5) is
+ * meaningful.
+ * @has_adjustable_doze - the doze (power management) control registers exist.
+ * @has_adjustable_doze_holdoff - the doze holdoff register exists.
+ * @has_product_properties - indicates the presence of F01_RMI_Query42,
+ * ProductProperties2.
+ * @productinfo_1 - meaning varies from product to product, consult your
+ * product spec sheet.
+ * @productinfo_2 - meaning varies from product to product, consult your
+ * product spec sheet.
+ * @year - year of manufacture MOD 2000.
+ * @month - month of manufacture
+ * @day - day of manufacture
+ * @wafer_id1_lsb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id1_msb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id2_lsb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id2_msb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ * @wafer_id3_lsb - The wafer-lot ID registers record the lot number of the
+ * wafer from which the module’s touch controller was produced.
+ */
+union f01_basic_queries {
+ struct {
+ u8 manufacturer_id:8;
+
+ bool custom_map:1;
+ bool non_compliant:1;
+ bool has_lts:1;
+ bool has_sensor_id:1;
+ bool has_charger_input:1;
+ bool has_adjustable_doze:1;
+ bool has_adjustable_doze_holdoff:1;
+ bool has_product_properties_2:1;
+
+ u8 productinfo_1:7;
+ bool q2_bit_7:1;
+ u8 productinfo_2:7;
+ bool q3_bit_7:1;
+
+ u8 year:5;
+ u8 month:4;
+ u8 day:5;
+ bool cp1:1;
+ bool cp2:1;
+ u8 wafer_id1_lsb:8;
+ u8 wafer_id1_msb:8;
+ u8 wafer_id2_lsb:8;
+ u8 wafer_id2_msb:8;
+ u8 wafer_id3_lsb:8;
+ } __attribute__((__packed__));
+ u8 regs[11];
+};
+
+union f01_device_status {
+ struct {
+ u8 status_code:4;
+ u8 reserved:2;
+ bool flash_prog:1;
+ bool unconfigured:1;
+ } __attribute__((__packed__));
+ u8 regs[1];
+};
+
+/* control register bits */
+#define RMI_SLEEP_MODE_NORMAL (0x00)
+#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define RMI_SLEEP_MODE_RESERVED0 (0x02)
+#define RMI_SLEEP_MODE_RESERVED1 (0x03)
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+ (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+/**
+ * @sleep_mode - This field controls power management on the device. This
+ * field affects all functions of the device together.
+ * @nosleep - When set to ‘1’, this bit disables whatever sleep mode may be
+ * selected by the sleep_mode field,and forces the device to run at full power
+ * without sleeping.
+ * @charger_input - When this bit is set to ‘1’, the touch controller employs
+ * a noise-filtering algorithm designed for use with a connected battery
+ * charger.
+ * @report_rate - sets the report rate for the device. The effect of this
+ * setting is highly product dependent. Check the spec sheet for your
+ * particular touch sensor.
+ * @configured - written by the host as an indicator that the device has been
+ * successfuly configured.
+ */
+union f01_device_control_0 {
+ struct {
+ u8 sleep_mode:2;
+ bool nosleep:1;
+ u8 reserved:2;
+ bool charger_input:1;
+ bool report_rate:1;
+ bool configured:1;
+ } __attribute__((__packed__));
+ u8 regs[1];
+};
+
+#endif
rmi_bus.c implements the basic functionality of the RMI bus. This file is
greatly simplified compared to the previous patch - we've switched from
"do it yourself" device/driver binding to using device_type to distinguish
between the two kinds of devices on the bus (sensor devices and function
specific devices) and using the standard bus implementation to manage devices
and drivers.
rmi_driver.c is a driver for the general functionality of the RMI sensor as a
whole, managing those behaviors not specific to any RMI4 function. This file
is also greatly simplified, mainly for the same reasons as rmi_bus.c was.
The rmi_driver.c implementation has been significantly decoupled from the F01
implementation, although it's unlikely that the dependencies on F01 can be
eliminated completely. Special dependencies from F01 back to rmi_driver.c
have been eliminated, though (see notes on rmi_f01.c for details).
IRQ management has been shifted from the physical layer into rmi_driver.c.
This simplifies the code and eliminates a bug where unserviced IRQs would
significantly impede the boot process.
One request from previously that we did not address with this patch is using
irq_chip to dispatch the attention IRQs to the various functions. We felt that
with the major overhaul of the bus structure, there was enough to consider with
the current changes. We're currently investigating the use of irq_chip - the
request hasn't been forgotten.
The header file rmi_driver.h provides definitions that are shared among
the modules of the RMI implementation, but not thought to be necessary
outside it.
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
---
drivers/input/rmi4/rmi_bus.c | 231 ++++++
drivers/input/rmi4/rmi_driver.c | 1529 +++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/rmi_driver.h | 438 +++++++++++
3 files changed, 2198 insertions(+), 0 deletions(-)
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
new file mode 100644
index 0000000..7b64cf4
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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 <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/kconfig.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#endif
+#include "rmi_driver.h"
+
+DEFINE_MUTEX(rmi_bus_mutex);
+
+static struct attribute *function_dev_attrs[] = {
+ NULL,
+};
+
+static struct attribute_group function_dev_attr_group = {
+ .attrs = function_dev_attrs,
+};
+
+static struct attribute_group *function_dev_attr_groups[] = {
+ &function_dev_attr_group,
+ NULL,
+};
+
+struct device_type rmi_function_type = {
+ .name = "rmi_function",
+ .groups = function_dev_attr_groups,
+};
+EXPORT_SYMBOL_GPL(rmi_function_type);
+
+static struct attribute *sensor_dev_attrs[] = {
+ NULL,
+};
+static struct attribute_group sensor_dev_attr_group = {
+ .attrs = sensor_dev_attrs,
+};
+
+static struct attribute_group *sensor_dev_attr_groups[] = {
+ &sensor_dev_attr_group,
+ NULL,
+};
+
+struct device_type rmi_sensor_type = {
+ .name = "rmi_sensor",
+ .groups = sensor_dev_attr_groups,
+};
+EXPORT_SYMBOL_GPL(rmi_sensor_type);
+
+static atomic_t physical_device_count = ATOMIC_INIT(0);
+
+#ifdef CONFIG_RMI4_DEBUG
+static struct dentry *rmi_debugfs_root;
+#endif
+
+#ifdef CONFIG_PM
+static int rmi_bus_suspend(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+ const struct dev_pm_ops *pm;
+
+ if (!driver)
+ return 0;
+
+ pm = driver->pm;
+ if (pm && pm->suspend)
+ return pm->suspend(dev);
+ if (driver->suspend)
+ return driver->suspend(dev, PMSG_SUSPEND);
+
+ return 0;
+}
+
+static int rmi_bus_resume(struct device *dev)
+{
+ struct device_driver *driver = dev->driver;
+ const struct dev_pm_ops *pm;
+
+ if (!driver)
+ return 0;
+
+ pm = driver->pm;
+ if (pm && pm->resume)
+ return pm->resume(dev);
+ if (driver->resume)
+ return driver->resume(dev);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops,
+ rmi_bus_suspend, rmi_bus_resume);
+
+struct bus_type rmi_bus_type = {
+ .name = "rmi",
+ .pm = &rmi_bus_pm_ops
+};
+EXPORT_SYMBOL_GPL(rmi_bus_type);
+
+static void release_rmidev_device(struct device *dev)
+{
+ device_unregister(dev);
+}
+
+int rmi_register_phys_device(struct rmi_phys_device *phys)
+{
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ struct rmi_device *rmi_dev;
+
+ if (!pdata) {
+ dev_err(phys->dev, "no platform data!\n");
+ return -EINVAL;
+ }
+
+ rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
+ if (!rmi_dev)
+ return -ENOMEM;
+
+ rmi_dev->phys = phys;
+ rmi_dev->dev.bus = &rmi_bus_type;
+ rmi_dev->dev.type = &rmi_sensor_type;
+
+ rmi_dev->number = atomic_inc_return(&physical_device_count) - 1;
+ rmi_dev->dev.release = release_rmidev_device;
+
+ dev_set_name(&rmi_dev->dev, "sensor%02d", rmi_dev->number);
+ dev_dbg(phys->dev, "%s: Registered %s as %s.\n", __func__,
+ pdata->sensor_name, dev_name(&rmi_dev->dev));
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG) && rmi_debugfs_root) {
+ rmi_dev->debugfs_root = debugfs_create_dir(
+ dev_name(&rmi_dev->dev), rmi_debugfs_root);
+ if (!rmi_dev->debugfs_root)
+ dev_err(&rmi_dev->dev, "Failed to create debugfs root.\n");
+ }
+
+ phys->rmi_dev = rmi_dev;
+ return device_register(&rmi_dev->dev);
+}
+EXPORT_SYMBOL(rmi_register_phys_device);
+
+void rmi_unregister_phys_device(struct rmi_phys_device *phys)
+{
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG) && rmi_dev->debugfs_root)
+ debugfs_remove(rmi_dev->debugfs_root);
+
+ kfree(rmi_dev);
+}
+EXPORT_SYMBOL(rmi_unregister_phys_device);
+
+int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data))
+{
+ int retval;
+ mutex_lock(&rmi_bus_mutex);
+ retval = bus_for_each_dev(&rmi_bus_type, NULL, data, func);
+ mutex_unlock(&rmi_bus_mutex);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(rmi_for_each_dev);
+
+static int __init rmi_bus_init(void)
+{
+ int error;
+
+ mutex_init(&rmi_bus_mutex);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ rmi_debugfs_root = debugfs_create_dir(rmi_bus_type.name, NULL);
+ if (!rmi_debugfs_root)
+ pr_err("%s: Failed to create debugfs root.\n",
+ __func__);
+ else if (IS_ERR(rmi_debugfs_root)) {
+ pr_err("%s: Kernel may not contain debugfs support, code=%ld\n",
+ __func__, PTR_ERR(rmi_debugfs_root));
+ rmi_debugfs_root = NULL;
+ }
+ }
+
+ error = bus_register(&rmi_bus_type);
+ if (error < 0) {
+ pr_err("%s: error registering the RMI bus: %d\n", __func__,
+ error);
+ return error;
+ }
+ pr_debug("%s: successfully registered RMI bus.\n", __func__);
+
+ return 0;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+ /* We should only ever get here if all drivers are unloaded, so
+ * all we have to do at this point is unregister ourselves.
+ */
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG) && rmi_debugfs_root)
+ debugfs_remove(rmi_debugfs_root);
+ bus_unregister(&rmi_bus_type);
+}
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
new file mode 100644
index 0000000..209fa41
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -0,0 +1,1529 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for generic RMI4 devices from Synpatics. It
+ * implements the mandatory f01 RMI register and depends on the presence of
+ * other required RMI functions.
+ *
+ * The RMI4 specification can be found here (URL split after files/ for
+ * style reasons):
+ * http://www.synaptics.com/sites/default/files/
+ * 511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
+ *
+ * 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 <linux/kernel.h>
+#include <linux/bitmap.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/kconfig.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include "rmi_driver.h"
+#include "rmi_f01.h"
+
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+
+#define RMI_DEVICE_RESET_CMD 0x01
+#define DEFAULT_RESET_DELAY_MS 100
+
+#define IRQ_DEBUG(data) (IS_ENABLED(CONFIG_RMI4_DEBUG) && data->irq_debug)
+
+/* sysfs files for attributes for driver values. */
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#ifdef CONFIG_RMI4_DEBUG
+struct driver_debugfs_data {
+ bool done;
+ struct rmi_device *rmi_dev;
+};
+
+static int debug_open(struct inode *inodep, struct file *filp)
+{
+ struct driver_debugfs_data *data;
+ struct rmi_device *rmi_dev;
+
+ rmi_dev = inodep->i_private;
+ data = devm_kzalloc(&rmi_dev->dev, sizeof(struct driver_debugfs_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->rmi_dev = inodep->i_private;
+ filp->private_data = data;
+ return 0;
+}
+
+#define DELAY_NAME "delay"
+
+static ssize_t delay_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_device_platform_data *pdata =
+ data->rmi_dev->phys->dev->platform_data;
+ int retval;
+ char local_buf[size];
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%d %d %d %d %d\n",
+ pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
+ pdata->spi_data.block_delay_us,
+ pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t delay_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_device_platform_data *pdata =
+ data->rmi_dev->phys->dev->platform_data;
+ int retval;
+ char local_buf[size];
+ unsigned int new_read_delay;
+ unsigned int new_write_delay;
+ unsigned int new_block_delay;
+ unsigned int new_pre_delay;
+ unsigned int new_post_delay;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ retval = sscanf(local_buf, "%u %u %u %u %u", &new_read_delay,
+ &new_write_delay, &new_block_delay,
+ &new_pre_delay, &new_post_delay);
+ if (retval != 5) {
+ dev_err(&data->rmi_dev->dev,
+ "Incorrect number of values provided for delay.");
+ return -EINVAL;
+ }
+ if (new_read_delay < 0) {
+ dev_err(&data->rmi_dev->dev,
+ "Byte delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_write_delay < 0) {
+ dev_err(&data->rmi_dev->dev,
+ "Write delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_block_delay < 0) {
+ dev_err(&data->rmi_dev->dev,
+ "Block delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_pre_delay < 0) {
+ dev_err(&data->rmi_dev->dev,
+ "Pre-transfer delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_post_delay < 0) {
+ dev_err(&data->rmi_dev->dev,
+ "Post-transfer delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(&data->rmi_dev->dev,
+ "Setting delays to %u %u %u %u %u.\n", new_read_delay,
+ new_write_delay, new_block_delay, new_pre_delay,
+ new_post_delay);
+ pdata->spi_data.read_delay_us = new_read_delay;
+ pdata->spi_data.write_delay_us = new_write_delay;
+ pdata->spi_data.block_delay_us = new_block_delay;
+ pdata->spi_data.pre_delay_us = new_pre_delay;
+ pdata->spi_data.post_delay_us = new_post_delay;
+
+ return size;
+}
+
+static const struct file_operations delay_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .read = delay_read,
+ .write = delay_write,
+};
+
+#define PHYS_NAME "phys"
+
+static ssize_t phys_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_phys_info *info = &data->rmi_dev->phys->info;
+ int retval;
+ char local_buf[size];
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size,
+ "%-5s %ld %ld %ld %ld %ld %ld\n",
+ info->proto ? info->proto : "unk",
+ info->tx_count, info->tx_bytes, info->tx_errs,
+ info->rx_count, info->rx_bytes, info->rx_errs);
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static const struct file_operations phys_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .read = phys_read,
+};
+
+static ssize_t attn_count_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_driver_data *rmi_data = dev_get_drvdata(&data->rmi_dev->dev);
+ int retval;
+ char local_buf[size];
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%d\n",
+ rmi_data->attn_count.counter);
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static const struct file_operations attn_count_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .read = attn_count_read,
+};
+
+static ssize_t irq_debug_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_driver_data *rmi_data = dev_get_drvdata(&data->rmi_dev->dev);
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n", rmi_data->irq_debug);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t irq_debug_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ unsigned int new_value;
+ struct driver_debugfs_data *data = filp->private_data;
+ struct rmi_driver_data *rmi_data = dev_get_drvdata(&data->rmi_dev->dev);
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ retval = sscanf(local_buf, "%u", &new_value);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ rmi_data->irq_debug = new_value;
+
+ return size;
+}
+
+static const struct file_operations irq_debug_fops = {
+ .owner = THIS_MODULE,
+ .open = debug_open,
+ .read = irq_debug_read,
+ .write = irq_debug_write,
+};
+
+static int setup_debugfs(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_phys_info *info = &rmi_dev->phys->info;
+ int retval = 0;
+
+ if (!rmi_dev->debugfs_root)
+ return -ENODEV;
+
+ if (IS_ENABLED(CONFIG_RMI4_SPI) && !strncmp("spi", info->proto, 3)) {
+ data->debugfs_delay = debugfs_create_file(DELAY_NAME,
+ RMI_RW_ATTR, rmi_dev->debugfs_root, rmi_dev,
+ &delay_fops);
+ if (!data->debugfs_delay || IS_ERR(data->debugfs_delay)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs delay.\n");
+ data->debugfs_delay = NULL;
+ }
+ }
+
+ data->debugfs_phys = debugfs_create_file(PHYS_NAME, RMI_RO_ATTR,
+ rmi_dev->debugfs_root, rmi_dev, &phys_fops);
+ if (!data->debugfs_phys || IS_ERR(data->debugfs_phys)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs phys.\n");
+ data->debugfs_phys = NULL;
+ }
+
+ data->debugfs_irq = debugfs_create_file("irq_debug",
+ RMI_RW_ATTR,
+ rmi_dev->debugfs_root,
+ rmi_dev, &irq_debug_fops);
+ if (!data->debugfs_irq || IS_ERR(data->debugfs_irq)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs irq_debug.\n");
+ data->debugfs_irq = NULL;
+ }
+
+ data->debugfs_attn_count = debugfs_create_file("attn_count",
+ RMI_RO_ATTR,
+ rmi_dev->debugfs_root,
+ rmi_dev, &attn_count_fops);
+ if (!data->debugfs_phys || IS_ERR(data->debugfs_attn_count)) {
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs attn_count.\n");
+ data->debugfs_attn_count = NULL;
+ }
+
+ return retval;
+}
+
+static void teardown_debugfs(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (IS_ENABLED(CONFIG_RMI4_SPI) && data->debugfs_delay)
+ debugfs_remove(data->debugfs_delay);
+ if (data->debugfs_phys)
+ debugfs_remove(data->debugfs_phys);
+ if (data->debugfs_irq)
+ debugfs_remove(data->debugfs_irq);
+ if (data->debugfs_attn_count)
+ debugfs_remove(data->debugfs_attn_count);
+}
+#else
+#define teardown_debugfs(rmi_dev)
+#define setup_debugfs(rmi_dev) 0
+#endif
+
+static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev);
+
+static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev);
+
+static int rmi_driver_irq_restore(struct rmi_device *rmi_dev);
+
+/** This sysfs attribute is deprecated, and will be removed in a future release.
+ */
+static struct device_attribute attrs[] = {
+ __ATTR(enabled, RMI_RW_ATTR,
+ rmi_driver_enabled_show, rmi_driver_enabled_store),
+};
+
+static struct device_attribute bsr_attribute = __ATTR(bsr, RMI_RW_ATTR,
+ rmi_driver_bsr_show, rmi_driver_bsr_store);
+
+/* Useful helper functions for u8* */
+
+static bool u8_is_any_set(u8 *target, int size)
+{
+ int i;
+ /* We'd like to use find_first_bit, but it ALWAYS returns 1,
+ * no matter what we pass it. So we have to do this the hard way.
+ * return find_first_bit((long unsigned int *) target, size) != 0;
+ */
+ for (i = 0; i < size; i++) {
+ if (target[i])
+ return true;
+ }
+ return false;
+}
+
+/** This is here because all those casts made for some ugly code.
+ */
+static void u8_and(u8 *dest, u8 *target1, u8 *target2, int nbits)
+{
+ bitmap_and((long unsigned int *) dest,
+ (long unsigned int *) target1,
+ (long unsigned int *) target2,
+ nbits);
+}
+
+static void rmi_free_function_list(struct rmi_device *rmi_dev)
+{
+ struct rmi_function_container *entry, *n;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (!data) {
+ dev_err(&rmi_dev->dev, "WTF: No driver data in %s\n", __func__);
+ return;
+ }
+
+ data->f01_container = NULL;
+
+ if (list_empty(&data->rmi_functions.list))
+ return;
+
+ list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
+ device_unregister(&entry->dev);
+ list_del(&entry->list);
+ }
+}
+
+static void release_fc_device(struct device *dev)
+{
+ dev_dbg(dev, "REMOVING KOBJ!");
+ kobject_put(&dev->kobj);
+}
+
+static int reset_one_function(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc || !fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+ if (fh->reset) {
+ retval = fh->reset(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Reset failed with code %d.\n",
+ retval);
+ }
+
+ return retval;
+}
+
+static int configure_one_function(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc || !fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+ if (fh->config) {
+ retval = fh->config(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Config failed with code %d.\n",
+ retval);
+ }
+
+ return retval;
+}
+
+static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *entry;
+ int retval;
+
+ if (list_empty(&data->rmi_functions.list))
+ return 0;
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list) {
+ retval = reset_one_function(entry);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *entry;
+ int retval;
+
+ if (list_empty(&data->rmi_functions.list))
+ return 0;
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list) {
+ retval = configure_one_function(entry);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static void process_one_interrupt(struct rmi_function_container *fc,
+ u8 *irq_status, struct rmi_driver_data *data)
+{
+ struct rmi_function_handler *fh;
+ u8 irq_bits[data->num_of_irq_regs];
+
+ if (!fc || !fc->dev.driver)
+ return;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+ if (fc->irq_mask && fh->attention) {
+ u8_and(irq_bits, irq_status, fc->irq_mask,
+ data->irq_count);
+ if (u8_is_any_set(irq_bits, data->num_of_irq_regs))
+ fh->attention(fc, irq_bits);
+ }
+
+}
+
+static int process_interrupt_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_function_container *entry;
+ u8 irq_status[data->num_of_irq_regs];
+ int error;
+
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.data_base_addr + 1,
+ irq_status, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "Failed to read irqs, code=%d\n", error);
+ return error;
+ }
+
+ mutex_lock(&data->irq_mutex);
+
+ u8_and(irq_status, irq_status, data->current_irq_mask,
+ data->irq_count);
+ /* At this point, irq_status has all bits that are set in the
+ * interrupt status register and are enabled.
+ */
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->irq_mask)
+ process_one_interrupt(entry, irq_status,
+ data);
+
+ mutex_unlock(&data->irq_mutex);
+ return 0;
+}
+
+static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ might_sleep();
+ /* Can get called before the driver is fully ready to deal with
+ * interrupts.
+ */
+ if (!data || !data->f01_container) {
+ dev_dbg(&rmi_dev->dev,
+ "Not ready to handle interrupts yet!\n");
+ return 0;
+ }
+
+ return process_interrupt_requests(rmi_dev);
+}
+
+static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ int error;
+
+ /* Can get called before the driver is fully ready to deal with
+ * this situation.
+ */
+ if (!data || !data->f01_container) {
+ dev_warn(&rmi_dev->dev,
+ "Not ready to handle reset yet!\n");
+ return 0;
+ }
+
+ error = rmi_driver_process_reset_requests(rmi_dev);
+ if (error < 0)
+ return error;
+
+
+ error = rmi_driver_process_config_requests(rmi_dev);
+ if (error < 0)
+ return error;
+
+ if (data->irq_stored) {
+ error = rmi_driver_irq_restore(rmi_dev);
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+/*
+ * Construct a function's IRQ mask. This should be called once and stored.
+ */
+static u8 *rmi_driver_irq_get_mask(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc) {
+ int i;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ /* call devm_kcalloc when it will be defined in kernel in future */
+ u8 *irq_mask = devm_kzalloc(&rmi_dev->dev, data->num_of_irq_regs,
+ GFP_KERNEL);
+
+ if (irq_mask)
+ for (i = 0; i < fc->num_of_irqs; i++)
+ set_bit(fc->irq_pos+i, (long unsigned int *) irq_mask);
+
+ return irq_mask;
+}
+
+/*
+ * This pair of functions allows functions like function 54 to request to have
+ * other interupts disabled until the restore function is called. Only one store
+ * happens at a time.
+ */
+static int rmi_driver_irq_save(struct rmi_device *rmi_dev, u8 * new_ints)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+
+ mutex_lock(&data->irq_mutex);
+ if (!data->irq_stored) {
+ /* Save current enabled interrupts */
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ /*
+ * Disable every interrupt except for function 54
+ * TODO:Will also want to not disable function 1-like functions.
+ * No need to take care of this now, since there's no good way
+ * to identify them.
+ */
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ new_ints, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to change enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ memcpy(data->current_irq_mask, new_ints,
+ data->num_of_irq_regs * sizeof(u8));
+ data->irq_stored = true;
+ } else {
+ retval = -ENOSPC; /* No space to store IRQs.*/
+ dev_err(dev, "Attempted to save IRQs when already stored!");
+ }
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return retval;
+}
+
+static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+ mutex_lock(&data->irq_mutex);
+
+ if (data->irq_stored) {
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to write enabled interupts!",
+ __func__);
+ goto error_unlock;
+ }
+ memcpy(data->current_irq_mask, data->irq_mask_store,
+ data->num_of_irq_regs * sizeof(u8));
+ data->irq_stored = false;
+ } else {
+ retval = -EINVAL;
+ dev_err(dev, "%s: Attempted to restore values when not stored!",
+ __func__);
+ }
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return retval;
+}
+
+static int init_function_device(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc)
+{
+ int retval;
+
+ /* This memset might not be what we want to do... */
+ memset(&(fc->dev), 0, sizeof(struct device));
+ dev_set_name(&(fc->dev), "%s.fn%02x", dev_name(&rmi_dev->dev),
+ fc->fd.function_number);
+ fc->dev.release = release_fc_device;
+
+ fc->dev.parent = &rmi_dev->dev;
+ fc->dev.type = &rmi_function_type;
+ fc->dev.bus = &rmi_bus_type;
+ dev_dbg(&rmi_dev->dev, "Register F%02X.\n", fc->fd.function_number);
+ retval = device_register(&fc->dev);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed device_register for F%02X.\n",
+ fc->fd.function_number);
+ return retval;
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ char dirname[12];
+
+ snprintf(dirname, 12, "F%02X", fc->fd.function_number);
+ fc->debugfs_root = debugfs_create_dir(dirname,
+ rmi_dev->debugfs_root);
+ if (!fc->debugfs_root)
+ dev_warn(&fc->dev, "Failed to create debugfs dir.\n");
+ }
+
+ return 0;
+}
+
+static int create_function_container(struct rmi_device *rmi_dev,
+ struct pdt_entry *pdt_ptr,
+ int *current_irq_count,
+ u16 page_start)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *fc = NULL;
+ int retval = 0;
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_device_platform_data *pdata;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ dev_dbg(dev, "Initializing F%02X for %s.\n", pdt_ptr->function_number,
+ pdata->sensor_name);
+
+ fc = devm_kzalloc(dev, sizeof(struct rmi_function_container),
+ GFP_KERNEL);
+ if (!fc) {
+ dev_err(dev, "Failed to allocate F%02X container.\n",
+ pdt_ptr->function_number);
+ return -ENOMEM;
+ }
+
+ copy_pdt_entry_to_fd(pdt_ptr, &fc->fd, page_start);
+
+ fc->rmi_dev = rmi_dev;
+ fc->num_of_irqs = pdt_ptr->interrupt_source_count;
+
+ fc->irq_pos = *current_irq_count;
+ *current_irq_count += fc->num_of_irqs;
+
+ retval = init_function_device(rmi_dev, fc);
+ if (retval < 0) {
+ dev_err(dev, "Failed to initialize F%02X device.\n",
+ pdt_ptr->function_number);
+ goto error_free_data;
+ }
+
+ INIT_LIST_HEAD(&fc->list);
+ /* we need to ensure that F01 is at the head of the list.
+ */
+ if (pdt_ptr->function_number == 0x01) {
+ list_add(&fc->list, &data->rmi_functions.list);
+ data->f01_container = fc;
+ } else
+ list_add_tail(&fc->list, &data->rmi_functions.list);
+ return 0;
+
+error_free_data:
+ return retval;
+}
+
+/*
+ * Once we find F01, we need to see if we're in bootloader mode. If we are,
+ * we'll stop scanning the PDT with the current page (usually 0x00 in that
+ * case).
+ */
+static void check_bootloader_mode(struct rmi_device *rmi_dev,
+ struct pdt_entry *pdt_ptr,
+ u16 page_start)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ union f01_device_status device_status;
+ int retval = 0;
+
+ retval = rmi_read(rmi_dev, pdt_ptr->data_base_addr+page_start,
+ device_status.regs);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "Failed to read device status.\n");
+ return;
+ }
+ data->f01_bootloader_mode = device_status.flash_prog;
+ if (device_status.flash_prog)
+ dev_warn(&rmi_dev->dev,
+ "WARNING: RMI4 device is in bootloader mode!\n");
+
+}
+
+/*
+ * Scan the PDT for F01 so we can force a reset before anything else
+ * is done. This forces the sensor into a known state, and also
+ * forces application of any pending updates from reflashing the
+ * firmware or configuration. We have to do this before actually
+ * building the PDT because the reflash might cause various registers
+ * to move around.
+ */
+static int do_initial_reset(struct rmi_device *rmi_dev)
+{
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ bool done = false;
+ bool has_f01 = false;
+ bool has_f34 = false;
+ struct pdt_entry f34_pdt, f01_pdt;
+ int i;
+ int retval;
+ struct rmi_device_platform_data *pdata;
+
+ dev_dbg(dev, "Initial reset.\n");
+ pdata = to_rmi_platform_data(rmi_dev);
+ for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
+ u16 page_start = RMI4_PAGE_SIZE * page;
+ u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
+ u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+
+ done = true;
+ for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read PDT entry at %#06x failed, code = %d.\n",
+ i, retval);
+ return retval;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+ done = false;
+
+ if (pdt_entry.function_number == 0x01) {
+ u16 cmd_addr = page_start +
+ pdt_entry.command_base_addr;
+ u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ retval = rmi_write_block(rmi_dev, cmd_addr,
+ &cmd_buf, 1);
+ if (retval < 0) {
+ dev_err(dev, "Initial reset failed. Code = %d.\n",
+ retval);
+ return retval;
+ }
+ mdelay(pdata->reset_delay_ms);
+ if (IS_ENABLED(CONFIG_RMI4_FWLIB))
+ memcpy(&f01_pdt, &pdt_entry,
+ sizeof(pdt_entry));
+ else
+ done = true;
+ has_f01 = true;
+ break;
+ } else if (IS_ENABLED(CONFIG_RMI4_FWLIB) &&
+ pdt_entry.function_number == 0x34) {
+ memcpy(&f34_pdt, &pdt_entry, sizeof(pdt_entry));
+ has_f34 = true;
+ }
+ }
+ }
+
+ if (!has_f01) {
+ dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
+ return -ENODEV;
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_FWLIB)) {
+ if (has_f34)
+ rmi4_fw_update(rmi_dev, &f01_pdt, &f34_pdt);
+ else
+ dev_warn(dev, "WARNING: No F34, firmware update will not be done.\n");
+ }
+
+ return 0;
+}
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ int irq_count = 0;
+ bool done = false;
+ int i;
+ int retval;
+
+ dev_dbg(dev, "Scanning PDT...\n");
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+ mutex_lock(&data->pdt_mutex);
+
+ for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
+ u16 page_start = RMI4_PAGE_SIZE * page;
+ u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
+ u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+
+ done = true;
+ for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read of PDT entry at %#06x failed.\n",
+ i);
+ goto error_exit;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+
+ dev_dbg(dev, "Found F%02X on page %#04x\n",
+ pdt_entry.function_number, page);
+ done = false;
+
+ if (pdt_entry.function_number == 0x01)
+ check_bootloader_mode(rmi_dev, &pdt_entry,
+ page_start);
+
+ retval = create_function_container(rmi_dev,
+ &pdt_entry, &irq_count, page_start);
+
+ if (retval)
+ goto error_exit;
+ }
+ done = done || data->f01_bootloader_mode;
+ }
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (irq_count + 7) / 8;
+ dev_dbg(dev, "%s: Done with PDT scan.\n", __func__);
+ retval = 0;
+
+error_exit:
+ mutex_unlock(&data->pdt_mutex);
+ return retval;
+}
+
+static irqreturn_t rmi_irq_thread(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_driver *driver = rmi_dev->driver;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ struct rmi_driver_data *data;
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ if IRQ_DEBUG(data)
+ dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
+ gpio_get_value(pdata->attn_gpio));
+
+ if (gpio_get_value(pdata->attn_gpio) == pdata->attn_polarity) {
+ atomic_inc(&data->attn_count);
+ if (driver && driver->irq_handler && rmi_dev)
+ driver->irq_handler(rmi_dev, irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int enable_sensor(struct rmi_device *rmi_dev);
+static void disable_sensor(struct rmi_device *rmi_dev);
+
+static int __devinit rmi_driver_probe(struct device *dev)
+{
+ struct rmi_driver *rmi_driver;
+ struct rmi_driver_data *data = NULL;
+ struct rmi_function_container *fc;
+ struct rmi_device_platform_data *pdata;
+ int retval = 0;
+ int attr_count = 0;
+ struct rmi_device *rmi_dev;
+
+ dev_dbg(dev, "%s: Starting probe.\n", __func__);
+ if (!dev->driver) {
+ dev_err(dev, "No driver for RMI4 device during probe!\n");
+ return -ENODEV;
+ }
+
+ if (dev->type != &rmi_sensor_type) {
+ dev_dbg(dev, "Not a sensor device.\n");
+ return 1;
+ }
+
+ rmi_dev = to_rmi_device(dev);
+ rmi_driver = to_rmi_driver(dev->driver);
+ rmi_dev->driver = rmi_driver;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ data = devm_kzalloc(dev, sizeof(struct rmi_driver_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(dev, "%s: Failed to allocate driver data.\n", __func__);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&data->rmi_functions.list);
+ dev_set_drvdata(&rmi_dev->dev, data);
+ mutex_init(&data->pdt_mutex);
+
+ /* Right before a warm boot, the sensor might be in some unusual state,
+ * such as F54 diagnostics, or F34 bootloader mode. In order to clear
+ * the sensor to a known state, we issue a initial reset to clear any
+ * previous settings and force it into normal operation.
+ *
+ * For a number of reasons, this initial reset may fail to return
+ * within the specified time, but we'll still be able to bring up the
+ * driver normally after that failure. This occurs most commonly in
+ * a cold boot situation (where then firmware takes longer to come up
+ * than from a warm boot) and the reset_delay_ms in the platform data
+ * has been set too short to accomodate that. Since the sensor will
+ * eventually come up and be usable, we don't want to just fail here
+ * and leave the customer's device unusable. So we warn them, and
+ * continue processing.
+ */
+ if (!pdata->reset_delay_ms)
+ pdata->reset_delay_ms = DEFAULT_RESET_DELAY_MS;
+ retval = do_initial_reset(rmi_dev);
+ if (retval)
+ dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n");
+
+
+ retval = rmi_scan_pdt(rmi_dev);
+ if (retval) {
+ dev_err(dev, "PDT scan for %s failed with code %d.\n",
+ pdata->sensor_name, retval);
+ goto err_free_data;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "missing F01 container!\n");
+ retval = -EINVAL;
+ goto err_free_data;
+ }
+
+ list_for_each_entry(fc, &data->rmi_functions.list, list)
+ fc->irq_mask = rmi_driver_irq_get_mask(rmi_dev, fc);
+
+ retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION,
+ data->pdt_props.regs);
+ if (retval < 0) {
+ /* we'll print out a warning and continue since
+ * failure to get the PDT properties is not a cause to fail
+ */
+ dev_warn(dev, "Could not read PDT properties from %#06x. Assuming 0x00.\n",
+ PDT_PROPERTIES_LOCATION);
+ }
+
+ dev_dbg(dev, "%s: Creating sysfs files.", __func__);
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = device_create_file(dev, &attrs[attr_count]);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to create sysfs file %s.\n",
+ __func__, attrs[attr_count].attr.name);
+ goto err_free_data;
+ }
+ }
+ if (data->pdt_props.has_bsr) {
+ retval = device_create_file(dev, &bsr_attribute);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to create sysfs file bsr.\n",
+ __func__);
+ goto err_free_data;
+ }
+ }
+
+ mutex_init(&data->irq_mutex);
+ /* call devm_kcalloc when it will be defined in kernel in furture */
+ data->current_irq_mask = devm_kzalloc(dev,
+ data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->current_irq_mask) {
+ dev_err(dev, "Failed to allocate current_irq_mask.\n");
+ retval = -ENOMEM;
+ goto err_free_data;
+ }
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_free_data;
+ }
+
+ /* call devm_kcalloc when it will be defined in kernel in furture */
+ data->irq_mask_store = devm_kzalloc(dev,
+ data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->irq_mask_store) {
+ dev_err(dev, "Failed to allocate mask store.\n");
+ retval = -ENOMEM;
+ goto err_free_data;
+ }
+
+ if (IS_ENABLED(CONFIG_PM)) {
+ data->pm_data = pdata->pm_data;
+ data->pre_suspend = pdata->pre_suspend;
+ data->post_suspend = pdata->post_suspend;
+ data->pre_resume = pdata->pre_resume;
+ data->post_resume = pdata->post_resume;
+
+ mutex_init(&data->suspend_mutex);
+ }
+
+ data->irq = gpio_to_irq(pdata->attn_gpio);
+ if (pdata->level_triggered) {
+ data->irq_flags = IRQF_ONESHOT |
+ ((pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH)
+ ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW);
+ } else {
+ data->irq_flags =
+ (pdata->attn_polarity == RMI_ATTN_ACTIVE_HIGH)
+ ? IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ }
+
+ if (data->f01_container->dev.driver) {
+ /* Driver already bound, so enable ATTN now. */
+ enable_sensor(rmi_dev);
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ retval = setup_debugfs(rmi_dev);
+ if (retval < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs. Code: %d.\n",
+ retval);
+ }
+
+ return 0;
+
+ err_free_data:
+ rmi_free_function_list(rmi_dev);
+ for (attr_count--; attr_count >= 0; attr_count--)
+ device_remove_file(dev, &attrs[attr_count]);
+ if (data->pdt_props.has_bsr)
+ device_remove_file(dev, &bsr_attribute);
+ return retval;
+}
+
+static void disable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (!data->enabled)
+ return;
+
+ if (rmi_dev->phys->disable_device)
+ rmi_dev->phys->disable_device(rmi_dev->phys);
+
+ disable_irq(data->irq);
+ free_irq(data->irq, rmi_dev->phys);
+
+ data->enabled = false;
+}
+
+static int enable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_phys_device *rmi_phys;
+ int retval = 0;
+
+ if (data->enabled)
+ return 0;
+
+ if (rmi_dev->phys->enable_device) {
+ retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+ if (retval)
+ return retval;
+ }
+
+ rmi_phys = rmi_dev->phys;
+ retval = request_threaded_irq(data->irq,
+ rmi_phys->hard_irq ? rmi_phys->hard_irq : NULL,
+ rmi_phys->irq_thread ?
+ rmi_phys->irq_thread : rmi_irq_thread,
+ data->irq_flags,
+ dev_name(&rmi_dev->dev), rmi_phys);
+ if (retval)
+ return retval;
+
+
+ data->enabled = true;
+ return 0;
+}
+
+static int f01_notifier_call(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct device *dev = data;
+ struct rmi_function_container *fc;
+
+ if (dev->type != &rmi_function_type)
+ return 0;
+
+ fc = to_rmi_function_container(dev);
+ if (fc->fd.function_number != 0x01)
+ return 0;
+
+ switch (action) {
+ case BUS_NOTIFY_BOUND_DRIVER:
+ dev_dbg(dev, "%s: F01 driver bound.\n", __func__);
+ enable_sensor(fc->rmi_dev);
+ break;
+ case BUS_NOTIFY_UNBIND_DRIVER:
+ dev_dbg(dev, "%s: F01 driver going away.\n", __func__);
+ disable_sensor(fc->rmi_dev);
+ break;
+ }
+ return 0;
+}
+
+static struct notifier_block rmi_bus_notifier = {
+ .notifier_call = f01_notifier_call,
+};
+
+#ifdef CONFIG_PM
+static int suspend_one_device(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+
+ if (fh->suspend) {
+ retval = fh->suspend(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Suspend failed, code: %d",
+ retval);
+ }
+
+ return retval;
+}
+
+static int standard_suspend(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (data->suspended)
+ goto exit;
+
+ if (data->pre_suspend) {
+ retval = data->pre_suspend(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ disable_sensor(rmi_dev);
+
+ /** Do it backwards so F01 comes last. */
+ list_for_each_entry_reverse(entry, &data->rmi_functions.list, list)
+ if (suspend_one_device(entry) < 0)
+ goto exit;
+
+ data->suspended = true;
+
+ if (data->post_suspend)
+ retval = data->post_suspend(data->pm_data);
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+static int resume_one_device(struct rmi_function_container *fc)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fc->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fc->dev.driver);
+
+ if (fh->resume) {
+ retval = fh->resume(fc);
+ if (retval < 0)
+ dev_err(&fc->dev, "Resume failed, code: %d",
+ retval);
+ }
+
+ return retval;
+}
+
+static int standard_resume(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (!data->suspended)
+ goto exit;
+
+ if (data->pre_resume) {
+ retval = data->pre_resume(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ /** Do it forwards, so F01 comes first. */
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (resume_one_device(entry) < 0)
+ goto exit;
+
+ retval = enable_sensor(rmi_dev);
+ if (retval)
+ goto exit;
+
+
+ if (data->post_resume) {
+ retval = data->post_resume(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ data->suspended = false;
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+static int rmi_driver_suspend(struct device *dev)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ return standard_suspend(rmi_dev);
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ return standard_resume(rmi_dev);
+}
+
+#endif /* CONFIG_PM */
+
+static int __devexit rmi_driver_remove(struct device *dev)
+{
+ struct rmi_driver_data *data;
+ int i;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ disable_sensor(rmi_dev);
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG))
+ teardown_debugfs(rmi_dev);
+
+ rmi_free_function_list(rmi_dev);
+ for (i = 0; i < ARRAY_SIZE(attrs); i++)
+ device_remove_file(&rmi_dev->dev, &attrs[i]);
+ if (data->pdt_props.has_bsr)
+ device_remove_file(&rmi_dev->dev, &bsr_attribute);
+ return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend,
+ rmi_driver_resume, NULL);
+
+static struct rmi_driver sensor_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_generic",
+ .bus = &rmi_bus_type,
+ .pm = &rmi_driver_pm,
+ .probe = rmi_driver_probe,
+ .remove = __devexit_p(rmi_driver_remove),
+ },
+ .irq_handler = rmi_driver_irq_handler,
+ .reset_handler = rmi_driver_reset_handler,
+ .get_func_irq_mask = rmi_driver_irq_get_mask,
+ .store_irq_mask = rmi_driver_irq_save,
+ .restore_irq_mask = rmi_driver_irq_restore,
+};
+
+/* sysfs show and store fns for driver attributes */
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ rmi_dev = to_rmi_device(dev);
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->bsr);
+}
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long val;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ /* need to convert the string data to an actual value */
+ retval = strict_strtoul(buf, 10, &val);
+ if (retval < 0 || val > 255) {
+ dev_err(dev, "Invalid value '%s' written to BSR.\n", buf);
+ return -EINVAL;
+ }
+
+ retval = rmi_write(rmi_dev, BSR_LOCATION, (u8)val);
+ if (retval < 0) {
+ dev_err(dev, "%s : failed to write bsr %lu to %#06x\n",
+ __func__, val, BSR_LOCATION);
+ return retval;
+ }
+
+ data->bsr = val;
+
+ return count;
+}
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->enabled);
+}
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ int new_value;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (sysfs_streq(buf, "0"))
+ new_value = false;
+ else if (sysfs_streq(buf, "1"))
+ new_value = true;
+ else
+ return -EINVAL;
+
+ if (new_value) {
+ retval = enable_sensor(rmi_dev);
+ if (retval) {
+ dev_err(dev, "Failed to enable sensor, code=%d.\n",
+ retval);
+ return -EIO;
+ }
+ } else {
+ disable_sensor(rmi_dev);
+ }
+
+ return count;
+}
+
+static int __init rmi_driver_init(void)
+{
+ int retval;
+
+ retval = driver_register(&sensor_driver.driver);
+ if (retval) {
+ pr_err("%s: driver register failed, code=%d.\n", __func__,
+ retval);
+ return retval;
+ }
+
+ /* Ask the bus to let us know when drivers are bound to devices. */
+ retval = bus_register_notifier(&rmi_bus_type, &rmi_bus_notifier);
+ if (retval) {
+ pr_err("%s: failed to register bus notifier, code=%d.\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return retval;
+}
+
+static void __exit rmi_driver_exit(void)
+{
+ bus_unregister_notifier(&rmi_bus_type, &rmi_bus_notifier);
+ driver_unregister(&sensor_driver.driver);
+}
+
+module_init(rmi_driver_init);
+module_exit(rmi_driver_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_DESCRIPTION("RMI generic driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
new file mode 100644
index 0000000..a5121b7
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2011, 2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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.
+ */
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#define RMI_DRIVER_VERSION "1.5"
+
+#define RMI_PRODUCT_ID_LENGTH 10
+#define RMI_PRODUCT_INFO_LENGTH 2
+#define RMI_DATE_CODE_LENGTH 3
+
+#include <linux/ctype.h>
+/* Sysfs related macros */
+
+/* You must define FUNCTION_DATA and FNUM to use these functions. */
+#if defined(FNUM) && defined(FUNCTION_DATA)
+
+#define tricat(x, y, z) tricat_(x, y, z)
+
+#define tricat_(x, y, z) x##y##z
+
+#define show_union_struct_prototype(propname)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
+ struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf);\
+\
+static DEVICE_ATTR(propname, RMI_RO_ATTR,\
+ tricat(rmi_fn_, FNUM, _##propname##_show), \
+ rmi_store_error);
+
+#define store_union_struct_prototype(propname)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
+ struct device *dev,\
+ struct device_attribute *attr,\
+ const char *buf, size_t count);\
+\
+static DEVICE_ATTR(propname, RMI_WO_ATTR,\
+ rmi_show_error,\
+ tricat(rmi_fn_, FNUM, _##propname##_store));
+
+#define show_store_union_struct_prototype(propname)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
+ struct device *dev,\
+ struct device_attribute *attr,\
+ char *buf);\
+\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
+ struct device *dev,\
+ struct device_attribute *attr,\
+ const char *buf, size_t count);\
+\
+static DEVICE_ATTR(propname, RMI_RW_ATTR,\
+ tricat(rmi_fn_, FNUM, _##propname##_show),\
+ tricat(rmi_fn_, FNUM, _##propname##_store));
+
+#define simple_show_union_struct(regtype, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
+ struct device_attribute *attr, char *buf) {\
+ struct rmi_function_container *fc;\
+ struct FUNCTION_DATA *data;\
+\
+ fc = to_rmi_function_container(dev);\
+ data = fc->data;\
+\
+ return snprintf(buf, PAGE_SIZE, fmt,\
+ data->regtype.propname);\
+}
+
+#define simple_show_union_struct2(regtype, reg_group, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
+ struct device_attribute *attr, char *buf) {\
+ struct rmi_function_container *fc;\
+ struct FUNCTION_DATA *data;\
+\
+ fc = to_rmi_function_container(dev);\
+ data = fc->data;\
+\
+ return snprintf(buf, PAGE_SIZE, fmt,\
+ data->regtype.reg_group->propname);\
+}
+
+#define show_union_struct(regtype, reg_group, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(\
+ struct device *dev,\
+ struct device_attribute *attr,\
+ char *buf) {\
+ struct rmi_function_container *fc;\
+ struct FUNCTION_DATA *data;\
+ int result;\
+\
+ fc = to_rmi_function_container(dev);\
+ data = fc->data;\
+\
+ mutex_lock(&data->regtype##_mutex);\
+ /* Read current regtype values */\
+ result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+ (u8 *)data->regtype.reg_group,\
+ sizeof(data->regtype.reg_group->regs));\
+ mutex_unlock(&data->regtype##_mutex);\
+ if (result < 0) {\
+ dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
+ __func__,\
+ data->regtype.reg_group->address);\
+ return result;\
+ } \
+ return snprintf(buf, PAGE_SIZE, fmt,\
+ data->regtype.reg_group->propname);\
+}
+
+#define show_store_union_struct(regtype, reg_group, propname, fmt)\
+show_union_struct(regtype, reg_group, propname, fmt)\
+\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(\
+ struct device *dev,\
+ struct device_attribute *attr,\
+ const char *buf, size_t count) {\
+ int result;\
+ unsigned long val;\
+ unsigned long old_val;\
+ struct rmi_function_container *fc;\
+ struct FUNCTION_DATA *data;\
+\
+ fc = to_rmi_function_container(dev);\
+ data = fc->data;\
+\
+ /* need to convert the string data to an actual value */\
+ result = strict_strtoul(buf, 10, &val);\
+\
+ /* if an error occured, return it */\
+ if (result)\
+ return result;\
+ /* Check value maybe */\
+\
+ /* Read current regtype values */\
+ mutex_lock(&data->regtype##_mutex);\
+ result =\
+ rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+ (u8 *)data->regtype.reg_group,\
+ sizeof(data->regtype.reg_group->regs));\
+\
+ if (result < 0) {\
+ mutex_unlock(&data->regtype##_mutex);\
+ dev_dbg(dev, "%s : Could not read regtype at 0x%x\\n",\
+ __func__,\
+ data->regtype.reg_group->address);\
+ return result;\
+ } \
+ /* if the current regtype registers are already set as we want them,\
+ * do nothing to them */\
+ if (data->regtype.reg_group->propname == val) {\
+ mutex_unlock(&data->regtype##_mutex);\
+ return count;\
+ } \
+ /* Write the regtype back to the regtype register */\
+ old_val = data->regtype.reg_group->propname;\
+ data->regtype.reg_group->propname = val;\
+ result =\
+ rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
+ (u8 *)data->regtype.reg_group,\
+ sizeof(data->regtype.reg_group->regs));\
+ if (result < 0) {\
+ dev_dbg(dev, "%s : Could not write regtype to 0x%x\\n",\
+ __func__,\
+ data->regtype.reg_group->address);\
+ /* revert change to local value if value not written */\
+ data->regtype.reg_group->propname = old_val;\
+ mutex_unlock(&data->regtype##_mutex);\
+ return result;\
+ } \
+ mutex_unlock(&data->regtype##_mutex);\
+ return count;\
+}
+
+
+#define show_repeated_union_struct(regtype, reg_group, propname, fmt)\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
+ struct device_attribute *attr,\
+ char *buf) {\
+ struct rmi_function_container *fc;\
+ struct FUNCTION_DATA *data;\
+ int reg_length;\
+ int result, size = 0;\
+ char *temp;\
+ int i;\
+\
+ fc = to_rmi_function_container(dev);\
+ data = fc->data;\
+ mutex_lock(&data->regtype##_mutex);\
+\
+ /* Read current regtype values */\
+ reg_length = data->regtype.reg_group->length;\
+ result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+ (u8 *) data->regtype.reg_group->regs,\
+ reg_length * sizeof(u8));\
+ mutex_unlock(&data->regtype##_mutex);\
+ if (result < 0) {\
+ dev_dbg(dev, "%s : Could not read regtype at 0x%x\n"\
+ "Data may be outdated.", __func__,\
+ data->regtype.reg_group->address);\
+ } \
+ temp = buf;\
+ for (i = 0; i < reg_length; i++) {\
+ result = snprintf(temp, PAGE_SIZE - size, fmt " ",\
+ data->regtype.reg_group->regs[i].propname);\
+ 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;\
+}
+
+#define show_store_repeated_union_struct(regtype, reg_group, propname, fmt)\
+show_repeated_union_struct(regtype, reg_group, propname, fmt)\
+\
+static ssize_t tricat(rmi_fn_, FNUM, _##propname##_store)(struct device *dev,\
+ struct device_attribute *attr,\
+ const char *buf, size_t count) {\
+ struct rmi_function_container *fc;\
+ struct FUNCTION_DATA *data;\
+ int reg_length;\
+ int result;\
+ const char *temp;\
+ int i;\
+ unsigned int newval;\
+\
+ fc = to_rmi_function_container(dev);\
+ data = fc->data;\
+ mutex_lock(&data->regtype##_mutex);\
+\
+ /* Read current regtype values */\
+\
+ reg_length = data->regtype.reg_group->length;\
+ result = rmi_read_block(fc->rmi_dev, data->regtype.reg_group->address,\
+ (u8 *) data->regtype.reg_group->regs,\
+ reg_length * sizeof(u8));\
+\
+ if (result < 0) {\
+ dev_dbg(dev, "%s: Could not read regtype at %#06x. "\
+ "Data may be outdated.", __func__,\
+ data->regtype.reg_group->address);\
+ } \
+ \
+ /* parse input */\
+ temp = buf;\
+ for (i = 0; i < reg_length; i++) {\
+ if (sscanf(temp, fmt, &newval) == 1) {\
+ data->regtype.reg_group->regs[i].propname = newval;\
+ } else {\
+ /* If we don't read a value for each position, abort, \
+ * restore previous values locally by rereading */\
+ result = rmi_read_block(fc->rmi_dev, \
+ data->regtype.reg_group->address,\
+ (u8 *) data->regtype.reg_group->regs,\
+ reg_length * sizeof(u8));\
+\
+ if (result < 0) {\
+ dev_dbg(dev, "%s: Couldn't read regtype at "\
+ "%#06x. Local data may be inaccurate", \
+ __func__,\
+ data->regtype.reg_group->address);\
+ } \
+ return -EINVAL;\
+ } \
+ /* move to next number */\
+ while (*temp != 0) {\
+ temp++;\
+ if (isspace(*(temp - 1)) && !isspace(*temp))\
+ break;\
+ } \
+ } \
+ result = rmi_write_block(fc->rmi_dev, data->regtype.reg_group->address,\
+ (u8 *) data->regtype.reg_group->regs,\
+ reg_length * sizeof(u8));\
+ mutex_unlock(&data->regtype##_mutex);\
+ if (result < 0) {\
+ dev_dbg(dev, "%s: Could not write new values to %#06x\n", \
+ __func__, data->regtype.reg_group->address);\
+ return result;\
+ } \
+ return count;\
+}
+
+/* Create templates for given types */
+#define simple_show_union_struct_unsigned(regtype, propname)\
+simple_show_union_struct(regtype, propname, "%u\n")
+
+#define simple_show_union_struct_unsigned2(regtype, reg_group, propname)\
+simple_show_union_struct2(regtype, reg_group, propname, "%u\n")
+
+#define show_union_struct_unsigned(regtype, reg_group, propname)\
+show_union_struct(regtype, reg_group, propname, "%u\n")
+
+#define show_store_union_struct_unsigned(regtype, reg_group, propname)\
+show_store_union_struct(regtype, reg_group, propname, "%u\n")
+
+#define show_repeated_union_struct_unsigned(regtype, reg_group, propname)\
+show_repeated_union_struct(regtype, reg_group, propname, "%u")
+
+#define show_store_repeated_union_struct_unsigned(regtype, reg_group, propname)\
+show_store_repeated_union_struct(regtype, reg_group, propname, "%u")
+
+/* Remove access to raw format string versions */
+/*#undef simple_show_union_struct
+#undef show_union_struct_unsigned
+#undef show_store_union_struct
+#undef show_repeated_union_struct
+#undef show_store_repeated_union_struct*/
+
+#endif
+
+#define GROUP(_attrs) { \
+ .attrs = _attrs, \
+}
+
+#define attrify(nm) (&dev_attr_##nm.attr)
+
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+
+union pdt_properties {
+ struct {
+ u8 reserved_1:6;
+ u8 has_bsr:1;
+ u8 reserved_2:1;
+ } __attribute__((__packed__));
+ u8 regs[1];
+};
+
+struct rmi_driver_data {
+ struct rmi_function_container rmi_functions;
+
+ struct rmi_function_container *f01_container;
+ bool f01_bootloader_mode;
+
+ atomic_t attn_count;
+ bool irq_debug;
+ int irq;
+ int irq_flags;
+ int num_of_irq_regs;
+ int irq_count;
+ u8 *current_irq_mask;
+ u8 *irq_mask_store;
+ bool irq_stored;
+ struct mutex irq_mutex;
+ struct mutex pdt_mutex;
+
+ union pdt_properties pdt_props;
+ u8 bsr;
+ bool enabled;
+
+#ifdef CONFIG_PM
+ bool suspended;
+ struct mutex suspend_mutex;
+
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_suspend) (const void *pm_data);
+ int (*pre_resume) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_delay;
+ struct dentry *debugfs_phys;
+ struct dentry *debugfs_reg_ctl;
+ struct dentry *debugfs_reg;
+ struct dentry *debugfs_irq;
+ struct dentry *debugfs_attn_count;
+ u16 reg_debug_addr;
+ u8 reg_debug_size;
+#endif
+
+ void *data;
+};
+
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+
+struct pdt_entry {
+ u8 query_base_addr:8;
+ u8 command_base_addr:8;
+ u8 control_base_addr:8;
+ u8 data_base_addr:8;
+ u8 interrupt_source_count:3;
+ u8 bits3and4:2;
+ u8 function_version:2;
+ u8 bit7:1;
+ u8 function_number:8;
+} __attribute__((__packed__));
+
+static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
+ struct rmi_function_descriptor *fd,
+ u16 page_start)
+{
+ fd->query_base_addr = pdt->query_base_addr + page_start;
+ fd->command_base_addr = pdt->command_base_addr + page_start;
+ fd->control_base_addr = pdt->control_base_addr + page_start;
+ fd->data_base_addr = pdt->data_base_addr + page_start;
+ fd->function_number = pdt->function_number;
+ fd->interrupt_source_count = pdt->interrupt_source_count;
+ fd->function_version = pdt->function_version;
+}
+
+#ifdef CONFIG_RMI4_FWLIB
+extern void rmi4_fw_update(struct rmi_device *rmi_dev,
+ struct pdt_entry *f01_pdt, struct pdt_entry *f34_pdt);
+#else
+#define rmi4_fw_update(rmi_dev, f01_pdt, f34_pdt)
+#endif
+
+#endif
rmi_f11.c is a driver for 2D touch sensors. It has been updated to support
the MT-B specification, partition control attributes between debugfs and sysfs,
and to use the standard bus model for loading/unloading.
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
Cc: Henrik Rydberg <[email protected]>
---
drivers/input/rmi4/rmi_f11.c | 2727 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 2727 insertions(+), 0 deletions(-)
diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
new file mode 100644
index 0000000..bba818b
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -0,0 +1,2727 @@
+/*
+ * Copyright (c) 2011,2012 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * 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 f11_data
+#define FNUM 11
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/kconfig.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+#ifdef CONFIG_RMI4_DEBUG
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#endif
+
+#define F11_MAX_NUM_OF_SENSORS 8
+#define F11_MAX_NUM_OF_FINGERS 10
+#define F11_MAX_NUM_OF_TOUCH_SHAPES 16
+
+#define F11_REL_POS_MIN -128
+#define F11_REL_POS_MAX 127
+
+#define FINGER_STATE_MASK 0x03
+#define GET_FINGER_STATE(f_states, i) \
+ ((f_states[i / 4] >> (2 * (i % 4))) & FINGER_STATE_MASK)
+
+#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
+#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
+
+#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
+#define INBOX(x, y, box) (x >= box.x && x < (box.x + box.width) \
+ && y >= box.y && y < (box.y + box.height))
+
+#define DEFAULT_XY_MAX 9999
+#define DEFAULT_MAX_ABS_MT_PRESSURE 255
+#define DEFAULT_MAX_ABS_MT_TOUCH 15
+#define DEFAULT_MAX_ABS_MT_ORIENTATION 1
+#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1
+#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10
+#define MAX_NAME_LENGTH 256
+
+static ssize_t f11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t f11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t f11_maxPos_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static void rmi_f11_free_memory(struct rmi_function_container *fc);
+
+static int rmi_f11_initialize(struct rmi_function_container *fc);
+
+static int rmi_f11_create_sysfs(struct rmi_function_container *fc);
+
+static int rmi_f11_config(struct rmi_function_container *fc);
+
+static int rmi_f11_register_devices(struct rmi_function_container *fc);
+
+static void rmi_f11_free_devices(struct rmi_function_container *fc);
+
+static void f11_set_abs_params(struct rmi_function_container *fc, int index);
+
+static struct device_attribute attrs[] = {
+ __ATTR(relreport, RMI_RW_ATTR, f11_relreport_show, f11_relreport_store),
+ __ATTR(maxPos, RMI_RO_ATTR, f11_maxPos_show, rmi_store_error),
+ __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, f11_rezero_store)
+};
+
+/**
+ * @rezero - writing 1 to this will cause the sensor to calibrate to the
+ * current capacitive state.
+ */
+union f11_2d_commands {
+ struct {
+ bool rezero:1;
+ u8 reserved:7;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @nbr_of_sensors - the number of 2D sensors on the touch device.
+ * @has_query9 - indicates the F11_2D_Query9 register exists.
+ * @has_query11 - indicates the F11_2D_Query11 register exists.
+ * @has_z_tuning - if set, the sensor supports Z tuning and registers
+ * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist.
+ * @has_pos_interpolation_tuning - TBD
+ * @has_w_tuning - the sensor supports Wx and Wy scaling and registers
+ * F11_2D_Ctrl36 through F11_2D_Ctrl39 exist.
+ * @has_pitch_info - the X and Y pitches of the sensor electrodes can be
+ * configured and registers F11_2D_Ctrl40 and F11_2D_Ctrl41 exist.
+ * @has_default_finger_width - the default finger width settings for the
+ * sensor can be configured and registers F11_2D_Ctrl42 through F11_2D_Ctrl44
+ * exist.
+ * @has_segmentation_aggressiveness - the sensor’s ability to distinguish
+ * multiple objects close together can be configured and register F11_2D_Ctrl45
+ * exists.
+ * @has_tx_rw_clip - the inactive outside borders of the sensor can be
+ * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist.
+ * @has_drumming_correction - the sensor can be configured to distinguish
+ * between a fast flick and a quick drumming movement and registers
+ * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist.
+ */
+struct f11_2d_device_query {
+ union {
+ struct {
+ u8 nbr_of_sensors:3;
+ bool has_query9:1;
+ bool has_query11:1;
+ u8 reserved:3;
+ } __attribute__((__packed__));
+ u8 f11_2d_query0;
+ };
+
+ union {
+ struct {
+ bool has_z_tuning:1;
+ bool has_pos_interpolation_tuning:1;
+ bool has_w_tuning:1;
+ bool has_pitch_info:1;
+ bool has_default_finger_width:1;
+ bool has_segmentation_aggressiveness:1;
+ bool has_tx_rw_clip:1;
+ bool has_drumming_correction:1;
+ } __attribute__((__packed__));
+ u8 f11_2d_query11;
+ };
+};
+
+/**
+ * @has_pen - detection of a stylus is supported and registers F11_2D_Ctrl20
+ * and F11_2D_Ctrl21 exist.
+ * @has_proximity - detection of fingers near the sensor is supported and
+ * registers F11_2D_Ctrl22 through F11_2D_Ctrl26 exist.
+ * @has_palm_det_sensitivity - the sensor supports the palm detect sensitivity
+ * feature and register F11_2D_Ctrl27 exists.
+ * @has_two_pen_thresholds - is has_pen is also set, then F11_2D_Ctrl35 exists.
+ * @has_contact_geometry - the sensor supports the use of contact geometry to
+ * map absolute X and Y target positions and registers F11_2D_Data18.* through
+ * F11_2D_Data27 exist.
+ */
+union f11_2d_query9 {
+ struct {
+ bool has_pen:1;
+ bool has_proximity:1;
+ bool has_palm_det_sensitivity:1;
+ bool has_suppress_on_palm_detect:1;
+ bool has_two_pen_thresholds:1;
+ bool has_contact_geometry:1;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @number_of_fingers - describes the maximum number of fingers the 2-D sensor
+ * supports.
+ * @has_rel - the sensor supports relative motion reporting.
+ * @has_abs - the sensor supports absolute poition reporting.
+ * @has_gestures - the sensor supports gesture reporting.
+ * @has_sensitivity_adjust - the sensor supports a global sensitivity
+ * adjustment.
+ * @configurable - the sensor supports various configuration options.
+ * @num_of_x_electrodes - the maximum number of electrodes the 2-D sensor
+ * supports on the X axis.
+ * @num_of_y_electrodes - the maximum number of electrodes the 2-D sensor
+ * supports on the Y axis.
+ * @max_electrodes - the total number of X and Y electrodes that may be
+ * configured.
+ * @abs_data_size - describes the format of data reported by the absolute
+ * data source. Only one format (the kind used here) is supported at this
+ * time.
+ * @has_anchored_finger - then the sensor supports the high-precision second
+ * finger tracking provided by the manual tracking and motion sensitivity
+ * options.
+ * @has_adjust_hyst - the difference between the finger release threshold and
+ * the touch threshold.
+ * @has_dribble - the sensor supports the generation of dribble interrupts,
+ * which may be enabled or disabled with the dribble control bit.
+ * @f11_2d_query6 - reserved.
+ * @has_single_tap - a basic single-tap gesture is supported.
+ * @has_tap_n_hold - tap-and-hold gesture is supported.
+ * @has_double_tap - double-tap gesture is supported.
+ * @has_early_tap - early tap is supported and reported as soon as the finger
+ * lifts for any tap event that could be interpreted as either a single tap
+ * or as the first tap of a double-tap or tap-and-hold gesture.
+ * @has_flick - flick detection is supported.
+ * @has_press - press gesture reporting is supported.
+ * @has_pinch - pinch gesture detection is supported.
+ * @has_palm_det - the 2-D sensor notifies the host whenever a large conductive
+ * object such as a palm or a cheek touches the 2-D sensor.
+ * @has_rotate - rotation gesture detection is supported.
+ * @has_touch_shapes - TouchShapes are supported. A TouchShape is a fixed
+ * rectangular area on the sensor that behaves like a capacitive button.
+ * @has_scroll_zones - scrolling areas near the sensor edges are supported.
+ * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported;
+ * if 0, then only two are supported.
+ * @has_multi_finger_scroll - the multifinger_scrolling bit will be set when
+ * more than one finger is involved in a scrolling action.
+ * @nbr_touch_shapes - the total number of touch shapes supported.
+ */
+struct f11_2d_sensor_query {
+ union {
+ struct {
+ /* query1 */
+ u8 number_of_fingers:3;
+ bool has_rel:1;
+ bool has_abs:1;
+ bool has_gestures:1;
+ bool has_sensitivity_adjust:1;
+ bool configurable:1;
+ /* query2 */
+ u8 num_of_x_electrodes:7;
+ u8 reserved_1:1;
+ /* query3 */
+ u8 num_of_y_electrodes:7;
+ u8 reserved_2:1;
+ /* query4 */
+ u8 max_electrodes:7;
+ u8 reserved_3:1;
+ } __attribute__((__packed__));
+ u8 f11_2d_query1__4[4];
+ };
+
+ union {
+ struct {
+ u8 abs_data_size:3;
+ bool has_anchored_finger:1;
+ bool has_adj_hyst:1;
+ bool has_dribble:1;
+ u8 reserved_4:2;
+ } __attribute__((__packed__));
+ u8 f11_2d_query5;
+ };
+
+ u8 f11_2d_query6;
+
+ union {
+ struct {
+ bool has_single_tap:1;
+ bool has_tap_n_hold:1;
+ bool has_double_tap:1;
+ bool has_early_tap:1;
+ bool has_flick:1;
+ bool has_press:1;
+ bool has_pinch:1;
+ bool padding:1;
+
+ bool has_palm_det:1;
+ bool has_rotate:1;
+ bool has_touch_shapes:1;
+ bool has_scroll_zones:1;
+ bool has_individual_scroll_zones:1;
+ bool has_multi_finger_scroll:1;
+ } __attribute__((__packed__));
+ u8 f11_2d_query7__8[2];
+ };
+
+ union f11_2d_query9 query9;
+
+ union {
+ struct {
+ u8 nbr_touch_shapes:5;
+ } __attribute__((__packed__));
+ u8 f11_2d_query10;
+ };
+};
+
+/**
+ * @reporting_mode - controls how often finger position data is reported.
+ * @abs_pos_filt - when set, enables various noise and jitter filtering
+ * algorithms for absolute reports.
+ * @rel_pos_filt - when set, enables various noise and jitter filtering
+ * algorithms for relative reports.
+ * @rel_ballistics - enables ballistics processing for the relative finger
+ * motion on the 2-D sensor.
+ * @dribble - enables the dribbling feature.
+ * @report_beyond_clip - when this is set, fingers outside the active area
+ * specified by the x_clip and y_clip registers will be reported, but with
+ * reported finger position clipped to the edge of the active area.
+ * @palm_detect_thresh - the threshold at which a wide finger is considered a
+ * palm. A value of 0 inhibits palm detection.
+ * @motion_sensitivity - specifies the threshold an anchored finger must move
+ * before it is considered no longer anchored. High values mean more
+ * sensitivity.
+ * @man_track_en - for anchored finger tracking, whether the host (1) or the
+ * device (0) determines which finger is the tracked finger.
+ * @man_tracked_finger - when man_track_en is 1, specifies whether finger 0 or
+ * finger 1 is the tracked finger.
+ * @delta_x_threshold - 2-D position update interrupts are inhibited unless
+ * the finger moves more than a certain threshold distance along the X axis.
+ * @delta_y_threshold - 2-D position update interrupts are inhibited unless
+ * the finger moves more than a certain threshold distance along the Y axis.
+ * @velocity - When rel_ballistics is set, this register defines the
+ * velocity ballistic parameter applied to all relative motion events.
+ * @acceleration - When rel_ballistics is set, this register defines the
+ * acceleration ballistic parameter applied to all relative motion events.
+ * @sensor_max_x_pos - the maximum X coordinate reported by the sensor.
+ * @sensor_max_y_pos - the maximum Y coordinate reported by the sensor.
+ */
+union f11_2d_ctrl0_9 {
+ struct {
+ /* F11_2D_Ctrl0 */
+ u8 reporting_mode:3;
+ bool abs_pos_filt:1;
+ bool rel_pos_filt:1;
+ bool rel_ballistics:1;
+ bool dribble:1;
+ bool report_beyond_clip:1;
+ /* F11_2D_Ctrl1 */
+ u8 palm_detect_thres:4;
+ u8 motion_sensitivity:2;
+ bool man_track_en:1;
+ bool man_tracked_finger:1;
+ /* F11_2D_Ctrl2 and 3 */
+ u8 delta_x_threshold:8;
+ u8 delta_y_threshold:8;
+ /* F11_2D_Ctrl4 and 5 */
+ u8 velocity:8;
+ u8 acceleration:8;
+ /* F11_2D_Ctrl6 thru 9 */
+ u16 sensor_max_x_pos:12;
+ u8 ctrl7_reserved:4;
+ u16 sensor_max_y_pos:12;
+ u8 ctrl9_reserved:4;
+ } __attribute__((__packed__));
+ struct {
+ u8 regs[10];
+ u16 address;
+ } __attribute__((__packed__));
+};
+
+/**
+ * @single_tap_int_enable - enable tap gesture recognition.
+ * @tap_n_hold_int_enable - enable tap-and-hold gesture recognition.
+ * @double_tap_int_enable - enable double-tap gesture recognition.
+ * @early_tap_int_enable - enable early tap notification.
+ * @flick_int_enable - enable flick detection.
+ * @press_int_enable - enable press gesture recognition.
+ * @pinch_int_enable - enable pinch detection.
+ */
+union f11_2d_ctrl10 {
+ struct {
+ bool single_tap_int_enable:1;
+ bool tap_n_hold_int_enable:1;
+ bool double_tap_int_enable:1;
+ bool early_tap_int_enable:1;
+ bool flick_int_enable:1;
+ bool press_int_enable:1;
+ bool pinch_int_enable:1;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @palm_detect_int_enable - enable palm detection feature.
+ * @rotate_int_enable - enable rotate gesture detection.
+ * @touch_shape_int_enable - enable the TouchShape feature.
+ * @scroll_zone_int_enable - enable scroll zone reporting.
+ * @multi_finger_scroll_int_enable - enable the multfinger scroll feature.
+ */
+union f11_2d_ctrl11 {
+ struct {
+ bool palm_detect_int_enable:1;
+ bool rotate_int_enable:1;
+ bool touch_shape_int_enable:1;
+ bool scroll_zone_int_enable:1;
+ bool multi_finger_scroll_int_enable:1;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+union f11_2d_ctrl12 {
+ struct {
+ u8 sensor_map:7;
+ bool xy_sel:1;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @sens_adjustment - allows a host to alter the overall sensitivity of a
+ * 2-D sensor. A positive value in this register will make the sensor more
+ * sensitive than the factory defaults, and a negative value will make it
+ * less sensitive.
+ * @hyst_adjustment - increase the touch/no-touch hysteresis by 2 Z-units for
+ * each one unit increment in this setting.
+ */
+union f11_2d_ctrl14 {
+ struct {
+ s8 sens_adjustment:5;
+ u8 hyst_adjustment:3;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @max_tap_time - the maximum duration of a tap, in 10-millisecond units.
+ */
+union f11_2d_ctrl15 {
+ struct {
+ u8 max_tap_time:8;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @min_press_time - The minimum duration required for stationary finger(s) to
+ * generate a press gesture, in 10-millisecond units.
+ */
+union f11_2d_ctrl16 {
+ struct {
+ u8 min_press_time:8;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @max_tap_distance - Determines the maximum finger movement allowed during
+ * a tap, in 0.1-millimeter units.
+ */
+union f11_2d_ctrl17 {
+ struct {
+ u8 max_tap_distance:8;
+ } __attribute__((__packed__));
+ u8 reg;
+};
+
+/**
+ * @min_flick_distance - the minimum finger movement for a flick gesture,
+ * in 1-millimeter units.
+ * @min_flick_speed - the minimum finger speed for a flick gesture, in
+ * 10-millimeter/second units.
+ */
+union f11_2d_ctrl18_19 {
+ struct {
+ u8 min_flick_distance:8;
+ u8 min_flick_speed:8;
+ } __attribute__((__packed__));
+ u8 reg[2];
+};
+
+/**
+ * @pen_detect_enable - enable reporting of stylus activity.
+ * @pen_jitter_filter_enable - Setting this enables the stylus anti-jitter
+ * filter.
+ * @pen_z_threshold - This is the stylus-detection lower threshold. Smaller
+ * values result in higher sensitivity.
+ */
+union f11_2d_ctrl20_21 {
+ struct {
+ bool pen_detect_enable:1;
+ bool pen_jitter_filter_enable:1;
+ u8 ctrl20_reserved:6;
+ u8 pen_z_threshold:8;
+ } __attribute__((__packed__));
+ u8 reg[2];
+};
+
+/**
+ * These are not accessible through sysfs yet.
+ *
+ * @proximity_detect_int_en - enable proximity detection feature.
+ * @proximity_jitter_filter_en - enables an anti-jitter filter on proximity
+ * data.
+ * @proximity_detection_z_threshold - the threshold for finger-proximity
+ * detection.
+ * @proximity_delta_x_threshold - In reduced-reporting modes, this is the
+ * threshold for proximate-finger movement in the direction parallel to the
+ * X-axis.
+ * @proximity_delta_y_threshold - In reduced-reporting modes, this is the
+ * threshold for proximate-finger movement in the direction parallel to the
+ * Y-axis.
+ * * @proximity_delta_Z_threshold - In reduced-reporting modes, this is the
+ * threshold for proximate-finger movement in the direction parallel to the
+ * Z-axis.
+ */
+union f11_2d_ctrl22_26 {
+ struct {
+ /* control 22 */
+ bool proximity_detect_int_en:1;
+ bool proximity_jitter_filter_en:1;
+ u8 f11_2d_ctrl6_b3__7:6;
+
+ /* control 23 */
+ u8 proximity_detection_z_threshold;
+
+ /* control 24 */
+ u8 proximity_delta_x_threshold;
+
+ /* control 25 */
+ u8 proximity_delta_y_threshold;
+
+ /* control 26 */
+ u8 proximity_delta_z_threshold;
+ } __attribute__((__packed__));
+ u8 regs[5];
+};
+
+/**
+ * @palm_detecy_sensitivity - When this value is small, smaller objects will
+ * be identified as palms; when this value is large, only larger objects will
+ * be identified as palms. 0 represents the factory default.
+ * @suppress_on_palm_detect - when set, all F11 interrupts except palm_detect
+ * are suppressed while a palm is detected.
+ */
+union f11_2d_ctrl27 {
+ struct {
+ s8 palm_detect_sensitivity:4;
+ bool suppress_on_palm_detect:1;
+ u8 f11_2d_ctrl27_b5__7:3;
+ } __attribute__((__packed__));
+ u8 regs[1];
+};
+
+/**
+ * @multi_finger_scroll_mode - allows choice of multi-finger scroll mode and
+ * determines whether and how X or Y displacements are reported.
+ * @edge_motion_en - enables the edge_motion feature.
+ * @multi_finger_scroll_momentum - controls the length of time that scrolling
+ * continues after fingers have been lifted.
+ */
+union f11_2d_ctrl28 {
+ struct {
+ u8 multi_finger_scroll_mode:2;
+ bool edge_motion_en:1;
+ bool f11_2d_ctrl28b_3:1;
+ u8 multi_finger_scroll_momentum:4;
+ } __attribute__((__packed__));
+ u8 regs[1];
+};
+
+/**
+ * @z_touch_threshold - Specifies the finger-arrival Z threshold. Large values
+ * may cause smaller fingers to be rejected.
+ * @z_touch_hysteresis - Specifies the difference between the finger-arrival
+ * Z threshold and the finger-departure Z threshold.
+ */
+union f11_2d_ctrl29_30 {
+ struct {
+ u8 z_touch_threshold;
+ u8 z_touch_hysteresis;
+ } __attribute__((__packed__));
+ struct {
+ u8 regs[2];
+ u16 address;
+ } __attribute__((__packed__));
+};
+
+
+struct f11_2d_ctrl {
+ union f11_2d_ctrl0_9 *ctrl0_9;
+ union f11_2d_ctrl10 *ctrl10;
+ union f11_2d_ctrl11 *ctrl11;
+ union f11_2d_ctrl12 *ctrl12;
+ u8 ctrl12_size;
+ union f11_2d_ctrl14 *ctrl14;
+ union f11_2d_ctrl15 *ctrl15;
+ union f11_2d_ctrl16 *ctrl16;
+ union f11_2d_ctrl17 *ctrl17;
+ union f11_2d_ctrl18_19 *ctrl18_19;
+ union f11_2d_ctrl20_21 *ctrl20_21;
+ union f11_2d_ctrl22_26 *ctrl22_26;
+ union f11_2d_ctrl27 *ctrl27;
+ union f11_2d_ctrl28 *ctrl28;
+ union f11_2d_ctrl29_30 *ctrl29_30;
+};
+
+/**
+ * @x_msb - top 8 bits of X finger position.
+ * @y_msb - top 8 bits of Y finger position.
+ * @x_lsb - bottom 4 bits of X finger position.
+ * @y_lsb - bottom 4 bits of Y finger position.
+ * @w_y - contact patch width along Y axis.
+ * @w_x - contact patch width along X axis.
+ * @z - finger Z value (proxy for pressure).
+ */
+struct f11_2d_data_1_5 {
+ u8 x_msb;
+ u8 y_msb;
+ u8 x_lsb:4;
+ u8 y_lsb:4;
+ u8 w_y:4;
+ u8 w_x:4;
+ u8 z;
+};
+
+/**
+ * @delta_x - relative motion along X axis.
+ * @delta_y - relative motion along Y axis.
+ */
+struct f11_2d_data_6_7 {
+ s8 delta_x;
+ s8 delta_y;
+};
+
+/**
+ * @single_tap - a single tap was recognized.
+ * @tap_and_hold - a tap-and-hold gesture was recognized.
+ * @double_tap - a double tap gesture was recognized.
+ * @early_tap - a tap gesture might be happening.
+ * @flick - a flick gesture was detected.
+ * @press - a press gesture was recognized.
+ * @pinch - a pinch gesture was detected.
+ */
+struct f11_2d_data_8 {
+ bool single_tap:1;
+ bool tap_and_hold:1;
+ bool double_tap:1;
+ bool early_tap:1;
+ bool flick:1;
+ bool press:1;
+ bool pinch:1;
+};
+
+/**
+ * @palm_detect - a palm or other large object is in contact with the sensor.
+ * @rotate - a rotate gesture was detected.
+ * @shape - a TouchShape has been activated.
+ * @scrollzone - scrolling data is available.
+ * @finger_count - number of fingers involved in the reported gesture.
+ */
+struct f11_2d_data_9 {
+ bool palm_detect:1;
+ bool rotate:1;
+ bool shape:1;
+ bool scrollzone:1;
+ u8 finger_count:3;
+};
+
+/**
+ * @pinch_motion - when a pinch gesture is detected, this is the change in
+ * distance between the two fingers since this register was last read.
+ */
+struct f11_2d_data_10 {
+ s8 pinch_motion;
+};
+
+/**
+ * @x_flick_dist - when a flick gesture is detected, the distance of flick
+ * gesture in X direction.
+ * @y_flick_dist - when a flick gesture is detected, the distance of flick
+ * gesture in Y direction.
+ * @flick_time - the total time of the flick gesture, in 10ms units.
+ */
+struct f11_2d_data_10_12 {
+ s8 x_flick_dist;
+ s8 y_flick_dist;
+ u8 flick_time;
+};
+
+/**
+ * @motion - when a rotate gesture is detected, the accumulated distance
+ * of the rotate motion. Clockwise motion is positive and counterclockwise
+ * motion is negative.
+ * @finger_separation - when a rotate gesture is detected, the distance
+ * between the fingers.
+ */
+struct f11_2d_data_11_12 {
+ s8 motion;
+ u8 finger_separation;
+};
+
+/**
+ * @shape_n - a bitmask of the currently activate TouchShapes (if any).
+ */
+struct f11_2d_data_13 {
+ u8 shape_n;
+};
+
+/**
+ * @horizontal - chiral scrolling distance in the X direction.
+ * @vertical - chiral scrolling distance in the Y direction.
+ */
+struct f11_2d_data_14_15 {
+ s8 horizontal;
+ s8 vertical;
+};
+
+/**
+ * @x_low - scroll zone motion along the lower edge of the sensor.
+ * @y_right - scroll zone motion along the right edge of the sensor.
+ * @x_upper - scroll zone motion along the upper edge of the sensor.
+ * @y_left - scroll zone motion along the left edge of the sensor.
+ */
+struct f11_2d_data_14_17 {
+ s8 x_low;
+ s8 y_right;
+ s8 x_upper;
+ s8 y_left;
+};
+
+struct f11_2d_data {
+ u8 *f_state;
+ const struct f11_2d_data_1_5 *abs_pos;
+ const struct f11_2d_data_6_7 *rel_pos;
+ const struct f11_2d_data_8 *gest_1;
+ const struct f11_2d_data_9 *gest_2;
+ const struct f11_2d_data_10 *pinch;
+ const struct f11_2d_data_10_12 *flick;
+ const struct f11_2d_data_11_12 *rotate;
+ const struct f11_2d_data_13 *shapes;
+ const struct f11_2d_data_14_15 *multi_scroll;
+ const struct f11_2d_data_14_17 *scroll_zones;
+};
+
+struct f11_2d_sensor {
+ struct rmi_f11_2d_axis_alignment axis_align;
+ struct f11_2d_sensor_query sens_query;
+ struct f11_2d_data data;
+ int prev_x[F11_MAX_NUM_OF_FINGERS];
+ int prev_y[F11_MAX_NUM_OF_FINGERS];
+ u16 max_x;
+ u16 max_y;
+ u8 nbr_fingers;
+ u8 finger_tracker[F11_MAX_NUM_OF_FINGERS];
+ u8 *data_pkt;
+ int pkt_size;
+ u8 sensor_index;
+ u8 *button_map;
+ struct rmi_f11_virtualbutton_map virtual_buttons;
+ bool type_a;
+ char input_name[MAX_NAME_LENGTH];
+ char input_phys[MAX_NAME_LENGTH];
+ struct input_dev *input;
+ struct input_dev *mouse_input;
+ struct rmi_function_container *fc;
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_flip;
+ struct dentry *debugfs_clip;
+ struct dentry *debugfs_delta_threshold;
+ struct dentry *debugfs_offset;
+ struct dentry *debugfs_swap;
+ struct dentry *debugfs_type_a;
+#endif
+};
+
+struct f11_data {
+ struct f11_2d_device_query dev_query;
+ struct f11_2d_ctrl dev_controls;
+ struct mutex dev_controls_mutex;
+ u16 rezero_wait_ms;
+ struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
+
+#ifdef CONFIG_RMI4_DEBUG
+ struct dentry *debugfs_rezero_wait;
+#endif
+};
+
+enum finger_state_values {
+ F11_NO_FINGER = 0x00,
+ F11_PRESENT = 0x01,
+ F11_INACCURATE = 0x02,
+ F11_RESERVED = 0x03
+};
+
+/* ctrl sysfs files */
+show_store_union_struct_prototype(abs_pos_filt)
+show_store_union_struct_prototype(z_touch_threshold)
+show_store_union_struct_prototype(z_touch_hysteresis)
+
+#ifdef CONFIG_RMI4_DEBUG
+
+struct sensor_debugfs_data {
+ bool done;
+ struct f11_2d_sensor *sensor;
+};
+
+static int sensor_debug_open(struct inode *inodep, struct file *filp)
+{
+ struct sensor_debugfs_data *data;
+ struct f11_2d_sensor *sensor = inodep->i_private;
+ struct rmi_function_container *fc = sensor->fc;
+
+ data = devm_kzalloc(&fc->dev, sizeof(struct sensor_debugfs_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->sensor = sensor;
+ filp->private_data = data;
+ return 0;
+}
+
+static ssize_t flip_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u\n",
+ data->sensor->axis_align.flip_x,
+ data->sensor->axis_align.flip_y);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t flip_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ unsigned int new_X;
+ unsigned int new_Y;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+ if (retval != 2 || new_X > 1 || new_Y > 1)
+ return -EINVAL;
+
+ data->sensor->axis_align.flip_x = new_X;
+ data->sensor->axis_align.flip_y = new_Y;
+
+ return size;
+}
+
+static const struct file_operations flip_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .read = flip_read,
+ .write = flip_write,
+};
+
+
+static ssize_t delta_threshold_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct sensor_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->sensor->fc->data;
+ struct f11_2d_ctrl *ctrl = &f11->dev_controls;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u\n",
+ ctrl->ctrl0_9->delta_x_threshold,
+ ctrl->ctrl0_9->delta_y_threshold);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t delta_threshold_write(struct file *filp,
+ const char __user *buffer, size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ unsigned int new_X, new_Y;
+ u8 save_X, save_Y;
+ int rc;
+ struct sensor_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->sensor->fc->data;
+ struct f11_2d_ctrl *ctrl = &f11->dev_controls;
+ struct rmi_device *rmi_dev = data->sensor->fc->rmi_dev;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+ if (retval != 2 || new_X > 1 || new_Y > 1)
+ return -EINVAL;
+
+ save_X = ctrl->ctrl0_9->delta_x_threshold;
+ save_Y = ctrl->ctrl0_9->delta_y_threshold;
+
+ ctrl->ctrl0_9->delta_x_threshold = new_X;
+ ctrl->ctrl0_9->delta_y_threshold = new_Y;
+ rc = rmi_write_block(rmi_dev,
+ ctrl->ctrl0_9->address,
+ ctrl->ctrl0_9->regs,
+ sizeof(ctrl->ctrl0_9->regs));
+ if (rc < 0) {
+ dev_warn(&data->sensor->fc->dev,
+ "Failed to write to delta_threshold. Code: %d.\n",
+ rc);
+ ctrl->ctrl0_9->delta_x_threshold = save_X;
+ ctrl->ctrl0_9->delta_y_threshold = save_Y;
+ }
+ return size;
+}
+
+static const struct file_operations delta_threshold_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .read = delta_threshold_read,
+ .write = delta_threshold_write,
+};
+
+static ssize_t offset_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+ retval = snprintf(local_buf, size, "%u %u\n",
+ data->sensor->axis_align.offset_X,
+ data->sensor->axis_align.offset_Y);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t offset_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char local_buf[size];
+ int new_X;
+ int new_Y;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+ retval = sscanf(local_buf, "%u %u", &new_X, &new_Y);
+ if (retval != 2)
+ return -EINVAL;
+
+ data->sensor->axis_align.offset_X = new_X;
+ data->sensor->axis_align.offset_Y = new_Y;
+
+ return size;
+}
+
+static const struct file_operations offset_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .read = offset_read,
+ .write = offset_write,
+};
+
+static ssize_t clip_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u %u %u %u\n",
+ data->sensor->axis_align.clip_X_low,
+ data->sensor->axis_align.clip_X_high,
+ data->sensor->axis_align.clip_Y_low,
+ data->sensor->axis_align.clip_Y_high);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t clip_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char local_buf[size];
+ unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+
+ retval = sscanf(local_buf, "%u %u %u %u",
+ &new_X_low, &new_X_high, &new_Y_low, &new_Y_high);
+ if (retval != 4)
+ return -EINVAL;
+
+ if (new_X_low >= new_X_high || new_Y_low >= new_Y_high)
+ return -EINVAL;
+
+ data->sensor->axis_align.clip_X_low = new_X_low;
+ data->sensor->axis_align.clip_X_high = new_X_high;
+ data->sensor->axis_align.clip_Y_low = new_Y_low;
+ data->sensor->axis_align.clip_Y_high = new_Y_high;
+
+ return size;
+}
+
+static const struct file_operations clip_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .read = clip_read,
+ .write = clip_write,
+};
+
+static ssize_t swap_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n",
+ data->sensor->axis_align.swap_axes);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t swap_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char local_buf[size];
+ int new_value;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+ retval = sscanf(local_buf, "%u", &new_value);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ data->sensor->axis_align.swap_axes = new_value;
+ return size;
+}
+
+static const struct file_operations swap_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .read = swap_read,
+ .write = swap_write,
+};
+
+static ssize_t type_a_read(struct file *filp, char __user *buffer, size_t size,
+ loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n",
+ data->sensor->type_a);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t type_a_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char local_buf[size];
+ int new_value;
+ struct sensor_debugfs_data *data = filp->private_data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+ retval = sscanf(local_buf, "%u", &new_value);
+ if (retval != 1 || new_value > 1)
+ return -EINVAL;
+
+ data->sensor->type_a = new_value;
+ return size;
+}
+
+static const struct file_operations type_a_fops = {
+ .owner = THIS_MODULE,
+ .open = sensor_debug_open,
+ .read = type_a_read,
+ .write = type_a_write,
+};
+
+
+static int setup_sensor_debugfs(struct f11_2d_sensor *sensor)
+{
+ int retval = 0;
+ char fname[MAX_NAME_LENGTH];
+ struct rmi_function_container *fc = sensor->fc;
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+
+ if (!fc->debugfs_root)
+ return -ENODEV;
+
+ retval = snprintf(fname, MAX_NAME_LENGTH, "flip.%d",
+ sensor->sensor_index);
+ sensor->debugfs_flip = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &flip_fops);
+ if (!sensor->debugfs_flip)
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, MAX_NAME_LENGTH, "clip.%d",
+ sensor->sensor_index);
+ sensor->debugfs_clip = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &clip_fops);
+ if (!sensor->debugfs_clip)
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, MAX_NAME_LENGTH, "delta_threshold.%d",
+ sensor->sensor_index);
+ sensor->debugfs_clip = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor,
+ &delta_threshold_fops);
+ if (!sensor->debugfs_delta_threshold)
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, MAX_NAME_LENGTH, "offset.%d",
+ sensor->sensor_index);
+ sensor->debugfs_offset = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &offset_fops);
+ if (!sensor->debugfs_offset)
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, MAX_NAME_LENGTH, "swap.%d",
+ sensor->sensor_index);
+ sensor->debugfs_swap = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &swap_fops);
+ if (!sensor->debugfs_swap)
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ retval = snprintf(fname, MAX_NAME_LENGTH, "type_a.%d",
+ sensor->sensor_index);
+ sensor->debugfs_type_a = debugfs_create_file(fname, RMI_RW_ATTR,
+ fc->debugfs_root, sensor, &type_a_fops);
+ if (!sensor->debugfs_type_a)
+ dev_warn(&rmi_dev->dev, "Failed to create debugfs %s.\n",
+ fname);
+
+ return retval;
+}
+
+static void teardown_sensor_debugfs(struct f11_2d_sensor *sensor)
+{
+ if (sensor->debugfs_flip)
+ debugfs_remove(sensor->debugfs_flip);
+
+ if (sensor->debugfs_clip)
+ debugfs_remove(sensor->debugfs_clip);
+
+ if (sensor->debugfs_offset)
+ debugfs_remove(sensor->debugfs_offset);
+
+ if (sensor->debugfs_swap)
+ debugfs_remove(sensor->debugfs_swap);
+
+ if (sensor->debugfs_type_a)
+ debugfs_remove(sensor->debugfs_type_a);
+}
+
+struct f11_debugfs_data {
+ bool done;
+ struct rmi_function_container *fc;
+};
+
+static int f11_debug_open(struct inode *inodep, struct file *filp)
+{
+ struct f11_debugfs_data *data;
+ struct rmi_function_container *fc = inodep->i_private;
+
+ data = devm_kzalloc(&fc->dev, sizeof(struct f11_debugfs_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->fc = fc;
+ filp->private_data = data;
+ return 0;
+}
+
+static ssize_t rezero_wait_read(struct file *filp, char __user *buffer,
+ size_t size, loff_t *offset) {
+ int retval;
+ char local_buf[size];
+ struct f11_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->fc->data;
+
+ if (data->done)
+ return 0;
+
+ data->done = 1;
+
+ retval = snprintf(local_buf, size, "%u\n", f11->rezero_wait_ms);
+
+ if (retval <= 0 || copy_to_user(buffer, local_buf, retval))
+ return -EFAULT;
+
+ return retval;
+}
+
+static ssize_t rezero_wait_write(struct file *filp, const char __user *buffer,
+ size_t size, loff_t *offset)
+{
+ int retval;
+ char local_buf[size];
+ int new_value;
+ struct f11_debugfs_data *data = filp->private_data;
+ struct f11_data *f11 = data->fc->data;
+
+ retval = copy_from_user(local_buf, buffer, size);
+ if (retval)
+ return -EFAULT;
+ retval = sscanf(local_buf, "%u", &new_value);
+ if (retval != 1 || new_value > 65535)
+ return -EINVAL;
+
+ f11->rezero_wait_ms = new_value;
+ return size;
+}
+
+static const struct file_operations rezero_wait_fops = {
+ .owner = THIS_MODULE,
+ .open = f11_debug_open,
+ .read = rezero_wait_read,
+ .write = rezero_wait_write,
+};
+
+static int setup_f11_debugfs(struct rmi_function_container *fc)
+{
+ struct f11_data *f11 = fc->data;
+
+ if (!fc->debugfs_root)
+ return -ENODEV;
+
+ f11->debugfs_rezero_wait = debugfs_create_file("rezero_wait",
+ RMI_RW_ATTR, fc->debugfs_root, fc, &rezero_wait_fops);
+ if (!f11->debugfs_rezero_wait)
+ dev_warn(&fc->dev,
+ "Failed to create debugfs rezero_wait.\n");
+
+ return 0;
+}
+
+static void teardown_f11_debugfs(struct f11_data *f11)
+{
+ if (f11->debugfs_rezero_wait)
+ debugfs_remove(f11->debugfs_rezero_wait);
+}
+#endif
+/* End adding debugfs */
+
+/* This is a group in case we add the other ctrls. */
+static struct attribute *attrs_ctrl0[] = {
+ attrify(abs_pos_filt),
+ NULL
+};
+static struct attribute_group attrs_control0 = GROUP(attrs_ctrl0);
+
+static struct attribute *attrs_ctrl29_30[] = {
+ attrify(z_touch_threshold),
+ attrify(z_touch_hysteresis),
+ NULL
+};
+static struct attribute_group attrs_control29_30 = GROUP(attrs_ctrl29_30);
+
+/** F11_INACCURATE state is overloaded to indicate pen present. */
+#define F11_PEN F11_INACCURATE
+
+static int get_tool_type(struct f11_2d_sensor *sensor, u8 finger_state)
+{
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ sensor->sens_query.query9.has_pen &&
+ finger_state == F11_PEN)
+ return MT_TOOL_PEN;
+ return MT_TOOL_FINGER;
+}
+
+static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ s8 x, y;
+ s8 temp;
+
+ x = data->rel_pos[n_finger].delta_x;
+ y = data->rel_pos[n_finger].delta_y;
+
+ x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x));
+ y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y));
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ }
+ if (axis_align->flip_x)
+ x = min(F11_REL_POS_MAX, -x);
+ if (axis_align->flip_y)
+ y = min(F11_REL_POS_MAX, -y);
+
+ if (x || y) {
+ input_report_rel(sensor->input, REL_X, x);
+ input_report_rel(sensor->input, REL_Y, y);
+ input_report_rel(sensor->mouse_input, REL_X, x);
+ input_report_rel(sensor->mouse_input, REL_Y, y);
+ }
+ input_sync(sensor->mouse_input);
+}
+
+static void rmi_f11_abs_pos_report(struct f11_data *f11,
+ struct f11_2d_sensor *sensor,
+ u8 finger_state, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ u8 prev_state = sensor->finger_tracker[n_finger];
+ int x, y, z;
+ int w_x, w_y, w_max, w_min, orient;
+ int temp;
+
+ if (prev_state && !finger_state) {
+ /* this is a release */
+ x = y = z = w_max = w_min = orient = 0;
+ } else if (!prev_state && !finger_state) {
+ /* nothing to report */
+ return;
+ } else {
+ x = ((data->abs_pos[n_finger].x_msb << 4) |
+ data->abs_pos[n_finger].x_lsb);
+ y = ((data->abs_pos[n_finger].y_msb << 4) |
+ data->abs_pos[n_finger].y_lsb);
+ z = data->abs_pos[n_finger].z;
+ w_x = data->abs_pos[n_finger].w_x;
+ w_y = data->abs_pos[n_finger].w_y;
+ w_max = max(w_x, w_y);
+ w_min = min(w_x, w_y);
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ temp = w_x;
+ w_x = w_y;
+ w_y = temp;
+ }
+
+ orient = w_x > w_y ? 1 : 0;
+
+ if (axis_align->flip_x)
+ x = max(sensor->max_x - x, 0);
+
+ if (axis_align->flip_y)
+ y = max(sensor->max_y - y, 0);
+
+ /*
+ ** here checking if X offset or y offset are specified is
+ ** redundant. We just add the offsets or, clip the values
+ **
+ ** note: offsets need to be done before clipping occurs,
+ ** or we could get funny values that are outside
+ ** clipping boundaries.
+ */
+ x += axis_align->offset_X;
+ y += axis_align->offset_Y;
+ x = max(axis_align->clip_X_low, x);
+ y = max(axis_align->clip_Y_low, y);
+ if (axis_align->clip_X_high)
+ x = min(axis_align->clip_X_high, x);
+ if (axis_align->clip_Y_high)
+ y = min(axis_align->clip_Y_high, y);
+
+ }
+
+ /* Some UIs ignore W of zero, so we fudge it to 1 for pens. */
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ get_tool_type(sensor, finger_state) == MT_TOOL_PEN) {
+ w_max = max(1, w_max);
+ w_min = max(1, w_min);
+ }
+
+ if (sensor->type_a) {
+ input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
+ input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
+ get_tool_type(sensor, finger_state));
+ } else {
+ input_mt_slot(sensor->input, n_finger);
+ input_mt_report_slot_state(sensor->input,
+ get_tool_type(sensor, finger_state), finger_state);
+ }
+
+ input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
+ input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max);
+ input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min);
+ input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient);
+ input_report_abs(sensor->input, ABS_MT_POSITION_X, x);
+ input_report_abs(sensor->input, ABS_MT_POSITION_Y, y);
+ dev_dbg(&sensor->fc->dev,
+ "finger[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
+ n_finger, finger_state, x, y, z, w_max, w_min);
+ /* MT sync between fingers */
+ if (sensor->type_a)
+ input_mt_sync(sensor->input);
+
+ sensor->finger_tracker[n_finger] = finger_state;
+}
+
+#ifdef CONFIG_RMI4_VIRTUAL_BUTTON
+static int rmi_f11_virtual_button_handler(struct f11_2d_sensor *sensor)
+{
+ int i;
+ int x;
+ int y;
+ struct rmi_button_map *virtualbutton_map;
+
+ if (sensor->sens_query.has_gestures &&
+ sensor->data.gest_1->single_tap) {
+ virtualbutton_map = &sensor->virtualbutton_map;
+ x = ((sensor->data.abs_pos[0].x_msb << 4) |
+ sensor->data.abs_pos[0].x_lsb);
+ y = ((sensor->data.abs_pos[0].y_msb << 4) |
+ sensor->data.abs_pos[0].y_lsb);
+ for (i = 0; i < virtualbutton_map->buttons; i++) {
+ if (INBOX(x, y, virtualbutton_map->map[i])) {
+ input_report_key(sensor->input,
+ virtualbutton_map->map[i].code, 1);
+ input_report_key(sensor->input,
+ virtualbutton_map->map[i].code, 0);
+ input_sync(sensor->input);
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+#else
+#define rmi_f11_virtual_button_handler(sensor)
+#endif
+static void rmi_f11_finger_handler(struct f11_data *f11,
+ struct f11_2d_sensor *sensor)
+{
+ const u8 *f_state = sensor->data.f_state;
+ u8 finger_state;
+ u8 finger_pressed_count;
+ u8 i;
+
+ for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
+ /* Possible of having 4 fingers per f_statet register */
+ finger_state = GET_FINGER_STATE(f_state, i);
+
+ if (finger_state == F11_RESERVED) {
+ pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
+ i, finger_state);
+ continue;
+ } else if ((finger_state == F11_PRESENT) ||
+ (finger_state == F11_INACCURATE)) {
+ finger_pressed_count++;
+ }
+
+ if (sensor->data.abs_pos)
+ rmi_f11_abs_pos_report(f11, sensor, finger_state, i);
+
+ if (sensor->data.rel_pos)
+ rmi_f11_rel_pos_report(sensor, i);
+ }
+ input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
+ input_sync(sensor->input);
+}
+
+static int f11_2d_construct_data(struct f11_2d_sensor *sensor)
+{
+ struct f11_2d_sensor_query *query = &sensor->sens_query;
+ struct f11_2d_data *data = &sensor->data;
+ int i;
+
+ sensor->nbr_fingers = (query->number_of_fingers == 5 ? 10 :
+ query->number_of_fingers + 1);
+
+ sensor->pkt_size = F11_CEIL(sensor->nbr_fingers, 4);
+
+ if (query->has_abs)
+ sensor->pkt_size += (sensor->nbr_fingers * 5);
+
+ if (query->has_rel)
+ sensor->pkt_size += (sensor->nbr_fingers * 2);
+
+ /* Check if F11_2D_Query7 is non-zero */
+ if (query->f11_2d_query7__8[0])
+ sensor->pkt_size += sizeof(u8);
+
+ /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
+ if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1])
+ sensor->pkt_size += sizeof(u8);
+
+ if (query->has_pinch || query->has_flick || query->has_rotate) {
+ sensor->pkt_size += 3;
+ if (!query->has_flick)
+ sensor->pkt_size--;
+ if (!query->has_rotate)
+ sensor->pkt_size--;
+ }
+
+ if (query->has_touch_shapes)
+ sensor->pkt_size += F11_CEIL(query->nbr_touch_shapes + 1, 8);
+
+ sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL);
+ if (!sensor->data_pkt)
+ return -ENOMEM;
+
+ data->f_state = sensor->data_pkt;
+ i = F11_CEIL(sensor->nbr_fingers, 4);
+
+ if (query->has_abs) {
+ data->abs_pos = (struct f11_2d_data_1_5 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 5);
+ }
+
+ if (query->has_rel) {
+ data->rel_pos = (struct f11_2d_data_6_7 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 2);
+ }
+
+ if (query->f11_2d_query7__8[0]) {
+ data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1]) {
+ data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_pinch) {
+ data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_flick) {
+ if (query->has_pinch) {
+ data->flick = (struct f11_2d_data_10_12 *)data->pinch;
+ i += 2;
+ } else {
+ data->flick = (struct f11_2d_data_10_12 *)
+ &sensor->data_pkt[i];
+ i += 3;
+ }
+ }
+
+ if (query->has_rotate) {
+ if (query->has_flick) {
+ data->rotate = (struct f11_2d_data_11_12 *)
+ (data->flick + 1);
+ } else {
+ data->rotate = (struct f11_2d_data_11_12 *)
+ &sensor->data_pkt[i];
+ i += 2;
+ }
+ }
+
+ if (query->has_touch_shapes)
+ data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i];
+
+ return 0;
+}
+
+static int f11_read_control_regs(struct rmi_device *rmi_dev,
+ struct f11_2d_ctrl *ctrl,
+ u16 ctrl_base_addr) {
+ u16 read_address = ctrl_base_addr;
+ int error = 0;
+
+ ctrl->ctrl0_9->address = read_address;
+ error = rmi_read_block(rmi_dev, read_address, ctrl->ctrl0_9->regs,
+ sizeof(ctrl->ctrl0_9->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl0, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl0_9->regs);
+
+ if (ctrl->ctrl10) {
+ error = rmi_read_block(rmi_dev, read_address,
+ &ctrl->ctrl10->reg, sizeof(union f11_2d_ctrl10));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl10, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl10);
+ }
+
+ if (ctrl->ctrl11) {
+ error = rmi_read_block(rmi_dev, read_address,
+ &ctrl->ctrl11->reg, sizeof(union f11_2d_ctrl11));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl11, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl11);
+ }
+
+ if (ctrl->ctrl14) {
+ error = rmi_read_block(rmi_dev, read_address,
+ &ctrl->ctrl14->reg, sizeof(union f11_2d_ctrl14));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl14, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl14);
+ }
+
+ if (ctrl->ctrl15) {
+ error = rmi_read_block(rmi_dev, read_address,
+ &ctrl->ctrl15->reg, sizeof(union f11_2d_ctrl15));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl15, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl15);
+ }
+
+ if (ctrl->ctrl16) {
+ error = rmi_read_block(rmi_dev, read_address,
+ &ctrl->ctrl16->reg, sizeof(union f11_2d_ctrl16));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl16, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl16);
+ }
+
+ if (ctrl->ctrl17) {
+ error = rmi_read_block(rmi_dev, read_address,
+ &ctrl->ctrl17->reg, sizeof(union f11_2d_ctrl17));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl17, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl17);
+ }
+
+ if (ctrl->ctrl18_19) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl18_19->reg, sizeof(union f11_2d_ctrl18_19));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl18_19, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl18_19);
+ }
+
+ if (ctrl->ctrl20_21) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl20_21->reg, sizeof(union f11_2d_ctrl20_21));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl20_21, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl20_21);
+ }
+
+ if (ctrl->ctrl22_26) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl22_26->regs, sizeof(union f11_2d_ctrl22_26));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl22_26, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl22_26);
+ }
+
+ if (ctrl->ctrl27) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl27->regs, sizeof(union f11_2d_ctrl27));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl27, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl27);
+ }
+
+ if (ctrl->ctrl28) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl28->regs, sizeof(union f11_2d_ctrl28));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl28, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(union f11_2d_ctrl28);
+ }
+
+ if (ctrl->ctrl29_30) {
+ ctrl->ctrl29_30->address = read_address;
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl29_30->regs, sizeof(ctrl->ctrl29_30->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl29_30, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl29_30->regs);
+ }
+ return 0;
+}
+
+static int f11_allocate_control_regs(struct rmi_device *rmi_dev,
+ struct f11_2d_device_query *device_query,
+ struct f11_2d_sensor_query *sensor_query,
+ struct f11_2d_ctrl *ctrl,
+ u16 ctrl_base_addr) {
+
+ struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_function_container *fc = driver_data->f01_container;
+
+ ctrl->ctrl0_9 = devm_kzalloc(&fc->dev, sizeof(union f11_2d_ctrl0_9),
+ GFP_KERNEL);
+ if (!ctrl->ctrl0_9)
+ return -ENOMEM;
+ if (sensor_query->f11_2d_query7__8[0]) {
+ ctrl->ctrl10 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl10), GFP_KERNEL);
+ if (!ctrl->ctrl10)
+ return -ENOMEM;
+ }
+
+ if (sensor_query->f11_2d_query7__8[1]) {
+ ctrl->ctrl11 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl11), GFP_KERNEL);
+ if (!ctrl->ctrl11)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query9 && sensor_query->query9.has_pen) {
+ ctrl->ctrl20_21 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl20_21), GFP_KERNEL);
+ if (!ctrl->ctrl20_21)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query9 && sensor_query->query9.has_proximity) {
+ ctrl->ctrl22_26 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl22_26), GFP_KERNEL);
+ if (!ctrl->ctrl22_26)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query9 &&
+ (sensor_query->query9.has_palm_det_sensitivity ||
+ sensor_query->query9.has_suppress_on_palm_detect)) {
+ ctrl->ctrl27 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl27), GFP_KERNEL);
+ if (!ctrl->ctrl27)
+ return -ENOMEM;
+ }
+
+ if (sensor_query->has_multi_finger_scroll) {
+ ctrl->ctrl28 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl28), GFP_KERNEL);
+ if (!ctrl->ctrl28)
+ return -ENOMEM;
+ }
+
+ if (device_query->has_query11 && device_query->has_z_tuning) {
+ ctrl->ctrl29_30 = devm_kzalloc(&fc->dev,
+ sizeof(union f11_2d_ctrl29_30), GFP_KERNEL);
+ if (!ctrl->ctrl29_30)
+ return -ENOMEM;
+ }
+
+ return f11_read_control_regs(rmi_dev, ctrl, ctrl_base_addr);
+}
+
+static int f11_write_control_regs(struct rmi_device *rmi_dev,
+ struct f11_2d_sensor_query *query,
+ struct f11_2d_ctrl *ctrl,
+ u16 ctrl_base_addr)
+{
+ u16 write_address = ctrl_base_addr;
+ int error;
+
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl0_9->regs,
+ sizeof(ctrl->ctrl0_9->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl0_9);
+
+ if (ctrl->ctrl10) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl10->reg, 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl11) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl11->reg, 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl12 && ctrl->ctrl12_size && query->configurable) {
+ if (ctrl->ctrl12_size > query->max_electrodes) {
+ dev_err(&rmi_dev->dev,
+ "%s: invalid cfg size:%d, should be < %d.\n",
+ __func__, ctrl->ctrl12_size,
+ query->max_electrodes);
+ return -EINVAL;
+ }
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl12->reg,
+ ctrl->ctrl12_size);
+ if (error < 0)
+ return error;
+ write_address += ctrl->ctrl12_size;
+ }
+
+ if (ctrl->ctrl14) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl14->reg, 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl15) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl15->reg, 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl16) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl16->reg, 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl17) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl17->reg, 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl18_19) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl18_19->reg, sizeof(union f11_2d_ctrl18_19));
+ if (error < 0)
+ return error;
+ write_address += sizeof(union f11_2d_ctrl18_19);
+ }
+
+ if (ctrl->ctrl20_21) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl20_21->reg,
+ sizeof(union f11_2d_ctrl20_21));
+ if (error < 0)
+ return error;
+ write_address += sizeof(union f11_2d_ctrl20_21);
+ }
+
+ if (ctrl->ctrl22_26) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl22_26->regs,
+ sizeof(union f11_2d_ctrl22_26));
+ if (error < 0)
+ return error;
+ write_address += sizeof(union f11_2d_ctrl22_26);
+ }
+
+ if (ctrl->ctrl27) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl27->regs,
+ sizeof(union f11_2d_ctrl27));
+ if (error < 0)
+ return error;
+ write_address += sizeof(union f11_2d_ctrl27);
+ }
+
+ if (ctrl->ctrl28) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl28->regs,
+ sizeof(union f11_2d_ctrl28));
+ if (error < 0)
+ return error;
+ write_address += sizeof(union f11_2d_ctrl28);
+ }
+
+ if (ctrl->ctrl29_30) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl29_30->regs,
+ sizeof(union f11_2d_ctrl29_30));
+ if (error < 0)
+ return error;
+ write_address += sizeof(union f11_2d_ctrl29_30);
+ }
+
+ return 0;
+}
+
+static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_sensor_query *query, u16 query_base_addr)
+{
+ int query_size;
+ int rc;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr, query->f11_2d_query1__4,
+ sizeof(query->f11_2d_query1__4));
+ if (rc < 0)
+ return rc;
+ query_size = rc;
+
+ if (query->has_abs) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query5);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (query->has_rel) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query6);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (query->has_gestures) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ query->f11_2d_query7__8,
+ sizeof(query->f11_2d_query7__8));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(query->f11_2d_query7__8);
+ }
+
+ if (query->has_touch_shapes) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query10);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ return query_size;
+}
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_container *fc, int index)
+{
+ struct f11_data *f11 = fc->data;
+ struct f11_2d_sensor *sensor = &f11->sensors[index];
+ struct input_dev *input = sensor->input;
+ int device_x_max =
+ f11->dev_controls.ctrl0_9->sensor_max_x_pos;
+ int device_y_max =
+ f11->dev_controls.ctrl0_9->sensor_max_y_pos;
+ int x_min, x_max, y_min, y_max;
+ if (sensor->axis_align.swap_axes) {
+ int temp = device_x_max;
+ device_x_max = device_y_max;
+ device_y_max = temp;
+ }
+ /* Use the max X and max Y read from the device, or the clip values,
+ * whichever is stricter.
+ */
+ x_min = sensor->axis_align.clip_X_low;
+ if (sensor->axis_align.clip_X_high)
+ x_max = min((int) device_x_max,
+ sensor->axis_align.clip_X_high);
+ else
+ x_max = device_x_max;
+
+ y_min = sensor->axis_align.clip_Y_low;
+ if (sensor->axis_align.clip_Y_high)
+ y_max = min((int) device_y_max,
+ sensor->axis_align.clip_Y_high);
+ else
+ y_max = device_y_max;
+
+ dev_dbg(&fc->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
+ x_min, x_max, y_min, y_max);
+
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+ DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+ 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+ 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+ input_set_abs_params(input, ABS_MT_ORIENTATION,
+ 0, DEFAULT_MAX_ABS_MT_ORIENTATION, 0, 0);
+ input_set_abs_params(input, ABS_MT_TRACKING_ID,
+ DEFAULT_MIN_ABS_MT_TRACKING_ID,
+ DEFAULT_MAX_ABS_MT_TRACKING_ID, 0, 0);
+ /* TODO get max_x_pos (and y) from control registers. */
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ x_min, x_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ y_min, y_max, 0, 0);
+ if (!sensor->type_a)
+ input_mt_init_slots(input, sensor->nbr_fingers);
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ sensor->sens_query.query9.has_pen)
+ input_set_abs_params(input, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ else
+ input_set_abs_params(input, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_FINGER, 0, 0);
+}
+
+static int f11_device_init(struct rmi_function_container *fc)
+{
+ int rc;
+
+ rc = rmi_f11_initialize(fc);
+ if (rc < 0)
+ goto err_free_data;
+
+ rc = rmi_f11_register_devices(fc);
+ if (rc < 0)
+ goto err_free_data;
+
+ rc = rmi_f11_create_sysfs(fc);
+ if (rc < 0)
+ goto err_free_data;
+
+ return 0;
+
+err_free_data:
+ rmi_f11_free_memory(fc);
+
+ return rc;
+}
+
+static void rmi_f11_free_memory(struct rmi_function_container *fc)
+{
+ struct f11_data *f11 = fc->data;
+ int i;
+
+ if (f11) {
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
+ kfree(f11->sensors[i].button_map);
+ }
+}
+
+
+static int rmi_f11_initialize(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11;
+ struct f11_2d_ctrl *ctrl;
+ u8 query_offset;
+ u16 query_base_addr;
+ u16 control_base_addr;
+ u16 max_x_pos, max_y_pos, temp;
+ int rc;
+ int i;
+ struct rmi_device_platform_data *pdata = to_rmi_platform_data(rmi_dev);
+
+ dev_dbg(&fc->dev, "Initializing F11 values for %s.\n",
+ pdata->sensor_name);
+
+ /*
+ ** init instance data, fill in values and create any sysfs files
+ */
+ f11 = devm_kzalloc(&fc->dev, sizeof(struct f11_data), GFP_KERNEL);
+ if (!f11)
+ return -ENOMEM;
+
+ fc->data = f11;
+ f11->rezero_wait_ms = pdata->f11_rezero_wait;
+
+ query_base_addr = fc->fd.query_base_addr;
+ control_base_addr = fc->fd.control_base_addr;
+
+ rc = rmi_read(rmi_dev, query_base_addr, &f11->dev_query.f11_2d_query0);
+ if (rc < 0)
+ return rc;
+
+ query_offset = (query_base_addr + 1);
+ /* Increase with one since number of sensors is zero based */
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ struct f11_2d_sensor *sensor = &f11->sensors[i];
+ sensor->sensor_index = i;
+ sensor->fc = fc;
+
+ rc = rmi_f11_get_query_parameters(rmi_dev, &sensor->sens_query,
+ query_offset);
+ if (rc < 0)
+ return rc;
+ query_offset += rc;
+
+ if (f11->dev_query.has_query9) {
+ rc = rmi_read(rmi_dev, query_offset,
+ &sensor->sens_query.query9.reg);
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read query 9.\n");
+ return rc;
+ }
+ query_offset += rc;
+ }
+
+ rc = f11_allocate_control_regs(rmi_dev,
+ &f11->dev_query, &sensor->sens_query,
+ &f11->dev_controls, control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to initialize F11 control params.\n");
+ return rc;
+ }
+
+ if (i < pdata->f11_sensor_count) {
+ sensor->axis_align =
+ pdata->f11_sensor_data[i].axis_align;
+ sensor->virtual_buttons =
+ pdata->f11_sensor_data[i].virtual_buttons;
+ sensor->type_a = pdata->f11_sensor_data[i].type_a;
+ }
+
+ rc = rmi_read_block(rmi_dev,
+ control_base_addr + F11_CTRL_SENSOR_MAX_X_POS_OFFSET,
+ (u8 *)&max_x_pos, sizeof(max_x_pos));
+ if (rc < 0)
+ return rc;
+
+ rc = rmi_read_block(rmi_dev,
+ control_base_addr + F11_CTRL_SENSOR_MAX_Y_POS_OFFSET,
+ (u8 *)&max_y_pos, sizeof(max_y_pos));
+ if (rc < 0)
+ return rc;
+
+ if (sensor->axis_align.swap_axes) {
+ temp = max_x_pos;
+ max_x_pos = max_y_pos;
+ max_y_pos = temp;
+ }
+ sensor->max_x = max_x_pos;
+ sensor->max_y = max_y_pos;
+
+ rc = f11_2d_construct_data(sensor);
+ if (rc < 0)
+ return rc;
+
+ ctrl = &f11->dev_controls;
+ if (sensor->axis_align.delta_x_threshold) {
+ ctrl->ctrl0_9->delta_x_threshold =
+ sensor->axis_align.delta_x_threshold;
+ rc = rmi_write_block(rmi_dev,
+ ctrl->ctrl0_9->address,
+ ctrl->ctrl0_9->regs,
+ sizeof(ctrl->ctrl0_9->regs));
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to write to delta_x_threshold %d. Code: %d.\n",
+ i, rc);
+
+ }
+
+ if (sensor->axis_align.delta_y_threshold) {
+ ctrl->ctrl0_9->delta_y_threshold =
+ sensor->axis_align.delta_y_threshold;
+ rc = rmi_write_block(rmi_dev,
+ ctrl->ctrl0_9->address,
+ ctrl->ctrl0_9->regs,
+ sizeof(ctrl->ctrl0_9->regs));
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to write to delta_y_threshold %d. Code: %d.\n",
+ i, rc);
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ rc = setup_sensor_debugfs(sensor);
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs for F11 sensor %d. Code: %d.\n",
+ i, rc);
+ }
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ rc = setup_f11_debugfs(fc);
+ if (rc < 0)
+ dev_warn(&fc->dev, "Failed to setup debugfs for F11. Code: %d.\n",
+ rc);
+ }
+
+ mutex_init(&f11->dev_controls_mutex);
+ return 0;
+}
+
+static void register_virtual_buttons(struct rmi_function_container *fc,
+ struct f11_2d_sensor *sensor) {
+ int j;
+
+ if (!sensor->sens_query.has_gestures)
+ return;
+ if (!sensor->virtual_buttons.buttons) {
+ dev_warn(&fc->dev, "No virtual button platform data for 2D sensor %d.\n",
+ sensor->sensor_index);
+ return;
+ }
+ /* call devm_kcalloc when it will be defined in kernel */
+ sensor->button_map = devm_kzalloc(&fc->dev,
+ sensor->virtual_buttons.buttons,
+ GFP_KERNEL);
+ if (!sensor->button_map) {
+ dev_err(&fc->dev, "Failed to allocate the virtual button map.\n");
+ return;
+ }
+
+ /* manage button map using input subsystem */
+ sensor->input->keycode = sensor->button_map;
+ sensor->input->keycodesize = sizeof(u8);
+ sensor->input->keycodemax = sensor->virtual_buttons.buttons;
+
+ /* set bits for each button... */
+ for (j = 0; j < sensor->virtual_buttons.buttons; j++) {
+ sensor->button_map[j] = sensor->virtual_buttons.map[j].code;
+ set_bit(sensor->button_map[j], sensor->input->keybit);
+ }
+}
+
+static int rmi_f11_register_devices(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11 = fc->data;
+ struct input_dev *input_dev;
+ struct input_dev *input_dev_mouse;
+ int sensors_itertd = 0;
+ int i;
+ int rc;
+
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ struct f11_2d_sensor *sensor = &f11->sensors[i];
+ sensors_itertd = i;
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ rc = -ENOMEM;
+ goto error_unregister;
+ }
+
+ sensor->input = input_dev;
+ /* TODO how to modify the dev name and
+ * phys name for input device */
+ sprintf(sensor->input_name, "%sfn%02x",
+ dev_name(&rmi_dev->dev), fc->fd.function_number);
+ input_dev->name = sensor->input_name;
+ sprintf(sensor->input_phys, "%s/input0",
+ input_dev->name);
+ input_dev->phys = sensor->input_phys;
+ input_dev->dev.parent = &rmi_dev->dev;
+ input_set_drvdata(input_dev, f11);
+
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_ABS, input_dev->evbit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
+ set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ f11_set_abs_params(fc, i);
+
+ dev_dbg(&fc->dev, "%s: Sensor %d hasRel %d.\n",
+ __func__, i, sensor->sens_query.has_rel);
+ if (sensor->sens_query.has_rel) {
+ set_bit(EV_REL, input_dev->evbit);
+ set_bit(REL_X, input_dev->relbit);
+ set_bit(REL_Y, input_dev->relbit);
+ }
+ rc = input_register_device(input_dev);
+ if (rc < 0) {
+ input_free_device(input_dev);
+ sensor->input = NULL;
+ goto error_unregister;
+ }
+
+ if (IS_ENABLED(CONFIG_RMI4_VIRTUAL_BUTTON))
+ register_virtual_buttons(fc, sensor);
+
+ if (sensor->sens_query.has_rel) {
+ /*create input device for mouse events */
+ input_dev_mouse = input_allocate_device();
+ if (!input_dev_mouse) {
+ rc = -ENOMEM;
+ goto error_unregister;
+ }
+
+ sensor->mouse_input = input_dev_mouse;
+ input_dev_mouse->name = "rmi_mouse";
+ input_dev_mouse->phys = "rmi_f11/input0";
+
+ 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);
+
+ rc = input_register_device(input_dev_mouse);
+ if (rc < 0) {
+ input_free_device(input_dev_mouse);
+ sensor->mouse_input = NULL;
+ goto error_unregister;
+ }
+
+ set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+ }
+
+ }
+
+ return 0;
+
+error_unregister:
+ for (; sensors_itertd > 0; sensors_itertd--) {
+ if (f11->sensors[sensors_itertd].input) {
+ if (f11->sensors[sensors_itertd].mouse_input) {
+ input_unregister_device(
+ f11->sensors[sensors_itertd].mouse_input);
+ f11->sensors[sensors_itertd].mouse_input = NULL;
+ }
+ input_unregister_device(f11->sensors[i].input);
+ f11->sensors[i].input = NULL;
+ }
+ }
+
+ return rc;
+}
+
+static void rmi_f11_free_devices(struct rmi_function_container *fc)
+{
+ struct f11_data *f11 = fc->data;
+ int i;
+
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ if (f11->sensors[i].input)
+ input_unregister_device(f11->sensors[i].input);
+ if (f11->sensors[i].sens_query.has_rel &&
+ f11->sensors[i].mouse_input)
+ input_unregister_device(f11->sensors[i].mouse_input);
+ }
+}
+
+static int rmi_f11_create_sysfs(struct rmi_function_container *fc)
+{
+ int attr_count = 0;
+ int rc;
+ struct f11_data *f11 = fc->data;
+
+ 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;
+ }
+ }
+ if (sysfs_create_group(&fc->dev.kobj, &attrs_control0) < 0) {
+ dev_err(&fc->dev, "Failed to create query sysfs files.\n");
+ return -ENODEV;
+ }
+ if (f11->dev_controls.ctrl29_30) {
+ if (sysfs_create_group(&fc->dev.kobj,
+ &attrs_control29_30) < 0) {
+ dev_err(&fc->dev,
+ "Failed to create query sysfs files.");
+ return -ENODEV;
+ }
+ }
+
+ return 0;
+
+err_remove_sysfs:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+ sysfs_remove_group(&fc->dev.kobj, &attrs_control0);
+ if (f11->dev_controls.ctrl29_30)
+ sysfs_remove_group(&fc->dev.kobj, &attrs_control29_30);
+ return rc;
+}
+
+static int rmi_f11_config(struct rmi_function_container *fc)
+{
+ struct f11_data *f11 = fc->data;
+ int i;
+ int rc;
+
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ rc = f11_write_control_regs(fc->rmi_dev,
+ &f11->sensors[i].sens_query,
+ &f11->dev_controls,
+ fc->fd.query_base_addr);
+ if (rc < 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+int rmi_f11_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11 = fc->data;
+ u16 data_base_addr = fc->fd.data_base_addr;
+ u16 data_base_addr_offset = 0;
+ int error;
+ int i;
+
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++) {
+ error = rmi_read_block(rmi_dev,
+ data_base_addr + data_base_addr_offset,
+ f11->sensors[i].data_pkt,
+ f11->sensors[i].pkt_size);
+ if (error < 0)
+ return error;
+
+ rmi_f11_finger_handler(f11, &f11->sensors[i]);
+ rmi_f11_virtual_button_handler(&f11->sensors[i]);
+ data_base_addr_offset += f11->sensors[i].pkt_size;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rmi_f11_resume(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *data = fc->data;
+ /* Command register always reads as 0, so we can just use a local. */
+ union f11_2d_commands commands = {};
+ int retval = 0;
+
+ dev_dbg(&fc->dev, "Resuming...\n");
+ if (!data->rezero_wait_ms)
+ return 0;
+
+ mdelay(data->rezero_wait_ms);
+
+ commands.rezero = 1;
+ retval = rmi_write_block(rmi_dev, fc->fd.command_base_addr,
+ &commands.reg, sizeof(commands.reg));
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "%s: failed to issue rezero command, error = %d.",
+ __func__, retval);
+ return retval;
+ }
+
+ return retval;
+}
+#endif /* CONFIG_PM */
+
+static int f11_remove_device(struct device *dev)
+{
+ int attr_count = 0;
+ struct f11_data *f11;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ f11 = fc->data;
+
+ if (IS_ENABLED(CONFIG_RMI4_DEBUG)) {
+ int i;
+
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
+ teardown_sensor_debugfs(&f11->sensors[i]);
+ teardown_f11_debugfs(f11);
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+ sysfs_remove_file(&fc->dev.kobj, &attrs[attr_count].attr);
+
+ sysfs_remove_group(&fc->dev.kobj, &attrs_control0);
+ if (f11->dev_controls.ctrl29_30)
+ sysfs_remove_group(&fc->dev.kobj, &attrs_control29_30);
+
+ rmi_f11_free_devices(fc);
+
+ rmi_f11_free_memory(fc);
+
+ return 0;
+}
+
+static int f11_probe(struct device *dev);
+
+static struct rmi_function_handler function_handler = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_f11",
+ .bus = &rmi_bus_type,
+ .probe = f11_probe,
+ .remove = f11_remove_device,
+ },
+ .func = 0x11,
+ .config = rmi_f11_config,
+ .attention = rmi_f11_attention,
+#ifdef CONFIG_PM
+ .resume = rmi_f11_resume
+#endif
+};
+
+static __devinit int f11_probe(struct device *dev)
+{
+ struct rmi_function_container *fc;
+
+ if (dev->type != &rmi_function_type) {
+ dev_dbg(dev, "Not a function device.\n");
+ return 1;
+ }
+ fc = to_rmi_function_container(dev);
+ if (fc->fd.function_number != function_handler.func) {
+ dev_dbg(dev, "Device is F%02X, not F%02X.\n",
+ fc->fd.function_number, function_handler.func);
+ return 1;
+ }
+
+ return f11_device_init(fc);
+}
+
+static int __init rmi_f11_module_init(void)
+{
+ int error;
+
+ error = driver_register(&function_handler.driver);
+ if (error < 0) {
+ pr_err("%s: register driver failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit rmi_f11_module_exit(void)
+{
+ driver_unregister(&function_handler.driver);
+}
+
+static ssize_t f11_maxPos_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ data->sensors[0].max_x, data->sensors[0].max_y);
+}
+
+static ssize_t f11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->
+ sensors[0].axis_align.rel_report_enabled);
+}
+
+static ssize_t f11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_value;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u", &new_value) != 1)
+ return -EINVAL;
+ if (new_value < 0 || new_value > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.rel_report_enabled = new_value;
+
+ return count;
+}
+
+static ssize_t f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc = NULL;
+ unsigned int rezero;
+ int retval = 0;
+ /* Command register always reads as 0, so we can just use a local. */
+ union f11_2d_commands commands = {};
+
+ fc = to_rmi_function_container(dev);
+
+ if (sscanf(buf, "%u", &rezero) != 1)
+ return -EINVAL;
+ if (rezero < 0 || rezero > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (rezero) {
+ commands.rezero = 1;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &commands.reg, sizeof(commands.reg));
+ if (retval < 0) {
+ dev_err(dev, "%s: failed to issue rezero command, error = %d.",
+ __func__, retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+/* Control sysfs files */
+show_store_union_struct_unsigned(dev_controls, ctrl0_9, abs_pos_filt)
+show_store_union_struct_unsigned(dev_controls, ctrl29_30, z_touch_threshold)
+show_store_union_struct_unsigned(dev_controls, ctrl29_30, z_touch_hysteresis)
+
+module_init(rmi_f11_module_init);
+module_exit(rmi_f11_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_DESCRIPTION("RMI F11 module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
Not much to say here, except that we've reduced the number of different
Kconfig options, which will hopefully make it easier to configure RMI4
support in kernels.
Signed-off-by: Christopher Heiny <[email protected]>
Cc: Dmitry Torokhov <[email protected]>
Cc: Linus Walleij <[email protected]>
Cc: Naveen Kumar Gaddipati <[email protected]>
Cc: Joeri de Gram <[email protected]>
---
drivers/input/Kconfig | 2 +
drivers/input/Makefile | 2 +
drivers/input/rmi4/Kconfig | 86 +++++++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/Makefile | 26 +++++++++++++
4 files changed, 116 insertions(+), 0 deletions(-)
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 55f7e57..2c543c0 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -188,6 +188,8 @@ source "drivers/input/touchscreen/Kconfig"
source "drivers/input/misc/Kconfig"
+source "drivers/input/rmi4/Kconfig"
+
endif
menu "Hardware I/O ports"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 5ca3f63..571515a 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -24,4 +24,6 @@ obj-$(CONFIG_INPUT_TABLET) += tablet/
obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
+obj-y += rmi4/
+
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
new file mode 100644
index 0000000..fa12638
--- /dev/null
+++ b/drivers/input/rmi4/Kconfig
@@ -0,0 +1,86 @@
+#
+# RMI4 configuration
+#
+config RMI4_BUS
+ bool "Synaptics RMI4 bus support"
+ help
+ Say Y here if you want to support the Synaptics RMI4 bus. This is
+ required for all RMI4 device support.
+
+ If unsure, say Y.
+
+ This feature is not currently available as a loadable module.
+
+config RMI4_DEBUG
+ bool "RMI4 Debugging"
+ depends on RMI4_BUS
+ help
+ Say Y here to enable debug feature in the RMI4 driver.
+
+ Note that the RMI4 driver debug features can generate a lot of
+ output (potentially clogging up your dmesg output) and generally
+ slow down driver operation. It's recommended to enable them only
+ if you are actively developing/debugging RMI4 features.
+
+ If unsure, say N.
+
+config RMI4_I2C
+ bool "RMI4 I2C Support"
+ depends on RMI4_BUS && I2C
+ help
+ Say Y here if you want to support RMI4 devices connected to an I2C
+ bus.
+
+ If unsure, say Y.
+
+ This feature is not currently available as a loadable module.
+
+config RMI4_GENERIC
+ bool "RMI4 Generic driver"
+ depends on RMI4_BUS
+ help
+ Say Y here if you want to support generic RMI4 devices.
+
+ This is pretty much required if you want to do anything useful with
+ your RMI device.
+
+ This feature is not currently available as a loadable module.
+
+config RMI4_F11
+ tristate "RMI4 Function 11 (2D pointing)"
+ depends on RMI4_BUS && RMI4_GENERIC
+ help
+ Say Y here if you want to add support for RMI4 function 11.
+
+ Function 11 provides 2D multifinger pointing for touchscreens and
+ touchpads. For sensors that support relative pointing, F11 also
+ provides mouse input.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rmi-f11.
+
+config RMI4_F11_PEN
+ bool "RMI4 F11 Pen Support"
+ depends on RMI4_F11
+ help
+ Say Y here to add support for pen input to RMI4 function 11.
+
+ If this feature is enabled, when pen inputs are detected they
+ will be reported to the input stream as MT_TOOL_PEN. Otherwise,
+ pens will be treated the same as fingers.
+
+ Not all UI implementations deal gracefully with pen discrimination.
+ If your system is not recognizing pen touches and you know your
+ sensor supports pen input, you probably want to turn this feature
+ off.
+
+config RMI4_VIRTUAL_BUTTON
+ tristate "RMI4 Vitual Button"
+ depends on RMI4_F11
+ help
+ Say Y here if you want to add support for RMI4 virtual button to F11.
+
+ The virtual button feature implement the virtual button device in
+ certain RMI4 touch sensors.
+
+ This works only if your sensor supports F11 gestures.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
new file mode 100644
index 0000000..1480689
--- /dev/null
+++ b/drivers/input/rmi4/Makefile
@@ -0,0 +1,26 @@
+obj-$(CONFIG_RMI4_BUS) += rmi_bus.o
+obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
+obj-$(CONFIG_RMI4_GENERIC) += rmi_driver.o rmi_f01.o
+obj-$(CONFIG_RMI4_F11) += rmi_f11.o
+
+ccflags-$(CONFIG_RMI4_DEBUG) += -DDEBUG
+
+ifeq ($(KERNELRELEASE),)
+
+# KERNELDIR ?= /home/<AndroidKernelDirectory>
+PWD := $(shell pwd)
+
+.PHONY: build clean
+
+build:
+ $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
+
+clean:
+ rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c
+else
+
+$(info Building with KERNELRELEASE = ${KERNELRELEASE})
+obj-m += rmi_dev.o
+
+endif
+
On Fri, 2012-10-05 at 21:09 -0700, Christopher Heiny wrote:
[]
Just some trivial comments:
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
[]
> @@ -0,0 +1,1529 @@
[]
> +static ssize_t delay_write(struct file *filp, const char __user *buffer,
> + size_t size, loff_t *offset) {
> + struct driver_debugfs_data *data = filp->private_data;
> + struct rmi_device_platform_data *pdata =
> + data->rmi_dev->phys->dev->platform_data;
> + int retval;
> + char local_buf[size];
> + unsigned int new_read_delay;
> + unsigned int new_write_delay;
> + unsigned int new_block_delay;
> + unsigned int new_pre_delay;
> + unsigned int new_post_delay;
> +
> + retval = copy_from_user(local_buf, buffer, size);
> + if (retval)
> + return -EFAULT;
> +
> + retval = sscanf(local_buf, "%u %u %u %u %u", &new_read_delay,
> + &new_write_delay, &new_block_delay,
> + &new_pre_delay, &new_post_delay);
> + if (retval != 5) {
> + dev_err(&data->rmi_dev->dev,
> + "Incorrect number of values provided for delay.");
> + return -EINVAL;
> + }
> + if (new_read_delay < 0) {
These are unnecessary tests as unsigned values are never < 0.
> + dev_err(&data->rmi_dev->dev,
> + "Byte delay must be positive microseconds.\n");
> + return -EINVAL;
> + }
> + if (new_write_delay < 0) {
etc.
> +static ssize_t phys_read(struct file *filp, char __user *buffer, size_t size,
> + loff_t *offset) {
> + struct driver_debugfs_data *data = filp->private_data;
> + struct rmi_phys_info *info = &data->rmi_dev->phys->info;
> + int retval;
> + char local_buf[size];
size comes from where? possible stack overflow?
> +static ssize_t irq_debug_write(struct file *filp, const char __user *buffer,
> + size_t size, loff_t *offset) {
> + int retval;
> + char local_buf[size];
here too
[]
> +static int process_interrupt_requests(struct rmi_device *rmi_dev)
> +{
[]
> + list_for_each_entry(entry, &data->rmi_functions.list, list)
> + if (entry->irq_mask)
> + process_one_interrupt(entry, irq_status,
> + data);
style nit, it'd be nicer with braces.
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
[]
> @@ -0,0 +1,438 @@
> +
> +#define tricat(x, y, z) tricat_(x, y, z)
> +
> +#define tricat_(x, y, z) x##y##z
I think these tricat macros are merely obfuscating
and don't need to be used.
On Sat, Oct 6, 2012 at 8:19 AM, Joe Perches <[email protected]> wrote:
> On Fri, 2012-10-05 at 21:09 -0700, Christopher Heiny wrote:
> []
>
> Just some trivial comments:
>
>> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> []
>> @@ -0,0 +1,1529 @@
> []
>> +static ssize_t delay_write(struct file *filp, const char __user *buffer,
>> + size_t size, loff_t *offset) {
>> + struct driver_debugfs_data *data = filp->private_data;
>> + struct rmi_device_platform_data *pdata =
>> + data->rmi_dev->phys->dev->platform_data;
>> + int retval;
>> + char local_buf[size];
>> + unsigned int new_read_delay;
>> + unsigned int new_write_delay;
>> + unsigned int new_block_delay;
>> + unsigned int new_pre_delay;
>> + unsigned int new_post_delay;
>> +
>> + retval = copy_from_user(local_buf, buffer, size);
>> + if (retval)
>> + return -EFAULT;
>> +
>> + retval = sscanf(local_buf, "%u %u %u %u %u", &new_read_delay,
>> + &new_write_delay, &new_block_delay,
>> + &new_pre_delay, &new_post_delay);
>> + if (retval != 5) {
>> + dev_err(&data->rmi_dev->dev,
>> + "Incorrect number of values provided for delay.");
>> + return -EINVAL;
>> + }
>> + if (new_read_delay < 0) {
>
> These are unnecessary tests as unsigned values are never < 0.
>
Nope.
1 main()
2 {
3 char buf[100] = "1 -2";
4 int t, t2;
5
6 sscanf(buf, "%u %u", &t, &t2);
7
8 if (t > 0) {
9 printf("greater\n");
10 }
11
12 if (t2 > 0) {
13 printf("greater\n");
14 } else {
15 printf("lesser\n");
16 }
17 }
Thanks,
On Sat, Oct 6, 2012 at 9:06 AM, devendra.aaru <[email protected]> wrote:
> On Sat, Oct 6, 2012 at 8:19 AM, Joe Perches <[email protected]> wrote:
>> On Fri, 2012-10-05 at 21:09 -0700, Christopher Heiny wrote:
>> []
>>
>> Just some trivial comments:
>>
>>> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
>> []
>>> @@ -0,0 +1,1529 @@
>> []
>>> +static ssize_t delay_write(struct file *filp, const char __user *buffer,
>>> + size_t size, loff_t *offset) {
>>> + struct driver_debugfs_data *data = filp->private_data;
>>> + struct rmi_device_platform_data *pdata =
>>> + data->rmi_dev->phys->dev->platform_data;
>>> + int retval;
>>> + char local_buf[size];
>>> + unsigned int new_read_delay;
>>> + unsigned int new_write_delay;
>>> + unsigned int new_block_delay;
>>> + unsigned int new_pre_delay;
>>> + unsigned int new_post_delay;
>>> +
>>> + retval = copy_from_user(local_buf, buffer, size);
>>> + if (retval)
>>> + return -EFAULT;
>>> +
>>> + retval = sscanf(local_buf, "%u %u %u %u %u", &new_read_delay,
>>> + &new_write_delay, &new_block_delay,
>>> + &new_pre_delay, &new_post_delay);
>>> + if (retval != 5) {
>>> + dev_err(&data->rmi_dev->dev,
>>> + "Incorrect number of values provided for delay.");
>>> + return -EINVAL;
>>> + }
>>> + if (new_read_delay < 0) {
>>
>> These are unnecessary tests as unsigned values are never < 0.
>>
>
Oops, i m sorry, i mistakenly took the variable as int, it should be
unsinged int.
sorry joe, you are right the are never < 0.
Thanks,
On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> As requested in the feedback from the previous patch, we've documented the
> debugfs and sysfs attributes in files in Documentation/ABI/testing. There's
> two files, one for debugfs and one for sysfs.
This is a massive improvement! Atleast as far as I've read... If you fix the
below remarks I think I'm ready to accept this file, but that's just me and
doesn't say anything about what Dmitry et al will comment on...
(...)
> + The RMI4 driver implementation exposes a set of informational and control
> + parameters via debugs. These parameters are those that typically are only
s/debugs/debugfs
(...)
> + comms_debug - (rw) Write 1 to this dump information about register
> + reads and writes to the console. Write 0 to this to turn
> + this feature off. WARNING: Imposes a major performance
> + penalty when turned on.
> + irq_debug - (rw) Write 1 to this dump information about interrupts
> + to the console. Write 0 to this to turn this feature off.
> + WARNIG: Imposes a major performance penalty when turned on.
Hm. Usually we control dynamic debug prints by standard kernel
frameworks, can you tell what is wrong with this and why you need
a custom mechanism? See the following:
Documentation/dynamic-debug-howto.txt
http://lwn.net/Articles/434833/
(...)
> +++ b/Documentation/ABI/testing/sysfs-rmi4
(...)
> + chargerinput ... (rw) User space programs can use this to tell the
> + sensor that the system is plugged into an external power
> + source (as opposed to running on batteries). This allows
> + the sensor firmware to make necessary adjustments for the
> + current capacitence regime. Write 1 to this when the
> + system is using external power, write 0 to this when the
> + system is running on batteries. See spec for full details.
I remember discussing in-kernel notifiers for this. I don't
really see the point in tunnelling a notification from the drivers/power
subsystem to the drivers/input subsystem through userspace for
no good.
It's no blocker though, I don't expect you to fix this as part of
this driver submission.
Maybe Anton can comment?
(...)
> + interrupt_enable ... (ro) This represents the current RMI4 interrupt
> + mask (F01_RMI_Ctrl1 registers). See spec for full details.
What does the userspace have to do with this stuff? Seems way
too low-level, but whatever.
(...)
> + sleepmode ... (rw) Controls power management on the device. Writing
> + 0 to this parameter puts the device into its normal operating
> + mode. Writing 1 to this parameter fully disables touch
> + sensors and similar inputs - no touch data will be reported
> + from the device in this mode. Writing 2 or 3 to this device
> + may or may not have an effect, depending on the particular
> + device - see the product specification for your sensor for
> + details.
Usually power management is controlled from kernelspace, but no
big deal, maybe userspace knows even more details in some
cases.
> + unconfigured ... (ro) This is the opposite of the configured bit,
> + described above.
So why is it needed? Isn't it implicit from the "configured" property
if this is 0 then it's unconfigured? Seems superfluous.
(...)
> +++ b/include/linux/rmi.h
(...)
> +#ifdef CONFIG_RMI4_DEBUG
> +#include <linux/debugfs.h>
> +#endif
Don't include it conditionally, always just include it whether
you use it or not.
It doesn't hurt, and doesn't cause compile problems.
(...)
> +/**
> + * struct rmi_device_platform_data_spi - provides paramters used in SPI
s/paramters/parameters/
> + * communitcations. All Synaptics SPI products support a standard SPI
s/communitcations/communications
> + * @cs_assert - For systems where the SPI subsystem does not control the CS/SSB
> + * line, or where such control is broken, you can provide a custom routine to
> + * handle a GPIO as CS/SSB. This routine will be called at the beginning and
> + * end of each SPI transaction. The RMI SPI implementation will wait
> + * pre_delay_us after this routine returns before starting the SPI transfer;
> + * and post_delay_us after completion of the SPI transfer(s) before calling it
> + * with assert==FALSE.
Hm hm, can you describe the case where this happens?
Usually we don't avoid fixes for broken drivers by duct-taping
solutions into other drivers, instead we fix the SPI driver.
I can think of systems where CS is asserted not by using
GPIO but by poking some special register for example, which
is a valid reason for including this, but working around broken
SPI drivers is not a valid reason to include this.
(Paging Mark about it.)
(...)
> +/**
> + * struct rmi_device_platform_data - system specific configuration info.
> + *
> + * @driver_name
(...)
> +struct rmi_device_platform_data {
> + char *driver_name;
I don't understand what the driver name is doing in the platform
data. The driver name should be part of the device driver struct,
and match on dev_name(struct device *), not be passed in here.
Please explain...
(...)
> +#ifdef CONFIG_RMI4_DEBUG
> + struct dentry *debugfs_root;
> +#endif
Maybe just use CONFIG_DEBUG_FS instead, it's more to the
point?
(occurs twice)
(...)
> +/**
> + * rmi_register_phys_device - register a physical device connection on the RMI
> + * bus. Physical drivers provide communication from the devices on the bus to
> + * the RMI4 sensor on a bus such as SPI, I2C, and so on.
> + *
> + * @phys: the physical device to register
> + */
Don't put the kerneldoc here in the header, put it in the code.
Only documentation for structs, enums and inline functions
go into the header files.
(Same comment for all functions.)
> +int rmi_register_phys_device(struct rmi_phys_device *phys);
(...)
> +/**
> + * Helper function to convert a 16 bit value (in host processor endianess) to
> + * a byte array in the RMI endianess for u16s. See above comment for
> + * why we dont us htons or something like that.
> + */
So in this case it's correct to document it here, because this is an
inline function.
> +static inline void hstoba(u8 *dest, u16 src)
> +{
> + dest[0] = src % 0x100;
> + dest[1] = src / 0x100;
But please use arithmetic operators (I think I said this on the last
review):
dest[0] = src & 0xFF;
dest[1] = src >> 8;
Doing it the above way makes artithmetic look like maths, and it isn't.
Besides it's done this way in most parts of the kernel and we're
familiar with it.
(...)
> +#ifdef CONFIG_RMI4_DEBUG
> +/**
> + * Utility routine to handle writes to read-only attributes. Hopefully
> + * this will never happen, but if the user does something stupid, we don't
> + * want to accept it quietly (which is what can happen if you just put NULL
> + * for the attribute's store function).
> + */
> +static inline ssize_t rmi_store_error(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + dev_warn(dev,
> + "WARNING: Attempt to write %d characters to read-only attribute %s.",
> + count, attr->attr.name);
> + return -EPERM;
> +}
Here it looks like you're hiding a lot of stuff that should be dev_warn()?
Consider my earlier point about dynamic debug.
> +static inline ssize_t rmi_show_error(struct device *dev,
> + struct device_attribute *attr, char *buf)
Dito.
Yours,
Linus Walleij
On Tue, Oct 09, 2012 at 09:43:13AM +0200, Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> > + * @cs_assert - For systems where the SPI subsystem does not control the CS/SSB
> > + * line, or where such control is broken, you can provide a custom routine to
> > + * handle a GPIO as CS/SSB. This routine will be called at the beginning and
> > + * end of each SPI transaction. The RMI SPI implementation will wait
> > + * pre_delay_us after this routine returns before starting the SPI transfer;
> > + * and post_delay_us after completion of the SPI transfer(s) before calling it
> > + * with assert==FALSE.
> Hm hm, can you describe the case where this happens?
> Usually we don't avoid fixes for broken drivers by duct-taping
> solutions into other drivers, instead we fix the SPI driver.
> I can think of systems where CS is asserted not by using
> GPIO but by poking some special register for example, which
> is a valid reason for including this, but working around broken
> SPI drivers is not a valid reason to include this.
> (Paging Mark about it.)
Yeah, this seems silly - by this logic we'd have to go round implementing
manual /CS control in every single SPI client driver which isn't
terribly sensible. The driver should just assume that the SPI
controller does what it's told. As you say if there's an issue the
relevant controller driver should take care of things.
We should also have generic support in the SPI framework for GPIO based
/CS, there's enough drivers open coding this already either due to
hardware limitations or to support extra chip selects.
The ability of SPI hardware and driver authors to get /CS right is
pretty depressing :/
On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> rmi_bus.c implements the basic functionality of the RMI bus. This file is
> greatly simplified compared to the previous patch - we've switched from
> "do it yourself" device/driver binding to using device_type to distinguish
> between the two kinds of devices on the bus (sensor devices and function
> specific devices) and using the standard bus implementation to manage devices
> and drivers.
So I think you really want Greg KH to look at this bus implementation
now. Please include Greg on future mailings...
It looks much improved from previous versions, and sorry if I am now
adding even more comments, but it's because you cleared out some
noise that was disturbing my perception so I can cleanly review
the architecture of this thing now. (I'm impressed by your work and
new high-speed turnaround time!)
> +#ifdef CONFIG_RMI4_DEBUG
> +#include <linux/debugfs.h>
> +#endif
As noted previously, drop the #ifdef:s
> +#ifdef CONFIG_RMI4_DEBUG
> +static struct dentry *rmi_debugfs_root;
> +#endif
I'd use #ifdef CONFIG_DEBUG_FS and try to
move this declaration closer to the actual debugfs
code block.
Apart from this the core bus looks good to me, but it's not my
area of expertise...
(...)
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> +#ifdef CONFIG_RMI4_DEBUG
I'd just use CONFIG_DEBUG_FS
> +struct driver_debugfs_data {
> + bool done;
> + struct rmi_device *rmi_dev;
> +};
(...)
> +#define DELAY_NAME "delay"
This is only used in one place, why not just use the string
"delay" there?
(...)
> + if (IS_ENABLED(CONFIG_RMI4_SPI) && !strncmp("spi", info->proto, 3)) {
> + data->debugfs_delay = debugfs_create_file(DELAY_NAME,
> + RMI_RW_ATTR, rmi_dev->debugfs_root, rmi_dev,
> + &delay_fops);
i.e. there.
(...)
> +/* Useful helper functions for u8* */
> +
> +static bool u8_is_any_set(u8 *target, int size)
> +{
> + int i;
> + /* We'd like to use find_first_bit, but it ALWAYS returns 1,
> + * no matter what we pass it. So we have to do this the hard way.
> + * return find_first_bit((long unsigned int *) target, size) != 0;
> + */
> + for (i = 0; i < size; i++) {
> + if (target[i])
> + return true;
> + }
> + return false;
> +}
Instead of:
if (u8_is_any_set(foo, 128) {}
Why can't you use:
if (!bitmap_empty(foo, 128*8) {}
?
If you look at the implementation in the <linux/bitmap.h> header
and __bitmap_empty() in lib/bitmap.c you will realize that this
function is already optimized like this (and I actually don't think
the RMI4 code is performance-critical for these functions anyway,
but prove me wrong!)
> +
> +/** This is here because all those casts made for some ugly code.
> + */
> +static void u8_and(u8 *dest, u8 *target1, u8 *target2, int nbits)
> +{
> + bitmap_and((long unsigned int *) dest,
> + (long unsigned int *) target1,
> + (long unsigned int *) target2,
> + nbits);
> +}
Hm, getting rid of unreadable casts is a valid case.
I'll be OK with this but maybe the real solution is to introduce such
helpers into <linux/bitmap.h>?
(...)
> +static int process_interrupt_requests(struct rmi_device *rmi_dev)
> +{
> + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> + struct device *dev = &rmi_dev->dev;
> + struct rmi_function_container *entry;
> + u8 irq_status[data->num_of_irq_regs];
Looking at this...
What does the data->num_of_irq_regs actually contain?
I just fear that it is something constant like always 2 or always 4,
so there is actually, in reality, a 16 or 32 bit register hiding in there.
In that case what you should do is to represent it as a u16 or u32 here,
just or the bits into a status word, and then walk over that status
word with something like ffs(bitword); ...
(...)
> +static int standard_resume(struct rmi_device *rmi_dev)
Standard eh? :-)
Atleast prefix with rmi4_*...
> +static int rmi_driver_suspend(struct device *dev)
> +{
> + struct rmi_device *rmi_dev = to_rmi_device(dev);
> + return standard_suspend(rmi_dev);
> +}
> +
> +static int rmi_driver_resume(struct device *dev)
> +{
> + struct rmi_device *rmi_dev = to_rmi_device(dev);
> + return standard_resume(rmi_dev);
> +}
If all these two are doing are to call another function with almost
the same name, what is the point? Just sink the code from
"standard_suspend()" and "standard_resume()" into these
functions and remove a pointless layer of abstraction.
Apart from this the core driver looks good to me.
(...)
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
(...)
> +#define simple_show_union_struct(regtype, propname, fmt)\
> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device *dev,\
> + struct device_attribute *attr, char *buf) {\
> + struct rmi_function_container *fc;\
> + struct FUNCTION_DATA *data;\
> +\
> + fc = to_rmi_function_container(dev);\
> + data = fc->data;\
> +\
> + return snprintf(buf, PAGE_SIZE, fmt,\
> + data->regtype.propname);\
> +}
OK I see the point, but is there really no other way to do this than
to #define huge static inlines like these? Is it really not possible to
create just generic functions instead of going this far?
(same comment for all)
> +union pdt_properties {
> + struct {
> + u8 reserved_1:6;
> + u8 has_bsr:1;
> + u8 reserved_2:1;
> + } __attribute__((__packed__));
> + u8 regs[1];
I don't understand what this union is trying to achieve.
regs[1] does not look right considering what you're trying to
achieve. Since the above fields require a regs[2] (9 bits!)
to be stored. Maybe write out what you're trying to do here
so I can understand it? (If everyone else in the world gets
it immediately, it's maybe me that need fixing instead...)
Apart from these remarks it's looking real nice now!
Yours,
Linus Walleij
On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]> wrote:
> The I2C physical driver is not extensively changed in terms of functionality
> since the previous patch. Management of the attention GPIO has been moved to
> rmi_driver.c (see previous email), and most of the debug related interfaces
> have been moved from sysfs to debugfs. Control of the debug features has been
> moved from compile-time to runtime switches available via debugfs.
>
> The core I2C functionality was previously ACKed by Jean Delvare. I don't
> believe that portion of the code has changed much since then, but we'd
> appreciate a second glance at this.
The above commit blurb looks more like a changelog than a description
of the actual patch. Nothing wrong with that but begin by describing
the patch first.
(...)
> +#ifdef CONFIG_RMI4_DEBUG
> +
> +#include <linux/debugfs.h>
> +#include <linux/uaccess.h>
Just move these up to the common includes. It doesn't matter
that they get included even when debug is not enabled.
> +static int setup_debugfs(struct rmi_device *rmi_dev, struct rmi_i2c_data *data);
> +static void teardown_debugfs(struct rmi_i2c_data *data);
Why do you need to forward-declare these? Can't you just move them
up above the functions using them?
> +struct i2c_debugfs_data {
> + bool done;
Done with what? ... needs some doc.
> + struct rmi_i2c_data *i2c_data;
> +};
(...)
> +static int __devinit rmi_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
(...)
> + rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
(...)
> + data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);
Can you use devm_kzalloc(&client->dev, ...) for these so you don't
need to free() them explicitly?
(...)
> +static int __devexit rmi_i2c_remove(struct i2c_client *client)
> +{
> + struct rmi_phys_device *phys = i2c_get_clientdata(client);
> + struct rmi_device_platform_data *pd = client->dev.platform_data;
> +
> + /* Can I remove this disable_device */
> + /*disable_device(phys); */
So just delete these two lines then?
Yours,
Linus Walleij
On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]> wrote:
(...)
> diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
(...)
> +config RMI4_DEBUG
> + bool "RMI4 Debugging"
> + depends on RMI4_BUS
select DEBUG_FS
This has been illustrated many times in the review. You definatley
have code depending on debugfs when this is selected.
Yours,
Linus Walleij
On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]> wrote:
> RMI Function 01 implements basic device control and power management
> behaviors for the RMI4 sensor. Since the last patch, we've decoupled rmi_f01.c
> implementation from rmi_driver.c, so rmi_f01.c acts as a standard driver
> module to handle F01 devices on the RMI bus.
>
> Like other modules, a number of attributes have been moved from sysfs to
> debugfs, depending on their expected use.
>
>
> rmi_f01.h exports definitions that we expect to be used by other functionality
> in the future (such as firmware reflash).
>
>
> Signed-off-by: Christopher Heiny <[email protected]>
>
> Cc: Dmitry Torokhov <[email protected]>
> Cc: Linus Walleij <[email protected]>
> Cc: Naveen Kumar Gaddipati <[email protected]>
> Cc: Joeri de Gram <[email protected]>
>
> ---
There is liberal whitespacing above. (No big deal, but anyway.)
(...)
> +/**
> + * @reset - set this bit to force a firmware reset of the sensor.
> + */
> +union f01_device_commands {
> + struct {
> + bool reset:1;
> + u8 reserved:7;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
I'm still scared by these unions. I see what you're doing but my
preferred style of driver writing is to use a simple u8 if you just treat
it the right way with some |= and &= ...
#include <linux/bitops.h>
#define F01_RESET BIT(0)
u8 my_command = F01_RESET;
send(&my_command);
I will not insist on this because it's a bit about programming style.
For memory-mapped devices we usually do it my way, but this
is more like some protocol and I know protocols like to do things
with structs and stuff so no big deal.
> +#ifdef CONFIG_RMI4_DEBUG
> +struct f01_debugfs_data {
> + bool done;
> + struct rmi_function_container *fc;
> +};
> +
> +static int f01_debug_open(struct inode *inodep, struct file *filp)
> +{
> + struct f01_debugfs_data *data;
> + struct rmi_function_container *fc = inodep->i_private;
> +
> + data = devm_kzalloc(&fc->dev, sizeof(struct f01_debugfs_data),
> + GFP_KERNEL);
Wait, you probably did this because I requested it, but I was maybe
wrong?
Will this not re-allocate a chunk every time you look at a debugfs
file? So it leaks memory?
In that case common kzalloc() and kfree() is the way to go, as it
is for dynamic buffers. Sorry for screwing things up for you. :-(
> + for (i = 0; i < f01->irq_count && *local_buf != 0;
> + i++, local_buf += 2) {
> + int irq_shift;
> + int interrupt_enable;
> + int result;
> +
> + irq_reg = i / 8;
> + irq_shift = i % 8;
Please stop doing these arithmetics-turned-maths things.
irq_reg = i >> 8;
irq_shift = i & 0xFF;
(...)
> +static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct rmi_function_container *fc;
> + struct f01_data *data;
> + int i, len, total_len = 0;
> + char *current_buf = buf;
> +
> + fc = to_rmi_function_container(dev);
> + data = fc->data;
> + /* loop through each irq value and copy its
> + * string representation into buf */
> + for (i = 0; i < data->irq_count; i++) {
> + int irq_reg;
> + int irq_shift;
> + int interrupt_enable;
> +
> + irq_reg = i / 8;
> + irq_shift = i % 8;
Dito.
(...)
> +static int f01_probe(struct device *dev);
Do you really need to forward-declare this?
(...)
> +static struct rmi_function_handler function_handler = {
> + .driver = {
> + .owner = THIS_MODULE,
> + .name = "rmi_f01",
> + .bus = &rmi_bus_type,
> + .probe = f01_probe,
> + .remove = f01_remove_device,
> + },
> + .func = 0x01,
> + .config = rmi_f01_config,
> + .attention = rmi_f01_attention,
> +
> +#ifdef CONFIG_PM
> + .suspend = rmi_f01_suspend,
> + .resume = rmi_f01_resume,
> +#endif /* CONFIG_PM */
> +};
Just move this block of struct *below* the probe function...
> +static __devinit int f01_probe(struct device *dev)
Header file looks OK (if these unions is the way to go...)
Yours,
Linus Walleij
On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]> wrote:
So looking closer at this one since we will use it. Maybe it's in such a
good shape now that I should be able to actually test it with the hardware?
(...)
> diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
(...)
> +#ifdef CONFIG_RMI4_DEBUG
> +#include <linux/debugfs.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#endif
Skip the #ifdef
> +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
> +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
> +
> +#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
Use existing kernel macros in <linux/kernel.h>
In this case:
#define F11_CEIL(x, y) DIV_ROUND_UP(x, y)
Or just use DIV_ROUND_UP() directly in the code, your choice.
> +#define MAX_NAME_LENGTH 256
Really? Are you sure there is not a null terminator or length byte
included so it's actually 255?
(...)
> +static int sensor_debug_open(struct inode *inodep, struct file *filp)
> +{
> + struct sensor_debugfs_data *data;
> + struct f11_2d_sensor *sensor = inodep->i_private;
> + struct rmi_function_container *fc = sensor->fc;
> +
> + data = devm_kzalloc(&fc->dev, sizeof(struct sensor_debugfs_data),
> + GFP_KERNEL);
Again I may have lead you astray. Check if this leaks memory, in that
case use kzalloc()/kfree(). Sorry :-(
(...)
> +static int f11_debug_open(struct inode *inodep, struct file *filp)
> +{
> + struct f11_debugfs_data *data;
> + struct rmi_function_container *fc = inodep->i_private;
> +
> + data = devm_kzalloc(&fc->dev, sizeof(struct f11_debugfs_data),
> + GFP_KERNEL);
Dito. :-(
(...)
> +static void rmi_f11_abs_pos_report(struct f11_data *f11,
> + struct f11_2d_sensor *sensor,
> + u8 finger_state, u8 n_finger)
(...)
> + if (axis_align->flip_y)
> + y = max(sensor->max_y - y, 0);
> +
> + /*
> + ** here checking if X offset or y offset are specified is
> + ** redundant. We just add the offsets or, clip the values
> + **
> + ** note: offsets need to be done before clipping occurs,
> + ** or we could get funny values that are outside
> + ** clipping boundaries.
> + */
This is a weird commenting style, what's wrong with a single star?
(No big deal but it stands out...)
(...)
> +static int f11_allocate_control_regs(struct rmi_device *rmi_dev,
> + struct f11_2d_device_query *device_query,
> + struct f11_2d_sensor_query *sensor_query,
> + struct f11_2d_ctrl *ctrl,
> + u16 ctrl_base_addr) {
> +
> + struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
> + struct rmi_function_container *fc = driver_data->f01_container;
> +
> + ctrl->ctrl0_9 = devm_kzalloc(&fc->dev, sizeof(union f11_2d_ctrl0_9),
> + GFP_KERNEL);
If this is called from .probe() only, this is correct.
So the rule is: use devm_* for anything that is allocated at .probe()
and released on .remove(). Any other dynamic buffers etc need to
use common kzalloc()/kfree().
> + if (!ctrl->ctrl0_9)
> + return -ENOMEM;
> + if (sensor_query->f11_2d_query7__8[0]) {
> + ctrl->ctrl10 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl10), GFP_KERNEL);
> + if (!ctrl->ctrl10)
> + return -ENOMEM;
> + }
> +
> + if (sensor_query->f11_2d_query7__8[1]) {
> + ctrl->ctrl11 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl11), GFP_KERNEL);
> + if (!ctrl->ctrl11)
> + return -ENOMEM;
> + }
> +
> + if (device_query->has_query9 && sensor_query->query9.has_pen) {
> + ctrl->ctrl20_21 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl20_21), GFP_KERNEL);
> + if (!ctrl->ctrl20_21)
> + return -ENOMEM;
> + }
> +
> + if (device_query->has_query9 && sensor_query->query9.has_proximity) {
> + ctrl->ctrl22_26 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl22_26), GFP_KERNEL);
> + if (!ctrl->ctrl22_26)
> + return -ENOMEM;
> + }
> +
> + if (device_query->has_query9 &&
> + (sensor_query->query9.has_palm_det_sensitivity ||
> + sensor_query->query9.has_suppress_on_palm_detect)) {
> + ctrl->ctrl27 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl27), GFP_KERNEL);
> + if (!ctrl->ctrl27)
> + return -ENOMEM;
> + }
> +
> + if (sensor_query->has_multi_finger_scroll) {
> + ctrl->ctrl28 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl28), GFP_KERNEL);
> + if (!ctrl->ctrl28)
> + return -ENOMEM;
> + }
> +
> + if (device_query->has_query11 && device_query->has_z_tuning) {
> + ctrl->ctrl29_30 = devm_kzalloc(&fc->dev,
> + sizeof(union f11_2d_ctrl29_30), GFP_KERNEL);
> + if (!ctrl->ctrl29_30)
> + return -ENOMEM;
> + }
All of these are probably correct as well.
> +
> + return f11_read_control_regs(rmi_dev, ctrl, ctrl_base_addr);
Hey why are you ending with a call to that function?
The function name gets misleading.
Instead call both functions in succession at the call site on
.probe().
(...)
> +static int f11_device_init(struct rmi_function_container *fc)
> +{
> + int rc;
> +
> + rc = rmi_f11_initialize(fc);
> + if (rc < 0)
> + goto err_free_data;
> +
> + rc = rmi_f11_register_devices(fc);
> + if (rc < 0)
> + goto err_free_data;
> +
> + rc = rmi_f11_create_sysfs(fc);
> + if (rc < 0)
> + goto err_free_data;
> +
> + return 0;
> +
> +err_free_data:
> + rmi_f11_free_memory(fc);
> +
> + return rc;
> +}
> +
> +static void rmi_f11_free_memory(struct rmi_function_container *fc)
> +{
> + struct f11_data *f11 = fc->data;
> + int i;
> +
> + if (f11) {
> + for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
> + kfree(f11->sensors[i].button_map);
> + }
This is wrong. The button_map was allocated with devm_kzalloc()
so it will get automatically freed. Just skip this step.
(...)
> +static int rmi_f11_initialize(struct rmi_function_container *fc)
> +{
(...)
> + rc = f11_allocate_control_regs(rmi_dev,
> + &f11->dev_query, &sensor->sens_query,
> + &f11->dev_controls, control_base_addr);
> + if (rc < 0) {
> + dev_err(&fc->dev,
> + "Failed to initialize F11 control params.\n");
"failed to allocate F11 control params"
> + return rc;
> + }
So after this call the read regs explicitly instead as described above.
(...)
> +static void register_virtual_buttons(struct rmi_function_container *fc,
> + struct f11_2d_sensor *sensor) {
> + int j;
> +
> + if (!sensor->sens_query.has_gestures)
> + return;
> + if (!sensor->virtual_buttons.buttons) {
> + dev_warn(&fc->dev, "No virtual button platform data for 2D sensor %d.\n",
> + sensor->sensor_index);
> + return;
> + }
> + /* call devm_kcalloc when it will be defined in kernel */
> + sensor->button_map = devm_kzalloc(&fc->dev,
> + sensor->virtual_buttons.buttons,
> + GFP_KERNEL);
> + if (!sensor->button_map) {
> + dev_err(&fc->dev, "Failed to allocate the virtual button map.\n");
> + return;
> + }
So as noted above, since it's using devm_kzalloc(), don't free() it.
(...)
> + sensor->mouse_input = input_dev_mouse;
> + input_dev_mouse->name = "rmi_mouse";
> + input_dev_mouse->phys = "rmi_f11/input0";
> +
> + input_dev_mouse->id.vendor = 0x18d1;
> + input_dev_mouse->id.product = 0x0210;
> + input_dev_mouse->id.version = 0x0100;
As noted in previous review, 0x18d1 is Google's vendor ID. Please use
a Synaptics Vendor ID, Product ID and version!
Hint: synaptics Vendor ID is 0x06cb.
Yours,
Linus Walleij
Hi Christopher,
> rmi_f11.c is a driver for 2D touch sensors. It has been updated to support
> the MT-B specification, partition control attributes between debugfs and sysfs,
> and to use the standard bus model for loading/unloading.
Please find comments inline.
Generally, if you want this merged as an input device sometime in the
future, you need to reduce the size and complexity of the whole
patchset. Given that you only need to feed the input subsystem with
raw data, you can make do without most of the sysfs nodes, debug
output, and internal gesture state.
The 2D sensor data, currently living in debugfs, might eventually find
a home in the input subsystem, based on the growing interest from
several directions. However, if you are just looking for a way to
transport the rmi data to userland, please consider implementing this
under a different subsystem.
> diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
> new file mode 100644
> index 0000000..bba818b
> --- /dev/null
> +++ b/drivers/input/rmi4/rmi_f11.c
> @@ -0,0 +1,2727 @@
> +/*
> + * Copyright (c) 2011,2012 Synaptics Incorporated
> + * Copyright (c) 2011 Unixphere
> + *
> + * 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 f11_data
> +#define FNUM 11
> +
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/input.h>
> +#include <linux/input/mt.h>
> +#include <linux/kconfig.h>
> +#include <linux/rmi.h>
> +#include <linux/slab.h>
> +#include "rmi_driver.h"
> +
> +#ifdef CONFIG_RMI4_DEBUG
> +#include <linux/debugfs.h>
> +#include <linux/fs.h>
> +#include <linux/uaccess.h>
> +#endif
> +
> +#define F11_MAX_NUM_OF_SENSORS 8
> +#define F11_MAX_NUM_OF_FINGERS 10
> +#define F11_MAX_NUM_OF_TOUCH_SHAPES 16
> +
> +#define F11_REL_POS_MIN -128
> +#define F11_REL_POS_MAX 127
> +
> +#define FINGER_STATE_MASK 0x03
> +#define GET_FINGER_STATE(f_states, i) \
> + ((f_states[i / 4] >> (2 * (i % 4))) & FINGER_STATE_MASK)
These could be open-coded or put in a function instead.
> +
> +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
> +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
> +
> +#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
> +#define INBOX(x, y, box) (x >= box.x && x < (box.x + box.width) \
> + && y >= box.y && y < (box.y + box.height))
> +
> +#define DEFAULT_XY_MAX 9999
> +#define DEFAULT_MAX_ABS_MT_PRESSURE 255
> +#define DEFAULT_MAX_ABS_MT_TOUCH 15
> +#define DEFAULT_MAX_ABS_MT_ORIENTATION 1
> +#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1
> +#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10
> +#define MAX_NAME_LENGTH 256
> +
> +static ssize_t f11_relreport_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf);
> +
> +static ssize_t f11_relreport_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count);
> +
> +static ssize_t f11_maxPos_show(struct device *dev,
> + struct device_attribute *attr, char *buf);
> +
> +static ssize_t f11_rezero_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count);
> +
> +static void rmi_f11_free_memory(struct rmi_function_container *fc);
> +
> +static int rmi_f11_initialize(struct rmi_function_container *fc);
> +
> +static int rmi_f11_create_sysfs(struct rmi_function_container *fc);
> +
> +static int rmi_f11_config(struct rmi_function_container *fc);
> +
> +static int rmi_f11_register_devices(struct rmi_function_container *fc);
> +
> +static void rmi_f11_free_devices(struct rmi_function_container *fc);
> +
> +static void f11_set_abs_params(struct rmi_function_container *fc, int index);
Please try to get rid of these.
> +
> +static struct device_attribute attrs[] = {
> + __ATTR(relreport, RMI_RW_ATTR, f11_relreport_show, f11_relreport_store),
> + __ATTR(maxPos, RMI_RO_ATTR, f11_maxPos_show, rmi_store_error),
> + __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, f11_rezero_store)
> +};
> +
> +/**
> + * @rezero - writing 1 to this will cause the sensor to calibrate to the
> + * current capacitive state.
> + */
> +union f11_2d_commands {
> + struct {
> + bool rezero:1;
> + u8 reserved:7;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
Maybe a constant would be more suitable here?
> +
> +/**
> + * @nbr_of_sensors - the number of 2D sensors on the touch device.
> + * @has_query9 - indicates the F11_2D_Query9 register exists.
> + * @has_query11 - indicates the F11_2D_Query11 register exists.
> + * @has_z_tuning - if set, the sensor supports Z tuning and registers
> + * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist.
> + * @has_pos_interpolation_tuning - TBD
> + * @has_w_tuning - the sensor supports Wx and Wy scaling and registers
> + * F11_2D_Ctrl36 through F11_2D_Ctrl39 exist.
> + * @has_pitch_info - the X and Y pitches of the sensor electrodes can be
> + * configured and registers F11_2D_Ctrl40 and F11_2D_Ctrl41 exist.
> + * @has_default_finger_width - the default finger width settings for the
> + * sensor can be configured and registers F11_2D_Ctrl42 through F11_2D_Ctrl44
> + * exist.
> + * @has_segmentation_aggressiveness - the sensor’s ability to distinguish
> + * multiple objects close together can be configured and register F11_2D_Ctrl45
> + * exists.
> + * @has_tx_rw_clip - the inactive outside borders of the sensor can be
> + * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist.
> + * @has_drumming_correction - the sensor can be configured to distinguish
> + * between a fast flick and a quick drumming movement and registers
> + * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist.
> + */
> +struct f11_2d_device_query {
> + union {
> + struct {
> + u8 nbr_of_sensors:3;
> + bool has_query9:1;
> + bool has_query11:1;
> + u8 reserved:3;
> + } __attribute__((__packed__));
> + u8 f11_2d_query0;
> + };
Why union here? It seems all you need is a packed struct named query0.
> +
> + union {
> + struct {
> + bool has_z_tuning:1;
> + bool has_pos_interpolation_tuning:1;
> + bool has_w_tuning:1;
> + bool has_pitch_info:1;
> + bool has_default_finger_width:1;
> + bool has_segmentation_aggressiveness:1;
> + bool has_tx_rw_clip:1;
> + bool has_drumming_correction:1;
> + } __attribute__((__packed__));
> + u8 f11_2d_query11;
> + };
> +};
Ditto.
> +
> +/**
> + * @has_pen - detection of a stylus is supported and registers F11_2D_Ctrl20
> + * and F11_2D_Ctrl21 exist.
> + * @has_proximity - detection of fingers near the sensor is supported and
> + * registers F11_2D_Ctrl22 through F11_2D_Ctrl26 exist.
> + * @has_palm_det_sensitivity - the sensor supports the palm detect sensitivity
> + * feature and register F11_2D_Ctrl27 exists.
> + * @has_two_pen_thresholds - is has_pen is also set, then F11_2D_Ctrl35 exists.
> + * @has_contact_geometry - the sensor supports the use of contact geometry to
> + * map absolute X and Y target positions and registers F11_2D_Data18.* through
> + * F11_2D_Data27 exist.
> + */
> +union f11_2d_query9 {
> + struct {
> + bool has_pen:1;
> + bool has_proximity:1;
> + bool has_palm_det_sensitivity:1;
> + bool has_suppress_on_palm_detect:1;
> + bool has_two_pen_thresholds:1;
> + bool has_contact_geometry:1;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
Ditto.
> +
> +/**
> + * @number_of_fingers - describes the maximum number of fingers the 2-D sensor
> + * supports.
> + * @has_rel - the sensor supports relative motion reporting.
> + * @has_abs - the sensor supports absolute poition reporting.
> + * @has_gestures - the sensor supports gesture reporting.
> + * @has_sensitivity_adjust - the sensor supports a global sensitivity
> + * adjustment.
> + * @configurable - the sensor supports various configuration options.
> + * @num_of_x_electrodes - the maximum number of electrodes the 2-D sensor
> + * supports on the X axis.
> + * @num_of_y_electrodes - the maximum number of electrodes the 2-D sensor
> + * supports on the Y axis.
> + * @max_electrodes - the total number of X and Y electrodes that may be
> + * configured.
> + * @abs_data_size - describes the format of data reported by the absolute
> + * data source. Only one format (the kind used here) is supported at this
> + * time.
> + * @has_anchored_finger - then the sensor supports the high-precision second
> + * finger tracking provided by the manual tracking and motion sensitivity
> + * options.
> + * @has_adjust_hyst - the difference between the finger release threshold and
> + * the touch threshold.
> + * @has_dribble - the sensor supports the generation of dribble interrupts,
> + * which may be enabled or disabled with the dribble control bit.
> + * @f11_2d_query6 - reserved.
> + * @has_single_tap - a basic single-tap gesture is supported.
> + * @has_tap_n_hold - tap-and-hold gesture is supported.
> + * @has_double_tap - double-tap gesture is supported.
> + * @has_early_tap - early tap is supported and reported as soon as the finger
> + * lifts for any tap event that could be interpreted as either a single tap
> + * or as the first tap of a double-tap or tap-and-hold gesture.
> + * @has_flick - flick detection is supported.
> + * @has_press - press gesture reporting is supported.
> + * @has_pinch - pinch gesture detection is supported.
> + * @has_palm_det - the 2-D sensor notifies the host whenever a large conductive
> + * object such as a palm or a cheek touches the 2-D sensor.
> + * @has_rotate - rotation gesture detection is supported.
> + * @has_touch_shapes - TouchShapes are supported. A TouchShape is a fixed
> + * rectangular area on the sensor that behaves like a capacitive button.
> + * @has_scroll_zones - scrolling areas near the sensor edges are supported.
> + * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported;
> + * if 0, then only two are supported.
> + * @has_multi_finger_scroll - the multifinger_scrolling bit will be set when
> + * more than one finger is involved in a scrolling action.
> + * @nbr_touch_shapes - the total number of touch shapes supported.
> + */
> +struct f11_2d_sensor_query {
> + union {
> + struct {
> + /* query1 */
> + u8 number_of_fingers:3;
> + bool has_rel:1;
> + bool has_abs:1;
> + bool has_gestures:1;
> + bool has_sensitivity_adjust:1;
> + bool configurable:1;
> + /* query2 */
> + u8 num_of_x_electrodes:7;
> + u8 reserved_1:1;
> + /* query3 */
> + u8 num_of_y_electrodes:7;
> + u8 reserved_2:1;
> + /* query4 */
> + u8 max_electrodes:7;
> + u8 reserved_3:1;
> + } __attribute__((__packed__));
> + u8 f11_2d_query1__4[4];
> + };
> +
> + union {
> + struct {
> + u8 abs_data_size:3;
> + bool has_anchored_finger:1;
> + bool has_adj_hyst:1;
> + bool has_dribble:1;
> + u8 reserved_4:2;
> + } __attribute__((__packed__));
> + u8 f11_2d_query5;
> + };
> +
> + u8 f11_2d_query6;
> +
> + union {
> + struct {
> + bool has_single_tap:1;
> + bool has_tap_n_hold:1;
> + bool has_double_tap:1;
> + bool has_early_tap:1;
> + bool has_flick:1;
> + bool has_press:1;
> + bool has_pinch:1;
> + bool padding:1;
> +
> + bool has_palm_det:1;
> + bool has_rotate:1;
> + bool has_touch_shapes:1;
> + bool has_scroll_zones:1;
> + bool has_individual_scroll_zones:1;
> + bool has_multi_finger_scroll:1;
> + } __attribute__((__packed__));
> + u8 f11_2d_query7__8[2];
> + };
> +
> + union f11_2d_query9 query9;
> +
> + union {
> + struct {
> + u8 nbr_touch_shapes:5;
> + } __attribute__((__packed__));
> + u8 f11_2d_query10;
> + };
> +};
Ditto. It is nice with some documentation, but in this case, it might
make sense to add it on the same line in the struct. Also, since an MT
device outputs the raw position data and leaves the gestures to
userspace, this whole construct is more complex than it needs to be.
> +
> +/**
> + * @reporting_mode - controls how often finger position data is reported.
> + * @abs_pos_filt - when set, enables various noise and jitter filtering
> + * algorithms for absolute reports.
> + * @rel_pos_filt - when set, enables various noise and jitter filtering
> + * algorithms for relative reports.
> + * @rel_ballistics - enables ballistics processing for the relative finger
> + * motion on the 2-D sensor.
> + * @dribble - enables the dribbling feature.
> + * @report_beyond_clip - when this is set, fingers outside the active area
> + * specified by the x_clip and y_clip registers will be reported, but with
> + * reported finger position clipped to the edge of the active area.
> + * @palm_detect_thresh - the threshold at which a wide finger is considered a
> + * palm. A value of 0 inhibits palm detection.
> + * @motion_sensitivity - specifies the threshold an anchored finger must move
> + * before it is considered no longer anchored. High values mean more
> + * sensitivity.
> + * @man_track_en - for anchored finger tracking, whether the host (1) or the
> + * device (0) determines which finger is the tracked finger.
> + * @man_tracked_finger - when man_track_en is 1, specifies whether finger 0 or
> + * finger 1 is the tracked finger.
> + * @delta_x_threshold - 2-D position update interrupts are inhibited unless
> + * the finger moves more than a certain threshold distance along the X axis.
> + * @delta_y_threshold - 2-D position update interrupts are inhibited unless
> + * the finger moves more than a certain threshold distance along the Y axis.
> + * @velocity - When rel_ballistics is set, this register defines the
> + * velocity ballistic parameter applied to all relative motion events.
> + * @acceleration - When rel_ballistics is set, this register defines the
> + * acceleration ballistic parameter applied to all relative motion events.
> + * @sensor_max_x_pos - the maximum X coordinate reported by the sensor.
> + * @sensor_max_y_pos - the maximum Y coordinate reported by the sensor.
> + */
> +union f11_2d_ctrl0_9 {
> + struct {
> + /* F11_2D_Ctrl0 */
> + u8 reporting_mode:3;
> + bool abs_pos_filt:1;
> + bool rel_pos_filt:1;
> + bool rel_ballistics:1;
> + bool dribble:1;
> + bool report_beyond_clip:1;
> + /* F11_2D_Ctrl1 */
> + u8 palm_detect_thres:4;
> + u8 motion_sensitivity:2;
> + bool man_track_en:1;
> + bool man_tracked_finger:1;
> + /* F11_2D_Ctrl2 and 3 */
> + u8 delta_x_threshold:8;
> + u8 delta_y_threshold:8;
> + /* F11_2D_Ctrl4 and 5 */
> + u8 velocity:8;
> + u8 acceleration:8;
> + /* F11_2D_Ctrl6 thru 9 */
> + u16 sensor_max_x_pos:12;
> + u8 ctrl7_reserved:4;
> + u16 sensor_max_y_pos:12;
> + u8 ctrl9_reserved:4;
> + } __attribute__((__packed__));
> + struct {
> + u8 regs[10];
> + u16 address;
> + } __attribute__((__packed__));
> +};
> +
> +/**
> + * @single_tap_int_enable - enable tap gesture recognition.
> + * @tap_n_hold_int_enable - enable tap-and-hold gesture recognition.
> + * @double_tap_int_enable - enable double-tap gesture recognition.
> + * @early_tap_int_enable - enable early tap notification.
> + * @flick_int_enable - enable flick detection.
> + * @press_int_enable - enable press gesture recognition.
> + * @pinch_int_enable - enable pinch detection.
> + */
> +union f11_2d_ctrl10 {
> + struct {
> + bool single_tap_int_enable:1;
> + bool tap_n_hold_int_enable:1;
> + bool double_tap_int_enable:1;
> + bool early_tap_int_enable:1;
> + bool flick_int_enable:1;
> + bool press_int_enable:1;
> + bool pinch_int_enable:1;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +/**
> + * @palm_detect_int_enable - enable palm detection feature.
> + * @rotate_int_enable - enable rotate gesture detection.
> + * @touch_shape_int_enable - enable the TouchShape feature.
> + * @scroll_zone_int_enable - enable scroll zone reporting.
> + * @multi_finger_scroll_int_enable - enable the multfinger scroll feature.
> + */
> +union f11_2d_ctrl11 {
> + struct {
> + bool palm_detect_int_enable:1;
> + bool rotate_int_enable:1;
> + bool touch_shape_int_enable:1;
> + bool scroll_zone_int_enable:1;
> + bool multi_finger_scroll_int_enable:1;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +union f11_2d_ctrl12 {
> + struct {
> + u8 sensor_map:7;
> + bool xy_sel:1;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +/**
> + * @sens_adjustment - allows a host to alter the overall sensitivity of a
> + * 2-D sensor. A positive value in this register will make the sensor more
> + * sensitive than the factory defaults, and a negative value will make it
> + * less sensitive.
> + * @hyst_adjustment - increase the touch/no-touch hysteresis by 2 Z-units for
> + * each one unit increment in this setting.
> + */
> +union f11_2d_ctrl14 {
> + struct {
> + s8 sens_adjustment:5;
> + u8 hyst_adjustment:3;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +/**
> + * @max_tap_time - the maximum duration of a tap, in 10-millisecond units.
> + */
> +union f11_2d_ctrl15 {
> + struct {
> + u8 max_tap_time:8;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +/**
> + * @min_press_time - The minimum duration required for stationary finger(s) to
> + * generate a press gesture, in 10-millisecond units.
> + */
> +union f11_2d_ctrl16 {
> + struct {
> + u8 min_press_time:8;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +/**
> + * @max_tap_distance - Determines the maximum finger movement allowed during
> + * a tap, in 0.1-millimeter units.
> + */
> +union f11_2d_ctrl17 {
> + struct {
> + u8 max_tap_distance:8;
> + } __attribute__((__packed__));
> + u8 reg;
> +};
> +
> +/**
> + * @min_flick_distance - the minimum finger movement for a flick gesture,
> + * in 1-millimeter units.
> + * @min_flick_speed - the minimum finger speed for a flick gesture, in
> + * 10-millimeter/second units.
> + */
> +union f11_2d_ctrl18_19 {
> + struct {
> + u8 min_flick_distance:8;
> + u8 min_flick_speed:8;
> + } __attribute__((__packed__));
> + u8 reg[2];
> +};
> +
> +/**
> + * @pen_detect_enable - enable reporting of stylus activity.
> + * @pen_jitter_filter_enable - Setting this enables the stylus anti-jitter
> + * filter.
> + * @pen_z_threshold - This is the stylus-detection lower threshold. Smaller
> + * values result in higher sensitivity.
> + */
> +union f11_2d_ctrl20_21 {
> + struct {
> + bool pen_detect_enable:1;
> + bool pen_jitter_filter_enable:1;
> + u8 ctrl20_reserved:6;
> + u8 pen_z_threshold:8;
> + } __attribute__((__packed__));
> + u8 reg[2];
> +};
Given that most of the above will not be used in this driver, it can
probably be compressed quite a bit.
> +
> +/**
> + * These are not accessible through sysfs yet.
> + *
> + * @proximity_detect_int_en - enable proximity detection feature.
> + * @proximity_jitter_filter_en - enables an anti-jitter filter on proximity
> + * data.
> + * @proximity_detection_z_threshold - the threshold for finger-proximity
> + * detection.
> + * @proximity_delta_x_threshold - In reduced-reporting modes, this is the
> + * threshold for proximate-finger movement in the direction parallel to the
> + * X-axis.
> + * @proximity_delta_y_threshold - In reduced-reporting modes, this is the
> + * threshold for proximate-finger movement in the direction parallel to the
> + * Y-axis.
> + * * @proximity_delta_Z_threshold - In reduced-reporting modes, this is the
> + * threshold for proximate-finger movement in the direction parallel to the
> + * Z-axis.
> + */
> +union f11_2d_ctrl22_26 {
> + struct {
> + /* control 22 */
> + bool proximity_detect_int_en:1;
> + bool proximity_jitter_filter_en:1;
> + u8 f11_2d_ctrl6_b3__7:6;
> +
> + /* control 23 */
> + u8 proximity_detection_z_threshold;
> +
> + /* control 24 */
> + u8 proximity_delta_x_threshold;
> +
> + /* control 25 */
> + u8 proximity_delta_y_threshold;
> +
> + /* control 26 */
> + u8 proximity_delta_z_threshold;
> + } __attribute__((__packed__));
> + u8 regs[5];
> +};
> +
> +/**
> + * @palm_detecy_sensitivity - When this value is small, smaller objects will
> + * be identified as palms; when this value is large, only larger objects will
> + * be identified as palms. 0 represents the factory default.
> + * @suppress_on_palm_detect - when set, all F11 interrupts except palm_detect
> + * are suppressed while a palm is detected.
> + */
> +union f11_2d_ctrl27 {
> + struct {
> + s8 palm_detect_sensitivity:4;
> + bool suppress_on_palm_detect:1;
> + u8 f11_2d_ctrl27_b5__7:3;
> + } __attribute__((__packed__));
> + u8 regs[1];
> +};
> +
> +/**
> + * @multi_finger_scroll_mode - allows choice of multi-finger scroll mode and
> + * determines whether and how X or Y displacements are reported.
> + * @edge_motion_en - enables the edge_motion feature.
> + * @multi_finger_scroll_momentum - controls the length of time that scrolling
> + * continues after fingers have been lifted.
> + */
> +union f11_2d_ctrl28 {
> + struct {
> + u8 multi_finger_scroll_mode:2;
> + bool edge_motion_en:1;
> + bool f11_2d_ctrl28b_3:1;
> + u8 multi_finger_scroll_momentum:4;
> + } __attribute__((__packed__));
> + u8 regs[1];
> +};
> +
> +/**
> + * @z_touch_threshold - Specifies the finger-arrival Z threshold. Large values
> + * may cause smaller fingers to be rejected.
> + * @z_touch_hysteresis - Specifies the difference between the finger-arrival
> + * Z threshold and the finger-departure Z threshold.
> + */
> +union f11_2d_ctrl29_30 {
> + struct {
> + u8 z_touch_threshold;
> + u8 z_touch_hysteresis;
> + } __attribute__((__packed__));
> + struct {
> + u8 regs[2];
> + u16 address;
> + } __attribute__((__packed__));
> +};
> +
> +
> +struct f11_2d_ctrl {
> + union f11_2d_ctrl0_9 *ctrl0_9;
> + union f11_2d_ctrl10 *ctrl10;
> + union f11_2d_ctrl11 *ctrl11;
> + union f11_2d_ctrl12 *ctrl12;
> + u8 ctrl12_size;
> + union f11_2d_ctrl14 *ctrl14;
> + union f11_2d_ctrl15 *ctrl15;
> + union f11_2d_ctrl16 *ctrl16;
> + union f11_2d_ctrl17 *ctrl17;
> + union f11_2d_ctrl18_19 *ctrl18_19;
> + union f11_2d_ctrl20_21 *ctrl20_21;
> + union f11_2d_ctrl22_26 *ctrl22_26;
> + union f11_2d_ctrl27 *ctrl27;
> + union f11_2d_ctrl28 *ctrl28;
> + union f11_2d_ctrl29_30 *ctrl29_30;
> +};
Will any of this data be used at all?
> +
> +/**
> + * @x_msb - top 8 bits of X finger position.
> + * @y_msb - top 8 bits of Y finger position.
> + * @x_lsb - bottom 4 bits of X finger position.
> + * @y_lsb - bottom 4 bits of Y finger position.
> + * @w_y - contact patch width along Y axis.
> + * @w_x - contact patch width along X axis.
> + * @z - finger Z value (proxy for pressure).
> + */
> +struct f11_2d_data_1_5 {
> + u8 x_msb;
> + u8 y_msb;
> + u8 x_lsb:4;
> + u8 y_lsb:4;
> + u8 w_y:4;
> + u8 w_x:4;
> + u8 z;
> +};
> +
> +/**
> + * @delta_x - relative motion along X axis.
> + * @delta_y - relative motion along Y axis.
> + */
> +struct f11_2d_data_6_7 {
> + s8 delta_x;
> + s8 delta_y;
> +};
> +
> +/**
> + * @single_tap - a single tap was recognized.
> + * @tap_and_hold - a tap-and-hold gesture was recognized.
> + * @double_tap - a double tap gesture was recognized.
> + * @early_tap - a tap gesture might be happening.
> + * @flick - a flick gesture was detected.
> + * @press - a press gesture was recognized.
> + * @pinch - a pinch gesture was detected.
> + */
> +struct f11_2d_data_8 {
> + bool single_tap:1;
> + bool tap_and_hold:1;
> + bool double_tap:1;
> + bool early_tap:1;
> + bool flick:1;
> + bool press:1;
> + bool pinch:1;
> +};
> +
> +/**
> + * @palm_detect - a palm or other large object is in contact with the sensor.
> + * @rotate - a rotate gesture was detected.
> + * @shape - a TouchShape has been activated.
> + * @scrollzone - scrolling data is available.
> + * @finger_count - number of fingers involved in the reported gesture.
> + */
> +struct f11_2d_data_9 {
> + bool palm_detect:1;
> + bool rotate:1;
> + bool shape:1;
> + bool scrollzone:1;
> + u8 finger_count:3;
> +};
> +
> +/**
> + * @pinch_motion - when a pinch gesture is detected, this is the change in
> + * distance between the two fingers since this register was last read.
> + */
> +struct f11_2d_data_10 {
> + s8 pinch_motion;
> +};
> +
> +/**
> + * @x_flick_dist - when a flick gesture is detected, the distance of flick
> + * gesture in X direction.
> + * @y_flick_dist - when a flick gesture is detected, the distance of flick
> + * gesture in Y direction.
> + * @flick_time - the total time of the flick gesture, in 10ms units.
> + */
> +struct f11_2d_data_10_12 {
> + s8 x_flick_dist;
> + s8 y_flick_dist;
> + u8 flick_time;
> +};
> +
> +/**
> + * @motion - when a rotate gesture is detected, the accumulated distance
> + * of the rotate motion. Clockwise motion is positive and counterclockwise
> + * motion is negative.
> + * @finger_separation - when a rotate gesture is detected, the distance
> + * between the fingers.
> + */
> +struct f11_2d_data_11_12 {
> + s8 motion;
> + u8 finger_separation;
> +};
> +
> +/**
> + * @shape_n - a bitmask of the currently activate TouchShapes (if any).
> + */
> +struct f11_2d_data_13 {
> + u8 shape_n;
> +};
> +
> +/**
> + * @horizontal - chiral scrolling distance in the X direction.
> + * @vertical - chiral scrolling distance in the Y direction.
> + */
> +struct f11_2d_data_14_15 {
> + s8 horizontal;
> + s8 vertical;
> +};
> +
> +/**
> + * @x_low - scroll zone motion along the lower edge of the sensor.
> + * @y_right - scroll zone motion along the right edge of the sensor.
> + * @x_upper - scroll zone motion along the upper edge of the sensor.
> + * @y_left - scroll zone motion along the left edge of the sensor.
> + */
> +struct f11_2d_data_14_17 {
> + s8 x_low;
> + s8 y_right;
> + s8 x_upper;
> + s8 y_left;
> +};
> +
> +struct f11_2d_data {
> + u8 *f_state;
> + const struct f11_2d_data_1_5 *abs_pos;
> + const struct f11_2d_data_6_7 *rel_pos;
> + const struct f11_2d_data_8 *gest_1;
> + const struct f11_2d_data_9 *gest_2;
> + const struct f11_2d_data_10 *pinch;
> + const struct f11_2d_data_10_12 *flick;
> + const struct f11_2d_data_11_12 *rotate;
> + const struct f11_2d_data_13 *shapes;
> + const struct f11_2d_data_14_15 *multi_scroll;
> + const struct f11_2d_data_14_17 *scroll_zones;
> +};
> +
> +struct f11_2d_sensor {
> + struct rmi_f11_2d_axis_alignment axis_align;
> + struct f11_2d_sensor_query sens_query;
> + struct f11_2d_data data;
> + int prev_x[F11_MAX_NUM_OF_FINGERS];
> + int prev_y[F11_MAX_NUM_OF_FINGERS];
> + u16 max_x;
> + u16 max_y;
> + u8 nbr_fingers;
> + u8 finger_tracker[F11_MAX_NUM_OF_FINGERS];
> + u8 *data_pkt;
> + int pkt_size;
> + u8 sensor_index;
> + u8 *button_map;
> + struct rmi_f11_virtualbutton_map virtual_buttons;
> + bool type_a;
> + char input_name[MAX_NAME_LENGTH];
> + char input_phys[MAX_NAME_LENGTH];
> + struct input_dev *input;
> + struct input_dev *mouse_input;
> + struct rmi_function_container *fc;
> +
> +#ifdef CONFIG_RMI4_DEBUG
> + struct dentry *debugfs_flip;
> + struct dentry *debugfs_clip;
> + struct dentry *debugfs_delta_threshold;
> + struct dentry *debugfs_offset;
> + struct dentry *debugfs_swap;
> + struct dentry *debugfs_type_a;
> +#endif
> +};
Up to this point in the file, very little is essential to the input deivce.
> +
> +struct f11_data {
> + struct f11_2d_device_query dev_query;
> + struct f11_2d_ctrl dev_controls;
> + struct mutex dev_controls_mutex;
> + u16 rezero_wait_ms;
> + struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
> +
> +#ifdef CONFIG_RMI4_DEBUG
> + struct dentry *debugfs_rezero_wait;
> +#endif
> +};
> +
> +enum finger_state_values {
> + F11_NO_FINGER = 0x00,
> + F11_PRESENT = 0x01,
> + F11_INACCURATE = 0x02,
> + F11_RESERVED = 0x03
> +};
> +
> +/* ctrl sysfs files */
> +show_store_union_struct_prototype(abs_pos_filt)
> +show_store_union_struct_prototype(z_touch_threshold)
> +show_store_union_struct_prototype(z_touch_hysteresis)
> +
> +#ifdef CONFIG_RMI4_DEBUG
> +
> +struct sensor_debugfs_data {
> + bool done;
> + struct f11_2d_sensor *sensor;
> +};
And this is really needed?
[...]
> +static void rmi_f11_abs_pos_report(struct f11_data *f11,
> + struct f11_2d_sensor *sensor,
> + u8 finger_state, u8 n_finger)
> +{
> + struct f11_2d_data *data = &sensor->data;
> + struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
> + u8 prev_state = sensor->finger_tracker[n_finger];
No need to keep track of the old state.
> + int x, y, z;
> + int w_x, w_y, w_max, w_min, orient;
> + int temp;
> +
> + if (prev_state && !finger_state) {
> + /* this is a release */
> + x = y = z = w_max = w_min = orient = 0;
This data is not sent during a release.
> + } else if (!prev_state && !finger_state) {
> + /* nothing to report */
> + return;
> + } else {
> + x = ((data->abs_pos[n_finger].x_msb << 4) |
> + data->abs_pos[n_finger].x_lsb);
> + y = ((data->abs_pos[n_finger].y_msb << 4) |
> + data->abs_pos[n_finger].y_lsb);
> + z = data->abs_pos[n_finger].z;
> + w_x = data->abs_pos[n_finger].w_x;
> + w_y = data->abs_pos[n_finger].w_y;
> + w_max = max(w_x, w_y);
> + w_min = min(w_x, w_y);
> +
> + if (axis_align->swap_axes) {
> + temp = x;
> + x = y;
> + y = temp;
> + temp = w_x;
> + w_x = w_y;
> + w_y = temp;
> + }
> +
> + orient = w_x > w_y ? 1 : 0;
> +
> + if (axis_align->flip_x)
> + x = max(sensor->max_x - x, 0);
> +
> + if (axis_align->flip_y)
> + y = max(sensor->max_y - y, 0);
> +
> + /*
> + ** here checking if X offset or y offset are specified is
> + ** redundant. We just add the offsets or, clip the values
> + **
> + ** note: offsets need to be done before clipping occurs,
> + ** or we could get funny values that are outside
> + ** clipping boundaries.
> + */
> + x += axis_align->offset_X;
> + y += axis_align->offset_Y;
> + x = max(axis_align->clip_X_low, x);
> + y = max(axis_align->clip_Y_low, y);
> + if (axis_align->clip_X_high)
> + x = min(axis_align->clip_X_high, x);
> + if (axis_align->clip_Y_high)
> + y = min(axis_align->clip_Y_high, y);
Why is the clipping configurable?
> +
> + }
> +
> + /* Some UIs ignore W of zero, so we fudge it to 1 for pens. */
> + if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
> + get_tool_type(sensor, finger_state) == MT_TOOL_PEN) {
> + w_max = max(1, w_max);
> + w_min = max(1, w_min);
> + }
Is this not true for all tool types?
> +
> + if (sensor->type_a) {
> + input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
> + input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
> + get_tool_type(sensor, finger_state));
> + } else {
> + input_mt_slot(sensor->input, n_finger);
> + input_mt_report_slot_state(sensor->input,
> + get_tool_type(sensor, finger_state), finger_state);
> + }
The driver should only report MT-B, please.
> + input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
> + input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max);
> + input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min);
> + input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient);
> + input_report_abs(sensor->input, ABS_MT_POSITION_X, x);
> + input_report_abs(sensor->input, ABS_MT_POSITION_Y, y);
Only if finger_state is true.
> + dev_dbg(&sensor->fc->dev,
> + "finger[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
> + n_finger, finger_state, x, y, z, w_max, w_min);
> + /* MT sync between fingers */
> + if (sensor->type_a)
> + input_mt_sync(sensor->input);
> +
> + sensor->finger_tracker[n_finger] = finger_state;
> +}
> +
> +#ifdef CONFIG_RMI4_VIRTUAL_BUTTON
> +static int rmi_f11_virtual_button_handler(struct f11_2d_sensor *sensor)
> +{
> + int i;
> + int x;
> + int y;
> + struct rmi_button_map *virtualbutton_map;
> +
> + if (sensor->sens_query.has_gestures &&
> + sensor->data.gest_1->single_tap) {
> + virtualbutton_map = &sensor->virtualbutton_map;
> + x = ((sensor->data.abs_pos[0].x_msb << 4) |
> + sensor->data.abs_pos[0].x_lsb);
> + y = ((sensor->data.abs_pos[0].y_msb << 4) |
> + sensor->data.abs_pos[0].y_lsb);
> + for (i = 0; i < virtualbutton_map->buttons; i++) {
> + if (INBOX(x, y, virtualbutton_map->map[i])) {
> + input_report_key(sensor->input,
> + virtualbutton_map->map[i].code, 1);
> + input_report_key(sensor->input,
> + virtualbutton_map->map[i].code, 0);
> + input_sync(sensor->input);
> + return 0;
> + }
> + }
> + }
> + return 0;
> +}
> +#else
> +#define rmi_f11_virtual_button_handler(sensor)
> +#endif
No virtual buttons, please, this can easily be mapped in userspace.
> +static void rmi_f11_finger_handler(struct f11_data *f11,
> + struct f11_2d_sensor *sensor)
> +{
> + const u8 *f_state = sensor->data.f_state;
> + u8 finger_state;
> + u8 finger_pressed_count;
> + u8 i;
> +
> + for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
> + /* Possible of having 4 fingers per f_statet register */
> + finger_state = GET_FINGER_STATE(f_state, i);
> +
> + if (finger_state == F11_RESERVED) {
> + pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
> + i, finger_state);
> + continue;
> + } else if ((finger_state == F11_PRESENT) ||
> + (finger_state == F11_INACCURATE)) {
> + finger_pressed_count++;
> + }
> +
> + if (sensor->data.abs_pos)
> + rmi_f11_abs_pos_report(f11, sensor, finger_state, i);
> +
> + if (sensor->data.rel_pos)
> + rmi_f11_rel_pos_report(sensor, i);
> + }
> + input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
Please use input_mt_sync_frame() here instead.
> + input_sync(sensor->input);
> +}
Stopping here.
Thanks,
Henrik
Joe Perches wrote:
> On Fri, 2012-10-05 at 21:09 -0700, Christopher Heiny wrote:
> []
>
> Just some trivial comments:
Thanks - see below for responses.
> > diff --git a/drivers/input/rmi4/rmi_driver.c
> > b/drivers/input/rmi4/rmi_driver.c
> []
>
> > @@ -0,0 +1,1529 @@
>
> []
>
> > +static ssize_t delay_write(struct file *filp, const char __user *buffer,
> > + size_t size, loff_t *offset) {
> > + struct driver_debugfs_data *data = filp->private_data;
> > + struct rmi_device_platform_data *pdata =
> > + data->rmi_dev->phys->dev->platform_data;
> > + int retval;
> > + char local_buf[size];
> > + unsigned int new_read_delay;
> > + unsigned int new_write_delay;
> > + unsigned int new_block_delay;
> > + unsigned int new_pre_delay;
> > + unsigned int new_post_delay;
> > +
> > + retval = copy_from_user(local_buf, buffer, size);
> > + if (retval)
> > + return -EFAULT;
> > +
> > + retval = sscanf(local_buf, "%u %u %u %u %u", &new_read_delay,
> > + &new_write_delay, &new_block_delay,
> > + &new_pre_delay, &new_post_delay);
> > + if (retval != 5) {
> > + dev_err(&data->rmi_dev->dev,
> > + "Incorrect number of values provided for delay.");
> > + return -EINVAL;
> > + }
> > + if (new_read_delay < 0) {
>
> These are unnecessary tests as unsigned values are never < 0.
Right. Thought we'd taken care of most of the silliness like that, but obviously missed some. We'll recheck the codebase.
[snip]
> > +static ssize_t phys_read(struct file *filp, char __user *buffer, size_t
> > size, + loff_t *offset) {
> > + struct driver_debugfs_data *data = filp->private_data;
> > + struct rmi_phys_info *info = &data->rmi_dev->phys->info;
> > + int retval;
> > + char local_buf[size];
>
> size comes from where? possible stack overflow?
Hmmmm. Good point. We'll look at this.
[snip]
> []
>
> > + list_for_each_entry(entry, &data->rmi_functions.list, list)
> > + if (entry->irq_mask)
> > + process_one_interrupt(entry, irq_status,
> > + data);
>
> style nit, it'd be nicer with braces.
I agree with you, but checkpatch.pl doesn't. :-(
>
> > diff --git a/drivers/input/rmi4/rmi_driver.h
> > b/drivers/input/rmi4/rmi_driver.h
> []
>
> > @@ -0,0 +1,438 @@
> >
> > +
> > +#define tricat(x, y, z) tricat_(x, y, z)
> > +
> > +#define tricat_(x, y, z) x##y##z
>
> I think these tricat macros are merely obfuscating
> and don't need to be used.
tricat is used internally by another collection of macros that helps generate sysfs files. In particular, it's used to generate the RMI4 register name symbols.-
On Thu, 2012-10-11 at 02:49 +0000, Christopher Heiny wrote:
> Joe Perches wrote:
[]
> > > + list_for_each_entry(entry, &data->rmi_functions.list, list)
> > > + if (entry->irq_mask)
> > > + process_one_interrupt(entry, irq_status,
> > > + data);
> >
> > style nit, it'd be nicer with braces.
>
> I agree with you, but checkpatch.pl doesn't. :-(
Sure it does.
$ cat t.c
{
list_for_each_entry(entry, &data->rmi_functions.list, list) {
if (entry->irq_mask)
process_one_interrupt(entry, irq_status, data);
}
}
$ ./scripts/checkpatch.pl --strict -f t.c
total: 0 errors, 0 warnings, 0 checks, 7 lines checked
t.c has no obvious style problems and is ready for submission.
Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> > As requested in the feedback from the previous patch, we've documented the
> > debugfs and sysfs attributes in files in
> > Documentation/ABI/testing. There's two files, one for debugfs and one
> > for sysfs.
>
> This is a massive improvement! Atleast as far as I've read... If you fix the
> below remarks I think I'm ready to accept this file, but that's just me and
> doesn't say anything about what Dmitry et al will comment on...
Thanks! See my comments below.
>
> (...)
>
> > + The RMI4 driver implementation exposes a set of informational and
> > control + parameters via debugs. These parameters are those that
> > typically are only
> s/debugs/debugfs
>
> (...)
>
> > + comms_debug - (rw) Write 1 to this dump information about
> > register + reads and writes to the console. Write 0 to
> > this to turn + this feature off. WARNING: Imposes a
> > major performance + penalty when turned on.
> > + irq_debug - (rw) Write 1 to this dump information about
> > interrupts + to the console. Write 0 to this to turn
> > this feature off. + WARNIG: Imposes a major performance
> > penalty when turned on.
> Hm. Usually we control dynamic debug prints by standard kernel
> frameworks, can you tell what is wrong with this and why you need
> a custom mechanism? See the following:
> Documentation/dynamic-debug-howto.txt
> http://lwn.net/Articles/434833/
The current arrangement was arrived at after some discussion with customers. Originally we went with the Kconfig based approach you suggested in August. However, the response from our guinea pigs, um, very helpful test customers, was "AAAAAAAggh! Too complicated and too static!!" As a result we explored alternatives. The dynamic debug interface was considered, but it is usually disabled in our customer's kernel configurations, even during development. In the end, we arrived at some simple debugfs on/off switches for the more verbose features (like comms_debug and irq_debug, above).
If this is a deal-breaker, I can go back to the customers and see if they are willing to consider enabling dynamic debug during prototyping and development.
>
> (...)
>
> > +++ b/Documentation/ABI/testing/sysfs-rmi4
>
> (...)
>
> > + chargerinput ... (rw) User space programs can use this to tell
> > the + sensor that the system is plugged into an external
> > power + source (as opposed to running on
> > batteries). This allows + the sensor firmware to make
> > necessary adjustments for the + current capacitence
> > regime. Write 1 to this when the + system is using
> > external power, write 0 to this when the + system is
> > running on batteries. See spec for full details.
> I remember discussing in-kernel notifiers for this. I don't
> really see the point in tunnelling a notification from the drivers/power
> subsystem to the drivers/input subsystem through userspace for
> no good.
>
> It's no blocker though, I don't expect you to fix this as part of
> this driver submission.
>
> Maybe Anton can comment?
Hmmm. I agree that it'd be good to avoid looping through userspace. But....
I found ways to notfiy the kernel that the charger is plugged/unplugged, but that's only useful if you're a battery charger device driver. I also found ways for userspace to get notification of charger events. I didn't spot any way for in-kernel drivers to get notification of such events. Perhaps I'm not looking the right places, though - can you provide a pointer?
>
> (...)
>
> > + interrupt_enable ... (ro) This represents the current RMI4
> > interrupt + mask (F01_RMI_Ctrl1 registers). See spec
> > for full details.
> What does the userspace have to do with this stuff? Seems way
> too low-level, but whatever.
It's primarily used in hardware prototyping and bring up. Perhaps it belongs in debugfs in that case.
>
> (...)
>
> > + sleepmode ... (rw) Controls power management on the
> > device. Writing + 0 to this parameter puts the device
> > into its normal operating + mode. Writing 1 to this
> > parameter fully disables touch + sensors and similar
> > inputs - no touch data will be reported + from the
> > device in this mode. Writing 2 or 3 to this device
> > + may or may not have an effect, depending on the
> > particular + device - see the product specification for
> > your sensor for + details.
>
> Usually power management is controlled from kernelspace, but no
> big deal, maybe userspace knows even more details in some
> cases.
Well, in some cases userspace does think it knows more :-). This one should definitely stay in sysfs.
>
> > + unconfigured ... (ro) This is the opposite of the configured
> > bit, + described above.
>
> So why is it needed? Isn't it implicit from the "configured" property
> if this is 0 then it's unconfigured? Seems superfluous.
You're right - we can omit one of them from sysfs as currently implemented.
>
> (...)
>
> > +++ b/include/linux/rmi.h
>
> (...)
>
> > +#ifdef CONFIG_RMI4_DEBUG
> > +#include <linux/debugfs.h>
> > +#endif
>
> Don't include it conditionally, always just include it whether
> you use it or not.
> It doesn't hurt, and doesn't cause compile problems.
>
> (...)
Will fix. I won't re-comment on the other places where you call out this sort of #ifdef.
>
> > +/**
> > + * struct rmi_device_platform_data_spi - provides paramters used in SPI
>
> s/paramters/parameters/
>
> > + * communitcations. All Synaptics SPI products support a standard SPI
>
> s/communitcations/communications
>
> > + * @cs_assert - For systems where the SPI subsystem does not control the
> > CS/SSB + * line, or where such control is broken, you can provide a
> > custom routine to + * handle a GPIO as CS/SSB. This routine will be
> > called at the beginning and + * end of each SPI transaction. The RMI SPI
> > implementation will wait + * pre_delay_us after this routine returns
> > before starting the SPI transfer; + * and post_delay_us after completion
> > of the SPI transfer(s) before calling it + * with assert==FALSE.
>
> Hm hm, can you describe the case where this happens?
>
> Usually we don't avoid fixes for broken drivers by duct-taping
> solutions into other drivers, instead we fix the SPI driver.
>
> I can think of systems where CS is asserted not by using
> GPIO but by poking some special register for example, which
> is a valid reason for including this, but working around broken
> SPI drivers is not a valid reason to include this.
>
> (Paging Mark about it.)
I see Mark has replied - I'll address this there.
>
> (...)
>
> > +/**
> > + * struct rmi_device_platform_data - system specific configuration info.
> > + *
> > + * @driver_name
>
> (...)
>
> > +struct rmi_device_platform_data {
> > + char *driver_name;
>
> I don't understand what the driver name is doing in the platform
> data. The driver name should be part of the device driver struct,
> and match on dev_name(struct device *), not be passed in here.
>
> Please explain...
This is a vestige of the old roll-your-own bus matching code. We'll get rid of it.
>
> (...)
>
> > +#ifdef CONFIG_RMI4_DEBUG
> > + struct dentry *debugfs_root;
> > +#endif
>
> Maybe just use CONFIG_DEBUG_FS instead, it's more to the
> point?
>
> (occurs twice)
CONFIG_DEBUG_FS might be enabled, but the customer might not desire RMI4 debugging features for some reason.
>
> (...)
>
> > +/**
> > + * rmi_register_phys_device - register a physical device connection on
> > the RMI + * bus. Physical drivers provide communication from the devices
> > on the bus to + * the RMI4 sensor on a bus such as SPI, I2C, and so on.
> > + *
> > + * @phys: the physical device to register
> > + */
>
> Don't put the kerneldoc here in the header, put it in the code.
>
> Only documentation for structs, enums and inline functions
> go into the header files.
>
> (Same comment for all functions.)
OK - thanks for clarification.
>
> > +int rmi_register_phys_device(struct rmi_phys_device *phys);
>
> (...)
>
> > +/**
> > + * Helper function to convert a 16 bit value (in host processor
> > endianess) to + * a byte array in the RMI endianess for u16s. See above
> > comment for + * why we dont us htons or something like that.
> > + */
>
> So in this case it's correct to document it here, because this is an
> inline function.
>
> > +static inline void hstoba(u8 *dest, u16 src)
> > +{
> > + dest[0] = src % 0x100;
> > + dest[1] = src / 0x100;
>
> But please use arithmetic operators (I think I said this on the last
> review):
>
> dest[0] = src & 0xFF;
> dest[1] = src >> 8;
>
> Doing it the above way makes artithmetic look like maths, and it isn't.
> Besides it's done this way in most parts of the kernel and we're
> familiar with it.
Yes, you mentioned it previously. I'm somewhat paranoid, though, and don't trust the shift/mask method to work correctly on big-endian machines. If the shifts can be relied on to behave (I'm guessing the answer is "yes", since you say this idiom is used widely in the kernel), then I'll change it.
>
> (...)
>
> > +#ifdef CONFIG_RMI4_DEBUG
> > +/**
> > + * Utility routine to handle writes to read-only attributes. Hopefully
> > + * this will never happen, but if the user does something stupid, we
> > don't
> > + * want to accept it quietly (which is what can happen if you just put
> > NULL + * for the attribute's store function).
> > + */
> > +static inline ssize_t rmi_store_error(struct device *dev,
> > + struct device_attribute *attr,
> > + const char *buf, size_t count)
> > +{
> > + dev_warn(dev,
> > + "WARNING: Attempt to write %d characters to read-only
> > attribute %s.", + count, attr->attr.name);
> > + return -EPERM;
> > +}
>
> Here it looks like you're hiding a lot of stuff that should be dev_warn()?
> Consider my earlier point about dynamic debug.
In previous patch submissions, we always used these warning functions. But in the feedback on those patches, we were asked to just make sysfs show/store NULL if the attribute is write/read only. However, during their development process, our customers want to see the warnings if the attributes are accessed incorrectly. So we made these warnings a debug option.
Mark Brown wrote:
> On Tue, Oct 09, 2012 at 09:43:13AM +0200, Linus Walleij wrote:
> > On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> > > + * @cs_assert - For systems where the SPI subsystem does not control
> > > the CS/SSB + * line, or where such control is broken, you can provide a
> > > custom routine to + * handle a GPIO as CS/SSB. This routine will be
> > > called at the beginning and + * end of each SPI transaction. The RMI
> > > SPI implementation will wait + * pre_delay_us after this routine
> > > returns before starting the SPI transfer; + * and post_delay_us after
> > > completion of the SPI transfer(s) before calling it + * with
> > > assert==FALSE.
> >
> > Hm hm, can you describe the case where this happens?
> >
> > Usually we don't avoid fixes for broken drivers by duct-taping
> > solutions into other drivers, instead we fix the SPI driver.
> >
> > I can think of systems where CS is asserted not by using
> > GPIO but by poking some special register for example, which
> > is a valid reason for including this, but working around broken
> > SPI drivers is not a valid reason to include this.
> >
> > (Paging Mark about it.)
>
> Yeah, this seems silly - by this logic we'd have to go round implementing
> manual /CS control in every single SPI client driver which isn't
> terribly sensible. The driver should just assume that the SPI
> controller does what it's told. As you say if there's an issue the
> relevant controller driver should take care of things.
>
> We should also have generic support in the SPI framework for GPIO based
> /CS, there's enough drivers open coding this already either due to
> hardware limitations or to support extra chip selects.
>
> The ability of SPI hardware and driver authors to get /CS right is
> pretty depressing :/
You will get no argument at all from me on that point. I'll even add board layout engineers to the list ("it wasn't convenient to run CS, so we just used a different pin. You can just mux it, right?"). Basically this feature exists to help get prototype systems up and running while the SPI hardware/driver/layout matures.
If this feature is a deal-breaker, we can take it out. In the absence of a generic GPIO implementation for CS, though, I'd much rather leave it in. Once generic GPIO CS arrives, we'll remove it pretty quickly.
On Thursday, October 11, 2012 02:21:53 AM you wrote:
> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> > rmi_bus.c implements the basic functionality of the RMI bus. This file is
> > greatly simplified compared to the previous patch - we've switched from
> > "do it yourself" device/driver binding to using device_type to distinguish
> > between the two kinds of devices on the bus (sensor devices and function
> > specific devices) and using the standard bus implementation to manage
> > devices and drivers.
>
> So I think you really want Greg KH to look at this bus implementation
> now. Please include Greg on future mailings...
>
> It looks much improved from previous versions, and sorry if I am now
> adding even more comments, but it's because you cleared out some
> noise that was disturbing my perception so I can cleanly review
> the architecture of this thing now. (I'm impressed by your work and
> new high-speed turnaround time!)
Thanks for the praise - it means a lot to me and my team.
We'll cc Greg on the next patch drop.
[snip some items covered in a previous email]
>
> (...)
>
> > diff --git a/drivers/input/rmi4/rmi_driver.c
> > b/drivers/input/rmi4/rmi_driver.c
[snip some items covered in a previous email]
>
> > +#define DELAY_NAME "delay"
>
> This is only used in one place, why not just use the string
> "delay" there?
>
> (...)
>
> > + if (IS_ENABLED(CONFIG_RMI4_SPI) && !strncmp("spi", info->proto,
> > 3)) { + data->debugfs_delay =
> > debugfs_create_file(DELAY_NAME,
> > + RMI_RW_ATTR, rmi_dev->debugfs_root,
> > rmi_dev, + &delay_fops);
>
> i.e. there.
That's a left-over. We'll consolidate it.
>
> (...)
>
> > +/* Useful helper functions for u8* */
> > +
> > +static bool u8_is_any_set(u8 *target, int size)
> > +{
> > + int i;
> > + /* We'd like to use find_first_bit, but it ALWAYS returns 1,
> > + * no matter what we pass it. So we have to do this the hard way.
> > + * return find_first_bit((long unsigned int *) target, size) !=
> > 0;
> > + */
> > + for (i = 0; i < size; i++) {
> > + if (target[i])
> > + return true;
> > + }
> > + return false;
> > +}
>
> Instead of:
>
> if (u8_is_any_set(foo, 128) {}
>
> Why can't you use:
>
> if (!bitmap_empty(foo, 128*8) {}
>
> ?
>
> If you look at the implementation in the <linux/bitmap.h> header
> and __bitmap_empty() in lib/bitmap.c you will realize that this
> function is already optimized like this (and I actually don't think
> the RMI4 code is performance-critical for these functions anyway,
> but prove me wrong!)
We'll give !bitmap_empty a try.
>
> > +
> > +/** This is here because all those casts made for some ugly code.
> > + */
> > +static void u8_and(u8 *dest, u8 *target1, u8 *target2, int nbits)
> > +{
> > + bitmap_and((long unsigned int *) dest,
> > + (long unsigned int *) target1,
> > + (long unsigned int *) target2,
> > + nbits);
> > +}
>
> Hm, getting rid of unreadable casts is a valid case.
>
> I'll be OK with this but maybe the real solution is to introduce such
> helpers into <linux/bitmap.h>?
Hmmm. We'll give that some thought. Thought I'd like to get the RMI4 driver nailed down, just to keep the area of change small. Once we've got all the kinks worked out here, we'll look at bitmap.h helpers.
>
> (...)
>
> > +static int process_interrupt_requests(struct rmi_device *rmi_dev)
> > +{
> > + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> > + struct device *dev = &rmi_dev->dev;
> > + struct rmi_function_container *entry;
> > + u8 irq_status[data->num_of_irq_regs];
>
> Looking at this...
>
> What does the data->num_of_irq_regs actually contain?
>
> I just fear that it is something constant like always 2 or always 4,
> so there is actually, in reality, a 16 or 32 bit register hiding in there.
>
> In that case what you should do is to represent it as a u16 or u32 here,
> just or the bits into a status word, and then walk over that status
> word with something like ffs(bitword); ...
Nope, it's not constant. In theory, and RMI4 based sensor can have up to 128 functions (in practice, it's far fewer), and each function can have as many as 7 interrupts. So the number of IRQ registers can vary from RMI4 sensor to RMI4 sensor, and needs to be computed during the scan of the product descriptor table.
>
> (...)
>
> > +static int standard_resume(struct rmi_device *rmi_dev)
>
> Standard eh?
>
> Atleast prefix with rmi4_*...
Ooops - we excised the Android based stuff, but forgot to change that function name.
>
> > +static int rmi_driver_suspend(struct device *dev)
> > +{
> > + struct rmi_device *rmi_dev = to_rmi_device(dev);
> > + return standard_suspend(rmi_dev);
> > +}
> > +
> > +static int rmi_driver_resume(struct device *dev)
> > +{
> > + struct rmi_device *rmi_dev = to_rmi_device(dev);
> > + return standard_resume(rmi_dev);
> > +}
>
> If all these two are doing are to call another function with almost
> the same name, what is the point? Just sink the code from
> "standard_suspend()" and "standard_resume()" into these
> functions and remove a pointless layer of abstraction.
Good point.
>
> Apart from this the core driver looks good to me.
>
> (...)
>
> > diff --git a/drivers/input/rmi4/rmi_driver.h
> > b/drivers/input/rmi4/rmi_driver.h
> (...)
>
> > +#define simple_show_union_struct(regtype, propname, fmt)\
> > +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device
> > *dev,\ + struct device_attribute *attr,
> > char *buf) {\ + struct rmi_function_container *fc;\
> > + struct FUNCTION_DATA *data;\
> > +\
> > + fc = to_rmi_function_container(dev);\
> > + data = fc->data;\
> > +\
> > + return snprintf(buf, PAGE_SIZE, fmt,\
> > + data->regtype.propname);\
> > +}
>
> OK I see the point, but is there really no other way to do this than
> to #define huge static inlines like these? Is it really not possible to
> create just generic functions instead of going this far?
>
> (same comment for all)
We tried generic functions previously, and it wound up really really ugly. We'd be willing to look at it again, though, since this isn't real beautiful either. If you've got an example implementation in mind. a pointer would help a great deal.
>
> > +union pdt_properties {
> > + struct {
> > + u8 reserved_1:6;
> > + u8 has_bsr:1;
> > + u8 reserved_2:1;
> > + } __attribute__((__packed__));
> > + u8 regs[1];
>
> I don't understand what this union is trying to achieve.
>
> regs[1] does not look right considering what you're trying to
> achieve. Since the above fields require a regs[2] (9 bits!)
> to be stored. Maybe write out what you're trying to do here
> so I can understand it? (If everyone else in the world gets
> it immediately, it's maybe me that need fixing instead...)
>
> Apart from these remarks it's looking real nice now!
I only count 8 bits there, unless there's something about packing I'm not aware of. Is there something else you found confusing about the union?
Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]> wrote:
> > The I2C physical driver is not extensively changed in terms of
> > functionality since the previous patch. Management of the attention GPIO
> > has been moved to rmi_driver.c (see previous email), and most of the
> > debug related interfaces have been moved from sysfs to debugfs. Control
> > of the debug features has been moved from compile-time to runtime
> > switches available via debugfs.
> >
> > The core I2C functionality was previously ACKed by Jean Delvare. I don't
> > believe that portion of the code has changed much since then, but we'd
> > appreciate a second glance at this.
>
> The above commit blurb looks more like a changelog than a description
> of the actual patch. Nothing wrong with that but begin by describing
> the patch first.
Good point. I was describing the patch, but not from the correct point of view. :-)
[snip some items covered in a previous email]
>
> > +static int setup_debugfs(struct rmi_device *rmi_dev, struct rmi_i2c_data
> > *data); +static void teardown_debugfs(struct rmi_i2c_data *data);
>
> Why do you need to forward-declare these? Can't you just move them
> up above the functions using them?
Probably. We'll do that if possible.
>
> > +struct i2c_debugfs_data {
> > + bool done;
>
> Done with what? ... needs some doc.
OK.
>
> > + struct rmi_i2c_data *i2c_data;
> > +};
>
> (...)
>
> > +static int __devinit rmi_i2c_probe(struct i2c_client *client,
> > + const struct i2c_device_id *id)
>
> (...)
>
> > + rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
>
> (...)
>
> > + data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);
>
> Can you use devm_kzalloc(&client->dev, ...) for these so you don't
> need to free() them explicitly?
Hmmmmmm. That looks like a merge regression - I'm pretty sure we implemented devm_kzalloc there.
>
> (...)
>
> > +static int __devexit rmi_i2c_remove(struct i2c_client *client)
> > +{
> > + struct rmi_phys_device *phys = i2c_get_clientdata(client);
> > + struct rmi_device_platform_data *pd = client->dev.platform_data;
> > +
> > + /* Can I remove this disable_device */
> > + /*disable_device(phys); */
>
> So just delete these two lines then?
Yes.-
Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]>
> wrote:
>
> (...)
>
> > diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
>
> (...)
>
> > +config RMI4_DEBUG
> > + bool "RMI4 Debugging"
> > + depends on RMI4_BUS
>
> select DEBUG_FS
>
> This has been illustrated many times in the review. You definatley
> have code depending on debugfs when this is selected.
Agreed.-
Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]> wrote:
> > RMI Function 01 implements basic device control and power management
> > behaviors for the RMI4 sensor. Since the last patch, we've decoupled
> > rmi_f01.c implementation from rmi_driver.c, so rmi_f01.c acts as a
> > standard driver module to handle F01 devices on the RMI bus.
> >
> > Like other modules, a number of attributes have been moved from sysfs to
> > debugfs, depending on their expected use.
> >
> >
> > rmi_f01.h exports definitions that we expect to be used by other
> > functionality in the future (such as firmware reflash).
> >
> >
> > Signed-off-by: Christopher Heiny <[email protected]>
> >
> > Cc: Dmitry Torokhov <[email protected]>
> > Cc: Linus Walleij <[email protected]>
> > Cc: Naveen Kumar Gaddipati <[email protected]>
> > Cc: Joeri de Gram <[email protected]>
> >
> > ---
>
> There is liberal whitespacing above. (No big deal, but anyway.)
>
> (...)
>
> > +/**
> > + * @reset - set this bit to force a firmware reset of the sensor.
> > + */
> > +union f01_device_commands {
> > + struct {
> > + bool reset:1;
> > + u8 reserved:7;
> > + } __attribute__((__packed__));
> > + u8 reg;
> > +};
>
> I'm still scared by these unions. I see what you're doing but my
> preferred style of driver writing is to use a simple u8 if you just treat
> it the right way with some |= and &= ...
>
> #include <linux/bitops.h>
>
> #define F01_RESET BIT(0)
>
> u8 my_command = F01_RESET;
>
> send(&my_command);
>
> I will not insist on this because it's a bit about programming style.
> For memory-mapped devices we usually do it my way, but this
> is more like some protocol and I know protocols like to do things
> with structs and stuff so no big deal.
That's a good summary of what we're trying to do. Our original version did more of the traditional mask+shift approach to manipulating the fields in the various registers, but in the case of complicated functions such as F11 this rapidly became unreadable. We found the unions worked a lot better - the code was more readable and less error prone. For consistency we decided to apply them throughout the code.
>
> > +#ifdef CONFIG_RMI4_DEBUG
> > +struct f01_debugfs_data {
> > + bool done;
> > + struct rmi_function_container *fc;
> > +};
> > +
> > +static int f01_debug_open(struct inode *inodep, struct file *filp)
> > +{
> > + struct f01_debugfs_data *data;
> > + struct rmi_function_container *fc = inodep->i_private;
> > +
> > + data = devm_kzalloc(&fc->dev, sizeof(struct f01_debugfs_data),
> > + GFP_KERNEL);
>
> Wait, you probably did this because I requested it, but I was maybe
> wrong?
>
> Will this not re-allocate a chunk every time you look at a debugfs
> file? So it leaks memory?
>
> In that case common kzalloc() and kfree() is the way to go, as it
> is for dynamic buffers. Sorry for screwing things up for you.
No problem - we'll fix it. Or unfix it. Or something like that. :-)
>
> > + for (i = 0; i < f01->irq_count && *local_buf != 0;
> > + i++, local_buf += 2) {
> > + int irq_shift;
> > + int interrupt_enable;
> > + int result;
> > +
> > + irq_reg = i / 8;
> > + irq_shift = i % 8;
>
> Please stop doing these arithmetics-turned-maths things.
>
> irq_reg = i >> 8;
> irq_shift = i & 0xFF;
See note on this in a previous email.
>
> (...)
>
> > +static ssize_t rmi_fn_01_interrupt_enable_show(struct device *dev,
> > + struct device_attribute *attr, char *buf)
> > +{
> > + struct rmi_function_container *fc;
> > + struct f01_data *data;
> > + int i, len, total_len = 0;
> > + char *current_buf = buf;
> > +
> > + fc = to_rmi_function_container(dev);
> > + data = fc->data;
> > + /* loop through each irq value and copy its
> > + * string representation into buf */
> > + for (i = 0; i < data->irq_count; i++) {
> > + int irq_reg;
> > + int irq_shift;
> > + int interrupt_enable;
> > +
> > + irq_reg = i / 8;
> > + irq_shift = i % 8;
>
> Dito.
>
> (...)
>
> > +static int f01_probe(struct device *dev);
>
> Do you really need to forward-declare this?
It's a leftover from the process of eliminating roll-your-own bus implementation, and move the other code around as well. (same applies for similar code in rmi_f11.c).
>
> (...)
>
> > +static struct rmi_function_handler function_handler = {
> > + .driver = {
> > + .owner = THIS_MODULE,
> > + .name = "rmi_f01",
> > + .bus = &rmi_bus_type,
> > + .probe = f01_probe,
> > + .remove = f01_remove_device,
> > + },
> > + .func = 0x01,
> > + .config = rmi_f01_config,
> > + .attention = rmi_f01_attention,
> > +
> > +#ifdef CONFIG_PM
> > + .suspend = rmi_f01_suspend,
> > + .resume = rmi_f01_resume,
> > +#endif /* CONFIG_PM */
> > +};
>
> Just move this block of struct below the probe function...
>
> > +static __devinit int f01_probe(struct device *dev)
>
> Header file looks OK (if these unions is the way to go...)
>
> Yours,
> Linus Walleij-
Linus Walleij wrote:
> On Sat, Oct 6, 2012 at 6:10 AM, Christopher Heiny <[email protected]>
> wrote:
>
> So looking closer at this one since we will use it. Maybe it's in such a
> good shape now that I should be able to actually test it with the hardware?
Well, it's been possible to test at least since the patch submitted last December. The code might have been ugly, but it was working.
>
> (...)
>
> > diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
[snip previously mentioned stuff]
>
> > +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
> > +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
> > +
> > +#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
>
> Use existing kernel macros in <linux/kernel.h>
>
> In this case:
> #define F11_CEIL(x, y) DIV_ROUND_UP(x, y)
>
> Or just use DIV_ROUND_UP() directly in the code, your choice.
Use it directly is simpler.
>
> > +#define MAX_NAME_LENGTH 256
>
> Really? Are you sure there is not a null terminator or length byte
> included so it's actually 255?
We were assuming 255 + terminator. Perhaps a better name such as NAME_BUFFER_SIZE would be clearer?
>
> (...)
>
> > +static int sensor_debug_open(struct inode *inodep, struct file *filp)
> > +{
> > + struct sensor_debugfs_data *data;
> > + struct f11_2d_sensor *sensor = inodep->i_private;
> > + struct rmi_function_container *fc = sensor->fc;
> > +
> > + data = devm_kzalloc(&fc->dev, sizeof(struct sensor_debugfs_data),
> > + GFP_KERNEL);
>
> Again I may have lead you astray. Check if this leaks memory, in that
> case use kzalloc()/kfree(). Sorry
No problem - will correct it.
>
> (...)
>
> > +static int f11_debug_open(struct inode *inodep, struct file *filp)
> > +{
> > + struct f11_debugfs_data *data;
> > + struct rmi_function_container *fc = inodep->i_private;
> > +
> > + data = devm_kzalloc(&fc->dev, sizeof(struct f11_debugfs_data),
> > + GFP_KERNEL);
>
> Dito.
>
> (...)
>
> > +static void rmi_f11_abs_pos_report(struct f11_data *f11,
> > + struct f11_2d_sensor *sensor,
> > + u8 finger_state, u8 n_finger)
>
> (...)
>
> > + if (axis_align->flip_y)
> > + y = max(sensor->max_y - y, 0);
> > +
> > + /*
> > + ** here checking if X offset or y offset are specified is
> > + ** redundant. We just add the offsets or, clip the
> > values
> > + **
> > + ** note: offsets need to be done before clipping occurs,
> > + ** or we could get funny values that are outside
> > + ** clipping boundaries.
> > + */
>
> This is a weird commenting style, what's wrong with a single star?
> (No big deal but it stands out...)
It's probably just someone's editor settings. We can tidy it up.
>
> (...)
>
> > +static int f11_allocate_control_regs(struct rmi_device *rmi_dev,
> > + struct f11_2d_device_query *device_query,
> > + struct f11_2d_sensor_query *sensor_query,
> > + struct f11_2d_ctrl *ctrl,
> > + u16 ctrl_base_addr) {
> > +
> > + struct rmi_driver_data *driver_data =
> > dev_get_drvdata(&rmi_dev->dev); + struct rmi_function_container *fc
> > = driver_data->f01_container; +
> > + ctrl->ctrl0_9 = devm_kzalloc(&fc->dev, sizeof(union
> > f11_2d_ctrl0_9), + GFP_KERNEL);
>
> If this is called from .probe() only, this is correct.
>
> So the rule is: use devm_* for anything that is allocated at .probe()
> and released on .remove(). Any other dynamic buffers etc need to
> use common kzalloc()/kfree().
OK - we'll review to make sure that rule is followed, and change as required.
[snip a bunch of the same]
>
> > +
> > + return f11_read_control_regs(rmi_dev, ctrl, ctrl_base_addr);
>
> Hey why are you ending with a call to that function?
> The function name gets misleading.
>
> Instead call both functions in succession at the call site on
> .probe().
OK.
>
> (...)
>
> > +static int f11_device_init(struct rmi_function_container *fc)
> > +{
> > + int rc;
> > +
> > + rc = rmi_f11_initialize(fc);
> > + if (rc < 0)
> > + goto err_free_data;
> > +
> > + rc = rmi_f11_register_devices(fc);
> > + if (rc < 0)
> > + goto err_free_data;
> > +
> > + rc = rmi_f11_create_sysfs(fc);
> > + if (rc < 0)
> > + goto err_free_data;
> > +
> > + return 0;
> > +
> > +err_free_data:
> > + rmi_f11_free_memory(fc);
> > +
> > + return rc;
> > +}
> > +
> > +static void rmi_f11_free_memory(struct rmi_function_container *fc)
> > +{
> > + struct f11_data *f11 = fc->data;
> > + int i;
> > +
> > + if (f11) {
> > + for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++)
> > + kfree(f11->sensors[i].button_map);
> > + }
>
> This is wrong. The button_map was allocated with devm_kzalloc()
> so it will get automatically freed. Just skip this step.
We'll correct that.
>
> (...)
>
> > +static int rmi_f11_initialize(struct rmi_function_container *fc)
> > +{
>
> (...)
>
> > + rc = f11_allocate_control_regs(rmi_dev,
> > + &f11->dev_query, &sensor->sens_query,
> > + &f11->dev_controls, control_base_addr);
> > + if (rc < 0) {
> > + dev_err(&fc->dev,
> > + "Failed to initialize F11 control
> > params.\n");
> "failed to allocate F11 control params"
>
> > + return rc;
> > + }
>
> So after this call the read regs explicitly instead as described above.
OK
>
> (...)
>
> > +static void register_virtual_buttons(struct rmi_function_container *fc,
> > + struct f11_2d_sensor *sensor) {
> > + int j;
> > +
> > + if (!sensor->sens_query.has_gestures)
> > + return;
> > + if (!sensor->virtual_buttons.buttons) {
> > + dev_warn(&fc->dev, "No virtual button platform data for 2D
> > sensor %d.\n", + sensor->sensor_index);
> > + return;
> > + }
> > + /* call devm_kcalloc when it will be defined in kernel */
> > + sensor->button_map = devm_kzalloc(&fc->dev,
> > + sensor->virtual_buttons.buttons,
> > + GFP_KERNEL);
> > + if (!sensor->button_map) {
> > + dev_err(&fc->dev, "Failed to allocate the virtual button
> > map.\n"); + return;
> > + }
>
> So as noted above, since it's using devm_kzalloc(), don't free() it.
OK.
>
> (...)
>
> > + sensor->mouse_input = input_dev_mouse;
> > + input_dev_mouse->name = "rmi_mouse";
> > + input_dev_mouse->phys = "rmi_f11/input0";
> > +
> > + input_dev_mouse->id.vendor = 0x18d1;
> > + input_dev_mouse->id.product = 0x0210;
> > + input_dev_mouse->id.version = 0x0100;
>
> As noted in previous review, 0x18d1 is Google's vendor ID. Please use
> a Synaptics Vendor ID, Product ID and version!
>
> Hint: synaptics Vendor ID is 0x06cb.
That's kind of embarrassing. We'll fix it.-
On Thu, Oct 11, 2012 at 04:15:56AM +0000, Christopher Heiny wrote:
> On Thursday, October 11, 2012 02:21:53 AM you wrote:
> > On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
>
> >
> > > +
> > > +/** This is here because all those casts made for some ugly code.
> > > + */
> > > +static void u8_and(u8 *dest, u8 *target1, u8 *target2, int nbits)
> > > +{
> > > + bitmap_and((long unsigned int *) dest,
> > > + (long unsigned int *) target1,
> > > + (long unsigned int *) target2,
> > > + nbits);
> > > +}
> >
> > Hm, getting rid of unreadable casts is a valid case.
> >
> > I'll be OK with this but maybe the real solution is to introduce such
> > helpers into <linux/bitmap.h>?
>
> Hmmm. We'll give that some thought. Thought I'd like to get the RMI4
> driver nailed down, just to keep the area of change small. Once we've
> got all the kinks worked out here, we'll look at bitmap.h helpers.
The question is why you are using u8 for bitmaps instead of doing
DECALRE_BITMAP() and using it instead? Then you would not need silly
wrappers around existing APIs.
>
> >
> > (...)
> >
> > > +static int process_interrupt_requests(struct rmi_device *rmi_dev)
> > > +{
> > > + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> > > + struct device *dev = &rmi_dev->dev;
> > > + struct rmi_function_container *entry;
> > > + u8 irq_status[data->num_of_irq_regs];
> >
> > Looking at this...
> >
> > What does the data->num_of_irq_regs actually contain?
> >
> > I just fear that it is something constant like always 2 or always 4,
> > so there is actually, in reality, a 16 or 32 bit register hiding in there.
> >
> > In that case what you should do is to represent it as a u16 or u32 here,
> > just or the bits into a status word, and then walk over that status
> > word with something like ffs(bitword); ...
>
> Nope, it's not constant. In theory, and RMI4 based sensor can have up
> to 128 functions (in practice, it's far fewer), and each function can
> have as many as 7 interrupts. So the number of IRQ registers can vary
> from RMI4 sensor to RMI4 sensor, and needs to be computed during the
> scan of the product descriptor table.
Is it a good idea to have it on stack then? Should it be part of
rmi_device instead?
> >
> > > +#define simple_show_union_struct(regtype, propname, fmt)\
> > > +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device
> > > *dev,\ + struct device_attribute *attr,
> > > char *buf) {\ + struct rmi_function_container *fc;\
> > > + struct FUNCTION_DATA *data;\
> > > +\
> > > + fc = to_rmi_function_container(dev);\
> > > + data = fc->data;\
> > > +\
> > > + return snprintf(buf, PAGE_SIZE, fmt,\
> > > + data->regtype.propname);\
> > > +}
> >
> > OK I see the point, but is there really no other way to do this than
> > to #define huge static inlines like these? Is it really not possible to
> > create just generic functions instead of going this far?
> >
> > (same comment for all)
>
> We tried generic functions previously, and it wound up really really ugly. We'd be willing to look at it again, though, since this isn't real beautiful either. If you've got an example implementation in mind. a pointer would help a great deal.
You just need to wrap around a custome structure around struct
device_attribute and then you shoudl be able to use generics. If you
look into trackpoint.c you should gett the idea.
Thanks.
--
Dmitry
On Fri, Oct 05, 2012 at 09:09:58PM -0700, Christopher Heiny wrote:
> +
> + int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
> + int len);
> + int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
> + int len);
> +
If you declare your buffer as [const] void * instead of u8 * I think you
will be able to get rid of most of your unions.
> +
> +/**
> + * Helper fn to convert a byte array representing a 16 bit value in the RMI
> + * endian-ness to a 16-bit value in the native processor's specific endianness.
> + * We don't use ntohs/htons here because, well, we're not dealing with
> + * a pair of 16 bit values. Casting dest to u16* wouldn't work, because
> + * that would imply knowing the byte order of u16 in the first place. The
> + * same applies for using shifts and masks.
> + */
> +static inline u16 batohs(u8 *src)
> +{
> + return src[1] * 0x100 + src[0];
> +}
> +
> +/**
> + * Helper function to convert a 16 bit value (in host processor endianess) to
> + * a byte array in the RMI endianess for u16s. See above comment for
> + * why we dont us htons or something like that.
> + */
> +static inline void hstoba(u8 *dest, u16 src)
> +{
> + dest[0] = src % 0x100;
> + dest[1] = src / 0x100;
> +}
These are not used anymore, right?
--
Dmitry
On Thu, Oct 11, 2012 at 03:41:41AM +0000, Christopher Heiny wrote:
> Linus Walleij wrote:
> > On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
> >
> > > +#ifdef CONFIG_RMI4_DEBUG
> > > +/**
> > > + * Utility routine to handle writes to read-only attributes. Hopefully
> > > + * this will never happen, but if the user does something stupid, we
> > > don't
> > > + * want to accept it quietly (which is what can happen if you just put
> > > NULL + * for the attribute's store function).
> > > + */
> > > +static inline ssize_t rmi_store_error(struct device *dev,
> > > + struct device_attribute *attr,
> > > + const char *buf, size_t count)
> > > +{
> > > + dev_warn(dev,
> > > + "WARNING: Attempt to write %d characters to read-only
> > > attribute %s.", + count, attr->attr.name);
> > > + return -EPERM;
> > > +}
> >
> > Here it looks like you're hiding a lot of stuff that should be dev_warn()?
> > Consider my earlier point about dynamic debug.
>
> In previous patch submissions, we always used these warning functions.
> But in the feedback on those patches, we were asked to just make sysfs
> show/store NULL if the attribute is write/read only. However, during
> their development process, our customers want to see the warnings if
> the attributes are accessed incorrectly. So we made these warnings a
> debug option.
I think it is the case when customer is not always right. Given that
the attributes are created with S_IRUGO mask how will we even get these
methods to fire?
--
Dmitry
On Thu, Oct 11, 2012 at 5:41 AM, Christopher Heiny <[email protected]> wrote:
> Linus Walleij wrote:
>> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
>> > + chargerinput ... (rw) User space programs can use this to tell
>> > the + sensor that the system is plugged into an external
>> > power + source (as opposed to running on
>> > batteries). This allows + the sensor firmware to make
>> > necessary adjustments for the + current capacitence
>> > regime. Write 1 to this when the + system is using
>> > external power, write 0 to this when the + system is
>> > running on batteries. See spec for full details.
>> I remember discussing in-kernel notifiers for this. I don't
>> really see the point in tunnelling a notification from the drivers/power
>> subsystem to the drivers/input subsystem through userspace for
>> no good.
>>
>> It's no blocker though, I don't expect you to fix this as part of
>> this driver submission.
>>
>> Maybe Anton can comment?
>
> Hmmm. I agree that it'd be good to avoid looping through userspace. But....
>
> I found ways to notfiy the kernel that the charger is plugged/unplugged,
> but that's only useful if you're a battery charger device driver. I also
> found ways for userspace to get notification of charger events. I
> didn't spot any way for in-kernel drivers to get notification of such
> events. Perhaps I'm not looking the right places, though - can you
> provide a pointer?
So my point was that there is indeed no such in-kernel
notification mechanism.
Maybe something engineered similar to this:
http://www.spinics.net/lists/lm-sensors/msg33834.html
Yours,
Linus Walleij
On Thu, Oct 11, 2012 at 5:41 AM, Christopher Heiny <[email protected]> wrote:
> Linus Walleij wrote:
>> But please use arithmetic operators (I think I said this on the last
>> review):
>>
>> dest[0] = src & 0xFF;
>> dest[1] = src >> 8;
>>
>> Doing it the above way makes artithmetic look like maths, and it isn't.
>> Besides it's done this way in most parts of the kernel and we're
>> familiar with it.
>
> Yes, you mentioned it previously. I'm somewhat paranoid, though, and
> don't trust the shift/mask method to work correctly on big-endian
> machines. If the shifts can be relied on to behave (I'm guessing the
> answer is "yes", since you say this idiom is used widely in the
> kernel), then I'll change it.
If the behaviour was not consistent across different endianness
it would not be part of the C language specification...
<< means shift left in the accumulator or whatever you have.
It will work the same no matter how bits are laid out in
memory.
>> > +static inline ssize_t rmi_store_error(struct device *dev,
>> > + struct device_attribute *attr,
>> > + const char *buf, size_t count)
>> > +{
>> > + dev_warn(dev,
>> > + "WARNING: Attempt to write %d characters to read-only
>> > attribute %s.", + count, attr->attr.name);
>> > + return -EPERM;
>> > +}
>>
>> Here it looks like you're hiding a lot of stuff that should be dev_warn()?
>> Consider my earlier point about dynamic debug.
>
> In previous patch submissions, we always used these warning functions.
> But in the feedback on those patches, we were asked to just make
> sysfs show/store NULL if the attribute is write/read only. However,
> during their development process, our customers want to see the
> warnings if the attributes are accessed incorrectly. So we made
> these warnings a debug option.
See Dmitry's comment ...
Basically my stance is that you should not lower yourself to the
level of others not getting the point of your technical solution
by making unelegant compromises, what
you should do is to bring them up to your level so they
understand that your solution is elegant.
Maybe a bit utopist I know...
Yours,
Linus Walleij
On Thu, Oct 11, 2012 at 6:15 AM, Christopher Heiny <[email protected]> wrote:
> On Thursday, October 11, 2012 02:21:53 AM I wrote:
>> > +union pdt_properties {
>> > + struct {
>> > + u8 reserved_1:6;
>> > + u8 has_bsr:1;
>> > + u8 reserved_2:1;
>> > + } __attribute__((__packed__));
>> > + u8 regs[1];
>>
>> I don't understand what this union is trying to achieve.
>>
>> regs[1] does not look right considering what you're trying to
>> achieve. Since the above fields require a regs[2] (9 bits!)
>> to be stored. Maybe write out what you're trying to do here
>> so I can understand it? (If everyone else in the world gets
>> it immediately, it's maybe me that need fixing instead...)
>>
>> Apart from these remarks it's looking real nice now!
>
> I only count 8 bits there, unless there's something about
> packing I'm not aware of. Is there something else you
> found confusing about the union?
I just did bad maths, too many figured in the struct...
But consider Dmitry's suggestion that you might get rid
of this unionizing.
Yours,
Linus Walleij
On Thu, Oct 11, 2012 at 03:56:22AM +0000, Christopher Heiny wrote:
Fix your mailer to word wrap within paragraphs.
> If this feature is a deal-breaker, we can take it out. In the absence
> of a generic GPIO implementation for CS, though, I'd much rather leave
> it in. Once generic GPIO CS arrives, we'll remove it pretty quickly.
Why not just implement this at an appropriate level in the SPI
subsystem? One of the great things about Linux is that you can change
the core code...
On Thu, Oct 11, 2012 at 05:32:59PM +0200, Linus Walleij wrote:
> On Thu, Oct 11, 2012 at 5:41 AM, Christopher Heiny <[email protected]> wrote:
> > In previous patch submissions, we always used these warning functions.
> > But in the feedback on those patches, we were asked to just make
> > sysfs show/store NULL if the attribute is write/read only. However,
> > during their development process, our customers want to see the
> > warnings if the attributes are accessed incorrectly. So we made
> > these warnings a debug option.
> Basically my stance is that you should not lower yourself to the
> level of others not getting the point of your technical solution
> by making unelegant compromises, what
> you should do is to bring them up to your level so they
> understand that your solution is elegant.
It seems like what you really want to do is add a debug feature to sysfs
which will optionally complain loudly at bad accesses; obviously it's
not something that should be there all the time as running then handling
an error is a perfectly legitimate thing to do. As with the /CS
handling it'd mean it was handled at an appropriate level and could be
reused elsewhere (it might also help make it clear to your customers why
this is generally bad form).
On 10/10/2012 08:06 PM, Joe Perches wrote:
> On Thu, 2012-10-11 at 02:49 +0000, Christopher Heiny wrote:
>> Joe Perches wrote:
> []
>>>> + list_for_each_entry(entry, &data->rmi_functions.list, list)
>>>> + if (entry->irq_mask)
>>>> + process_one_interrupt(entry, irq_status,
>>>> + data);
>>>
>>> style nit, it'd be nicer with braces.
>>
>> I agree with you, but checkpatch.pl doesn't. :-(
>
> Sure it does.
>
> $ cat t.c
> {
> list_for_each_entry(entry, &data->rmi_functions.list, list) {
> if (entry->irq_mask)
> process_one_interrupt(entry, irq_status, data);
> }
> }
> $ ./scripts/checkpatch.pl --strict -f t.c
> total: 0 errors, 0 warnings, 0 checks, 7 lines checked
>
> t.c has no obvious style problems and is ready for submission.
I stand corrected. Thanks!
On 10/11/2012 10:16 PM, Mark Brown wrote:
> On Thu, Oct 11, 2012 at 03:56:22AM +0000, Christopher Heiny wrote:
>
> Fix your mailer to word wrap within paragraphs.
Sorry - I was on the road and had to use a web interface. It looked OK
during composition. Is this better?
>> >If this feature is a deal-breaker, we can take it out. In the absence
>> >of a generic GPIO implementation for CS, though, I'd much rather leave
>> >it in. Once generic GPIO CS arrives, we'll remove it pretty quickly.
>
> Why not just implement this at an appropriate level in the SPI
> subsystem? One of the great things about Linux is that you can change
> the core code...
At the moment, we're trying to keep our work focused on just the core
RMI4 driver. It's evident that we've still got a bit to learn, and I'd
be more comfortable keeping our learning experiences confined as they
are now.
However, a general capability in SPI core is a fine idea for future
work, once we get the RMI4 driver ironed out. Can we agree to defer
that for now?
Thanks,
Chris
On 10/11/2012 01:20 AM, Dmitry Torokhov wrote:
> On Fri, Oct 05, 2012 at 09:09:58PM -0700, Christopher Heiny wrote:
>> +
>> + int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
>> + int len);
>> + int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
>> + int len);
>> +
>
> If you declare your buffer as [const] void * instead of u8 * I think you
> will be able to get rid of most of your unions.
Good point. We'll explore that. If you see it in the next patch,
you'll know it worked out.
>> + * Helper fn to convert a byte array representing a 16 bit value in the RMI
>> + * endian-ness to a 16-bit value in the native processor's specific endianness.
>> + * We don't use ntohs/htons here because, well, we're not dealing with
>> + * a pair of 16 bit values. Casting dest to u16* wouldn't work, because
>> + * that would imply knowing the byte order of u16 in the first place. The
>> + * same applies for using shifts and masks.
>> + */
>> +static inline u16 batohs(u8 *src)
>> +{
>> + return src[1] * 0x100 + src[0];
>> +}
>> +
>> +/**
>> + * Helper function to convert a 16 bit value (in host processor endianess) to
>> + * a byte array in the RMI endianess for u16s. See above comment for
>> + * why we dont us htons or something like that.
>> + */
>> +static inline void hstoba(u8 *dest, u16 src)
>> +{
>> + dest[0] = src % 0x100;
>> + dest[1] = src / 0x100;
>> +}
>
> These are not used anymore, right?
There are function drivers that we chose not to include that depend on
these. We'll drop these from rmi.h until we're ready to submit those
other function drivers.
On Tuesday, October 23, 2012 03:39:00 PM Christopher Heiny wrote:
> On 10/11/2012 01:20 AM, Dmitry Torokhov wrote:
> > On Fri, Oct 05, 2012 at 09:09:58PM -0700, Christopher Heiny wrote:
> >> +
> >> + int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
> >> + int len);
> >> + int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
> >> + int len);
> >> +
> >
> > If you declare your buffer as [const] void * instead of u8 * I think you
> > will be able to get rid of most of your unions.
>
> Good point. We'll explore that. If you see it in the next patch,
> you'll know it worked out.
>
> >> + * Helper fn to convert a byte array representing a 16 bit value in the
> >> RMI + * endian-ness to a 16-bit value in the native processor's specific
> >> endianness. + * We don't use ntohs/htons here because, well, we're not
> >> dealing with + * a pair of 16 bit values. Casting dest to u16* wouldn't
> >> work, because + * that would imply knowing the byte order of u16 in the
> >> first place. The + * same applies for using shifts and masks.
> >> + */
> >> +static inline u16 batohs(u8 *src)
> >> +{
> >> + return src[1] * 0x100 + src[0];
> >> +}
> >> +
> >> +/**
> >> + * Helper function to convert a 16 bit value (in host processor
> >> endianess) to + * a byte array in the RMI endianess for u16s. See above
> >> comment for + * why we dont us htons or something like that.
> >> + */
> >> +static inline void hstoba(u8 *dest, u16 src)
> >> +{
> >> + dest[0] = src % 0x100;
> >> + dest[1] = src / 0x100;
> >> +}
> >
> > These are not used anymore, right?
>
> There are function drivers that we chose not to include that depend on
> these. We'll drop these from rmi.h until we're ready to submit those
> other function drivers.
OK, in this case, like last time we discussed, your function drivers
should use cpu_to_le16() and le16_to_cpu() instead.
Thanks.
--
Dmitry
On 10/11/2012 01:24 AM, Dmitry Torokhov wrote:
> On Thu, Oct 11, 2012 at 03:41:41AM +0000, Christopher Heiny wrote:
>> >Linus Walleij wrote:
>>> > >On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny<[email protected]> wrote:
>>> > >
>>>> > > >+#ifdef CONFIG_RMI4_DEBUG
>>>> > > >+/**
>>>> > > >+ * Utility routine to handle writes to read-only attributes. Hopefully
>>>> > > >+ * this will never happen, but if the user does something stupid, we
>>>> > > >don't
>>>> > > >+ * want to accept it quietly (which is what can happen if you just put
>>>> > > >NULL + * for the attribute's store function).
>>>> > > >+ */
>>>> > > >+static inline ssize_t rmi_store_error(struct device *dev,
>>>> > > >+ struct device_attribute *attr,
>>>> > > >+ const char *buf, size_t count)
>>>> > > >+{
>>>> > > >+ dev_warn(dev,
>>>> > > >+ "WARNING: Attempt to write %d characters to read-only
>>>> > > >attribute %s.", + count, attr->attr.name);
>>>> > > >+ return -EPERM;
>>>> > > >+}
>>> > >
>>> > >Here it looks like you're hiding a lot of stuff that should be dev_warn()?
>>> > >Consider my earlier point about dynamic debug.
>> >
>> >In previous patch submissions, we always used these warning functions.
>> >But in the feedback on those patches, we were asked to just make sysfs
>> >show/store NULL if the attribute is write/read only. However, during
>> >their development process, our customers want to see the warnings if
>> >the attributes are accessed incorrectly. So we made these warnings a
>> >debug option.
>
> I think it is the case when customer is not always right. Given that
> the attributes are created with S_IRUGO mask how will we even get these
> methods to fire?
We were able to get those to fire in earlier kernels under some UIs
(such as Android). However, we no longer support those earlier version.
I have checked the behavior on up-to-date kernels and UI versions, and
everyone seems to handle this correctly. That means we can drop these
definitions entirely, so we'll do that.
On 10/11/2012 08:32 AM, Linus Walleij wrote:
> On Thu, Oct 11, 2012 at 5:41 AM, Christopher Heiny <[email protected]> wrote:
>> Linus Walleij wrote:
>
>>> But please use arithmetic operators (I think I said this on the last
>>> review):
>>>
>>> dest[0] = src & 0xFF;
>>> dest[1] = src >> 8;
>>>
>>> Doing it the above way makes artithmetic look like maths, and it isn't.
>>> Besides it's done this way in most parts of the kernel and we're
>>> familiar with it.
>>
>> Yes, you mentioned it previously. I'm somewhat paranoid, though, and
>> don't trust the shift/mask method to work correctly on big-endian
>> machines. If the shifts can be relied on to behave (I'm guessing the
>> answer is "yes", since you say this idiom is used widely in the
>> kernel), then I'll change it.
>
> If the behaviour was not consistent across different endianness
> it would not be part of the C language specification...
>
> << means shift left in the accumulator or whatever you have.
> It will work the same no matter how bits are laid out in
> memory.
OK, after reviewing the spec I'll accept that. We'll make the change.
>>>> +static inline ssize_t rmi_store_error(struct device *dev,
>>>> + struct device_attribute *attr,
>>>> + const char *buf, size_t count)
>>>> +{
>>>> + dev_warn(dev,
>>>> + "WARNING: Attempt to write %d characters to read-only
>>>> attribute %s.", + count, attr->attr.name);
>>>> + return -EPERM;
>>>> +}
>>>
>>> Here it looks like you're hiding a lot of stuff that should be dev_warn()?
>>> Consider my earlier point about dynamic debug.
>>
>> In previous patch submissions, we always used these warning functions.
>> But in the feedback on those patches, we were asked to just make
>> sysfs show/store NULL if the attribute is write/read only. However,
>> during their development process, our customers want to see the
>> warnings if the attributes are accessed incorrectly. So we made
>> these warnings a debug option.
>
> See Dmitry's comment ...
>
> Basically my stance is that you should not lower yourself to the
> level of others not getting the point of your technical solution
> by making unelegant compromises, what
> you should do is to bring them up to your level so they
> understand that your solution is elegant.
>
> Maybe a bit utopist I know...
What's the old saying? "I want to live in Theory. Everything is always
so nice there..." :-)
Anyway, see my reply to Dmitry a bit ago. These are no longer needed,
so we'll drop them.
On 10/15/2012 11:26 PM, Mark Brown wrote:
> On Thu, Oct 11, 2012 at 05:32:59PM +0200, Linus Walleij wrote:
>> On Thu, Oct 11, 2012 at 5:41 AM, Christopher Heiny <[email protected]> wrote:
>
>>> In previous patch submissions, we always used these warning functions.
>>> But in the feedback on those patches, we were asked to just make
>>> sysfs show/store NULL if the attribute is write/read only. However,
>>> during their development process, our customers want to see the
>>> warnings if the attributes are accessed incorrectly. So we made
>>> these warnings a debug option.
>
>> Basically my stance is that you should not lower yourself to the
>> level of others not getting the point of your technical solution
>> by making unelegant compromises, what
>> you should do is to bring them up to your level so they
>> understand that your solution is elegant.
>
> It seems like what you really want to do is add a debug feature to sysfs
> which will optionally complain loudly at bad accesses; obviously it's
> not something that should be there all the time as running then handling
> an error is a perfectly legitimate thing to do. As with the /CS
> handling it'd mean it was handled at an appropriate level and could be
> reused elsewhere (it might also help make it clear to your customers why
> this is generally bad form).
See my reply to Dmitry of a bit ago. These are no longer needed, and
we'll be dropping them.
On 10/11/2012 01:13 AM, Dmitry Torokhov wrote:
> On Thu, Oct 11, 2012 at 04:15:56AM +0000, Christopher Heiny wrote:
>> On Thursday, October 11, 2012 02:21:53 AM you wrote:
>>> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]> wrote:
>>
>>>
>>>> +
>>>> +/** This is here because all those casts made for some ugly code.
>>>> + */
>>>> +static void u8_and(u8 *dest, u8 *target1, u8 *target2, int nbits)
>>>> +{
>>>> + bitmap_and((long unsigned int *) dest,
>>>> + (long unsigned int *) target1,
>>>> + (long unsigned int *) target2,
>>>> + nbits);
>>>> +}
>>>
>>> Hm, getting rid of unreadable casts is a valid case.
>>>
>>> I'll be OK with this but maybe the real solution is to introduce such
>>> helpers into <linux/bitmap.h>?
>>
>> Hmmm. We'll give that some thought. Thought I'd like to get the RMI4
>> driver nailed down, just to keep the area of change small. Once we've
>> got all the kinks worked out here, we'll look at bitmap.h helpers.
>
> The question is why you are using u8 for bitmaps instead of doing
> DECALRE_BITMAP() and using it instead? Then you would not need silly
> wrappers around existing APIs.
OK, we'll look into that. My big concern is whether the bit-order in
bitmask.h will be the same as the bit order in the RMI4 sensor
registers. If that works out OK, we'll switch.
>>> (...)
>>>
>>>> +static int process_interrupt_requests(struct rmi_device *rmi_dev)
>>>> +{
>>>> + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
>>>> + struct device *dev = &rmi_dev->dev;
>>>> + struct rmi_function_container *entry;
>>>> + u8 irq_status[data->num_of_irq_regs];
>>>
>>> Looking at this...
>>>
>>> What does the data->num_of_irq_regs actually contain?
>>>
>>> I just fear that it is something constant like always 2 or always 4,
>>> so there is actually, in reality, a 16 or 32 bit register hiding in there.
>>>
>>> In that case what you should do is to represent it as a u16 or u32 here,
>>> just or the bits into a status word, and then walk over that status
>>> word with something like ffs(bitword); ...
>>
>> Nope, it's not constant. In theory, and RMI4 based sensor can have up
>> to 128 functions (in practice, it's far fewer), and each function can
>> have as many as 7 interrupts. So the number of IRQ registers can vary
>> from RMI4 sensor to RMI4 sensor, and needs to be computed during the
>> scan of the product descriptor table.
>
> Is it a good idea to have it on stack then? Should it be part of
> rmi_device instead?
It's not coming off the stack. We're allocating it via devm_kzalloc()
in rmi_driver_probe().
>>>> +#define simple_show_union_struct(regtype, propname, fmt)\
>>>> +static ssize_t tricat(rmi_fn_, FNUM, _##propname##_show)(struct device
>>>> *dev,\ + struct device_attribute *attr,
>>>> char *buf) {\ + struct rmi_function_container *fc;\
>>>> + struct FUNCTION_DATA *data;\
>>>> +\
>>>> + fc = to_rmi_function_container(dev);\
>>>> + data = fc->data;\
>>>> +\
>>>> + return snprintf(buf, PAGE_SIZE, fmt,\
>>>> + data->regtype.propname);\
>>>> +}
>>>
>>> OK I see the point, but is there really no other way to do this than
>>> to #define huge static inlines like these? Is it really not possible to
>>> create just generic functions instead of going this far?
>>>
>>> (same comment for all)
>>
>
>> We tried generic functions previously, and it wound up really really ugly. We'd be willing to look at it again, though, since this isn't real beautiful either. If you've got an example implementation in mind. a pointer would help a great deal.
>
> You just need to wrap around a custome structure around struct
> device_attribute and then you shoudl be able to use generics. If you
> look into trackpoint.c you should gett the idea.
That looks a lot tidier. We'll work on it for sysfs (and maybe for
debugfs, too). It might not be applicable in all cases, but it promises
to make a lot of things tidier.
Thanks!
On Tuesday, October 23, 2012 04:46:28 PM Christopher Heiny wrote:
> On 10/11/2012 01:13 AM, Dmitry Torokhov wrote:
> > On Thu, Oct 11, 2012 at 04:15:56AM +0000, Christopher Heiny wrote:
> >> On Thursday, October 11, 2012 02:21:53 AM you wrote:
> >>> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]>
wrote:
> >>>> +
> >>>> +/** This is here because all those casts made for some ugly code.
> >>>> + */
> >>>> +static void u8_and(u8 *dest, u8 *target1, u8 *target2, int nbits)
> >>>> +{
> >>>> + bitmap_and((long unsigned int *) dest,
> >>>> + (long unsigned int *) target1,
> >>>> + (long unsigned int *) target2,
> >>>> + nbits);
> >>>> +}
> >>>
> >>> Hm, getting rid of unreadable casts is a valid case.
> >>>
> >>> I'll be OK with this but maybe the real solution is to introduce such
> >>> helpers into <linux/bitmap.h>?
> >>
> >> Hmmm. We'll give that some thought. Thought I'd like to get the RMI4
> >> driver nailed down, just to keep the area of change small. Once we've
> >> got all the kinks worked out here, we'll look at bitmap.h helpers.
> >
> > The question is why you are using u8 for bitmaps instead of doing
> > DECALRE_BITMAP() and using it instead? Then you would not need silly
> > wrappers around existing APIs.
>
> OK, we'll look into that. My big concern is whether the bit-order in
> bitmask.h will be the same as the bit order in the RMI4 sensor
> registers. If that works out OK, we'll switch.
I think if you properly convert data to/from cpu-endianness it will clear
matters a lot.
>
> >>> (...)
> >>>
> >>>> +static int process_interrupt_requests(struct rmi_device *rmi_dev)
> >>>> +{
> >>>> + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
> >>>> + struct device *dev = &rmi_dev->dev;
> >>>> + struct rmi_function_container *entry;
> >>>> + u8 irq_status[data->num_of_irq_regs];
> >>>
> >>> Looking at this...
> >>>
> >>> What does the data->num_of_irq_regs actually contain?
> >>>
> >>> I just fear that it is something constant like always 2 or always 4,
> >>> so there is actually, in reality, a 16 or 32 bit register hiding in
> >>> there.
> >>>
> >>> In that case what you should do is to represent it as a u16 or u32 here,
> >>> just or the bits into a status word, and then walk over that status
> >>> word with something like ffs(bitword); ...
> >>
> >> Nope, it's not constant. In theory, and RMI4 based sensor can have up
> >> to 128 functions (in practice, it's far fewer), and each function can
> >> have as many as 7 interrupts. So the number of IRQ registers can vary
> >> from RMI4 sensor to RMI4 sensor, and needs to be computed during the
> >> scan of the product descriptor table.
> >
> > Is it a good idea to have it on stack then? Should it be part of
> > rmi_device instead?
>
> It's not coming off the stack. We're allocating it via devm_kzalloc()
> in rmi_driver_probe().
No, look at the part of the code that was quoted. "u8 irq_status[data-
>num_of_irq_regs];" is on stack.
Thanks.
--
Dmitry
On 10/23/2012 05:11 PM, Dmitry Torokhov wrote:
> On Tuesday, October 23, 2012 04:46:28 PM Christopher Heiny wrote:
>> On 10/11/2012 01:13 AM, Dmitry Torokhov wrote:
>>> On Thu, Oct 11, 2012 at 04:15:56AM +0000, Christopher Heiny wrote:
>>>> On Thursday, October 11, 2012 02:21:53 AM you wrote:
>>>>> On Sat, Oct 6, 2012 at 6:09 AM, Christopher Heiny <[email protected]>
> wrote:
[snip]
>>>>>> +static int process_interrupt_requests(struct rmi_device *rmi_dev)
>>>>>> +{
>>>>>> + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
>>>>>> + struct device *dev = &rmi_dev->dev;
>>>>>> + struct rmi_function_container *entry;
>>>>>> + u8 irq_status[data->num_of_irq_regs];
>>>>>
>>>>> Looking at this...
>>>>>
>>>>> What does the data->num_of_irq_regs actually contain?
>>>>>
>>>>> I just fear that it is something constant like always 2 or always 4,
>>>>> so there is actually, in reality, a 16 or 32 bit register hiding in
>>>>> there.
>>>>>
>>>>> In that case what you should do is to represent it as a u16 or u32 here,
>>>>> just or the bits into a status word, and then walk over that status
>>>>> word with something like ffs(bitword); ...
>>>>
>>>> Nope, it's not constant. In theory, and RMI4 based sensor can have up
>>>> to 128 functions (in practice, it's far fewer), and each function can
>>>> have as many as 7 interrupts. So the number of IRQ registers can vary
>>>> from RMI4 sensor to RMI4 sensor, and needs to be computed during the
>>>> scan of the product descriptor table.
>>>
>>> Is it a good idea to have it on stack then? Should it be part of
>>> rmi_device instead?
>>
>> It's not coming off the stack. We're allocating it via devm_kzalloc()
>> in rmi_driver_probe().
>
> No, look at the part of the code that was quoted. "u8 irq_status[data-
> num_of_irq_regs];" is on stack.
Sorry - I thought you were referring to data->num_of_irq_regs rather
than irq_status. We'll move that.
On Tue, Oct 23, 2012 at 03:10:20PM -0700, Christopher Heiny wrote:
> On 10/11/2012 10:16 PM, Mark Brown wrote:
> >On Thu, Oct 11, 2012 at 03:56:22AM +0000, Christopher Heiny wrote:
> >Fix your mailer to word wrap within paragraphs.
> Sorry - I was on the road and had to use a web interface. It looked
> OK during composition. Is this better?
Yes, that's great - thanks.
> However, a general capability in SPI core is a fine idea for future
> work, once we get the RMI4 driver ironed out. Can we agree to defer
> that for now?
Obviously there's no pressure to implement the framework feature,
however it shouldn't be implemented at the driver level.
On 10/10/2012 11:21 AM, Henrik Rydberg wrote:
> Hi Christopher,
>
>> rmi_f11.c is a driver for 2D touch sensors. It has been updated to support
>> the MT-B specification, partition control attributes between debugfs andsysfs,
>> and to use the standard bus model for loading/unloading.
>
> Please find comments inline.
Thanks very much. I though I'd replied to this along with the other
replies sent a couple of weeks ago, but don't see it in the archives. My
apologies if this is a duplicate message.
>
> Generally, if you want this merged as an input device sometime in the
> future, you need to reduce the size and complexity of the whole
> patchset. Given that you only need to feed the input subsystem with
> raw data, you can make do without most of the sysfs nodes, debug
> output, and internal gesture state.
>
> The 2D sensor data, currently living in debugfs, might eventually find
> a home in the input subsystem, based on the growing interest from
> several directions. However, if you are just looking for a way to
> transport the rmi data to userland, please consider implementing this
> under a different subsystem.
I must disagree with some of this. Feeding raw data to the input
subsystem is not the only thing that needs to be done - there needs to
be a way to configure and control the operation of the touch sensor,
both during product prototyping/development (we've partitioned that
stuff into debugfs) and during normal operation (we've put that stuff in
sysfs). If we remove the related code, that would not be possible. If
we move it to a different subsystem, where do you suggest it be moved to?
The gesture data is a different matter. Although the most popular user
space implementations may contain gesture recognition engines, not all
of them do. In that case, it is desirable to have the touch sensor
firmware perform basic gesture recognition. However, there is at this
time no way standard way to transfer that via the input subsystem. We
chose to use sysfs for this, but alternatively we could investigate
using a custom event stream in the input subsystem. We'd also be quite
happy to work with you to define a standard set of gesture events for
basic gestures.
>
>> diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
[snip]
>> +
>> +#define FINGER_STATE_MASK 0x03
>> +#define GET_FINGER_STATE(f_states, i) \
>> + ((f_states[i / 4] >> (2 * (i % 4))) & FINGER_STATE_MASK)
>
> These could be open-coded or put in a function instead.
We'll put that in a function.
>> +
>> +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
>> +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
>> +
>> +#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
>> +#define INBOX(x, y, box) (x >= box.x && x < (box.x + box.width) \
>> + && y >= box.y && y < (box.y + box.height))
>> +
>> +#define DEFAULT_XY_MAX 9999
>> +#define DEFAULT_MAX_ABS_MT_PRESSURE 255
>> +#define DEFAULT_MAX_ABS_MT_TOUCH 15
>> +#define DEFAULT_MAX_ABS_MT_ORIENTATION 1
>> +#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1
>> +#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10
>> +#define MAX_NAME_LENGTH 256
>> +
>> +static ssize_t f11_relreport_show(struct device *dev,
>> + struct device_attribute *attr,
>> + char *buf);
>> +
>> +static ssize_t f11_relreport_store(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf, size_t count);
>> +
>> +static ssize_t f11_maxPos_show(struct device *dev,
>> + struct device_attribute *attr, char *buf);
>> +
>> +static ssize_t f11_rezero_store(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf, size_t count);
>> +
>> +static void rmi_f11_free_memory(struct rmi_function_container *fc);
>> +
>> +static int rmi_f11_initialize(struct rmi_function_container *fc);
>> +
>> +static int rmi_f11_create_sysfs(struct rmi_function_container *fc);
>> +
>> +static int rmi_f11_config(struct rmi_function_container *fc);
>> +
>> +static int rmi_f11_register_devices(struct rmi_function_container *fc);
>> +
>> +static void rmi_f11_free_devices(struct rmi_function_container *fc);
>> +
>> +static void f11_set_abs_params(struct rmi_function_container *fc, int index);
>
> Please try to get rid of these.
OK
[snip]
>> +
>> +/**
>> + * @rezero - writing 1 to this will cause the sensor to calibrate to the
>> + * current capacitive state.
>> + */
>> +union f11_2d_commands {
>> + struct {
>> + bool rezero:1;
>> + u8 reserved:7;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>
> Maybe a constant would be more suitable here?
We're trying to use a single idiom for accessing registers (the
union/struct mechanism you see here). In a couple of cases, that means
we use a struct to control a single bit, instead of a constant. The end
result is the same, and we find that not mixing our idioms makes the
code a lot easier to maintain.
>
>> +
>> +/**
>> + * @nbr_of_sensors - the number of 2D sensors on the touch device.
>> + * @has_query9 - indicates the F11_2D_Query9 register exists.
>> + * @has_query11 - indicates the F11_2D_Query11 register exists.
>> + * @has_z_tuning - if set, the sensor supports Z tuning and registers
>> + * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist.
>> + * @has_pos_interpolation_tuning - TBD
>> + * @has_w_tuning - the sensor supports Wx and Wy scaling and registers
>> + * F11_2D_Ctrl36 through F11_2D_Ctrl39 exist.
>> + * @has_pitch_info - the X and Y pitches of the sensor electrodes can be
>> + * configured and registers F11_2D_Ctrl40 and F11_2D_Ctrl41 exist.
>> + * @has_default_finger_width - the default finger width settings for the
>> + * sensor can be configured and registers F11_2D_Ctrl42 through F11_2D_Ctrl44
>> + * exist.
>> + * @has_segmentation_aggressiveness - the sensor’s ability to distinguish
>> + * multiple objects close together can be configured and register F11_2D_Ctrl45
>> + * exists.
>> + * @has_tx_rw_clip - the inactive outside borders of the sensor can be
>> + * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist.
>> + * @has_drumming_correction - the sensor can be configured to distinguish
>> + * between a fast flick and a quick drumming movement and registers
>> + * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist.
>> + */
>> +struct f11_2d_device_query {
>> + union {
>> + struct {
>> + u8 nbr_of_sensors:3;
>> + bool has_query9:1;
>> + bool has_query11:1;
>> + u8 reserved:3;
>> + } __attribute__((__packed__));
>> + u8 f11_2d_query0;
>> + };
>
> Why union here? It seems all you need is a packed struct named query0.
If Dmitry's suggestion (discussed in other emails) to use void * instead
of u8 * for data buffers works out, then this union will become just a
struct.
[snip dittos of the above topic]
>
>> +
>> +/**
>> + * @number_of_fingers - describes the maximum number of fingers the 2-Dsensor
>> + * supports.
>> + * @has_rel - the sensor supports relative motion reporting.
>> + * @has_abs - the sensor supports absolute poition reporting.
>> + * @has_gestures - the sensor supports gesture reporting.
>> + * @has_sensitivity_adjust - the sensor supports a global sensitivity
>> + * adjustment.
>> + * @configurable - the sensor supports various configuration options.
>> + * @num_of_x_electrodes - the maximum number of electrodes the 2-D sensor
>> + * supports on the X axis.
>> + * @num_of_y_electrodes - the maximum number of electrodes the 2-D sensor
>> + * supports on the Y axis.
>> + * @max_electrodes - the total number of X and Y electrodes that may be
>> + * configured.
>> + * @abs_data_size - describes the format of data reported by the absolute
>> + * data source. Only one format (the kind used here) is supported at this
>> + * time.
>> + * @has_anchored_finger - then the sensor supports the high-precision second
>> + * finger tracking provided by the manual tracking and motion sensitivity
>> + * options.
>> + * @has_adjust_hyst - the difference between the finger release threshold and
>> + * the touch threshold.
>> + * @has_dribble - the sensor supports the generation of dribble interrupts,
>> + * which may be enabled or disabled with the dribble control bit.
>> + * @f11_2d_query6 - reserved.
>> + * @has_single_tap - a basic single-tap gesture is supported.
>> + * @has_tap_n_hold - tap-and-hold gesture is supported.
>> + * @has_double_tap - double-tap gesture is supported.
>> + * @has_early_tap - early tap is supported and reported as soon as the finger
>> + * lifts for any tap event that could be interpreted as either a singletap
>> + * or as the first tap of a double-tap or tap-and-hold gesture.
>> + * @has_flick - flick detection is supported.
>> + * @has_press - press gesture reporting is supported.
>> + * @has_pinch - pinch gesture detection is supported.
>> + * @has_palm_det - the 2-D sensor notifies the host whenever a large conductive
>> + * object such as a palm or a cheek touches the 2-D sensor.
>> + * @has_rotate - rotation gesture detection is supported.
>> + * @has_touch_shapes - TouchShapes are supported. A TouchShape is a fixed
>> + * rectangular area on the sensor that behaves like a capacitive button.
>> + * @has_scroll_zones - scrolling areas near the sensor edges are supported.
>> + * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported;
>> + * if 0, then only two are supported.
>> + * @has_multi_finger_scroll - the multifinger_scrolling bit will be setwhen
>> + * more than one finger is involved in a scrolling action.
>> + * @nbr_touch_shapes - the total number of touch shapes supported.
>> + */
>> +struct f11_2d_sensor_query {
>> + union {
>> + struct {
>> + /* query1 */
>> + u8 number_of_fingers:3;
>> + bool has_rel:1;
>> + bool has_abs:1;
>> + bool has_gestures:1;
>> + bool has_sensitivity_adjust:1;
>> + bool configurable:1;
>> + /* query2 */
>> + u8 num_of_x_electrodes:7;
>> + u8 reserved_1:1;
>> + /* query3 */
>> + u8 num_of_y_electrodes:7;
>> + u8 reserved_2:1;
>> + /* query4 */
>> + u8 max_electrodes:7;
>> + u8 reserved_3:1;
>> + } __attribute__((__packed__));
>> + u8 f11_2d_query1__4[4];
>> + };
>> +
>> + union {
>> + struct {
>> + u8 abs_data_size:3;
>> + bool has_anchored_finger:1;
>> + bool has_adj_hyst:1;
>> + bool has_dribble:1;
>> + u8 reserved_4:2;
>> + } __attribute__((__packed__));
>> + u8 f11_2d_query5;
>> + };
>> +
>> + u8 f11_2d_query6;
>> +
>> + union {
>> + struct {
>> + bool has_single_tap:1;
>> + bool has_tap_n_hold:1;
>> + bool has_double_tap:1;
>> + bool has_early_tap:1;
>> + bool has_flick:1;
>> + bool has_press:1;
>> + bool has_pinch:1;
>> + bool padding:1;
>> +
>> + bool has_palm_det:1;
>> + bool has_rotate:1;
>> + bool has_touch_shapes:1;
>> + bool has_scroll_zones:1;
>> + bool has_individual_scroll_zones:1;
>> + bool has_multi_finger_scroll:1;
>> + } __attribute__((__packed__));
>> + u8 f11_2d_query7__8[2];
>> + };
>> +
>> + union f11_2d_query9 query9;
>> +
>> + union {
>> + struct {
>> + u8 nbr_touch_shapes:5;
>> + } __attribute__((__packed__));
>> + u8 f11_2d_query10;
>> + };
>> +};
>
> Ditto. It is nice with some documentation, but in this case, it might
> make sense to add it on the same line in the struct. Also, since an MT
> device outputs the raw position data and leaves the gestures to
> userspace, this whole construct is more complex than it needs to be.
See remarks on gestures at the top of this email.
>
>> +
>> +/**
>> + * @reporting_mode - controls how often finger position data is reported.
>> + * @abs_pos_filt - when set, enables various noise and jitter filtering
>> + * algorithms for absolute reports.
>> + * @rel_pos_filt - when set, enables various noise and jitter filtering
>> + * algorithms for relative reports.
>> + * @rel_ballistics - enables ballistics processing for the relative finger
>> + * motion on the 2-D sensor.
>> + * @dribble - enables the dribbling feature.
>> + * @report_beyond_clip - when this is set, fingers outside the active area
>> + * specified by the x_clip and y_clip registers will be reported, but with
>> + * reported finger position clipped to the edge of the active area.
>> + * @palm_detect_thresh - the threshold at which a wide finger is considered a
>> + * palm. A value of 0 inhibits palm detection.
>> + * @motion_sensitivity - specifies the threshold an anchored finger must move
>> + * before it is considered no longer anchored. High values mean more
>> + * sensitivity.
>> + * @man_track_en - for anchored finger tracking, whether the host (1) or the
>> + * device (0) determines which finger is the tracked finger.
>> + * @man_tracked_finger - when man_track_en is 1, specifies whether finger 0 or
>> + * finger 1 is the tracked finger.
>> + * @delta_x_threshold - 2-D position update interrupts are inhibited unless
>> + * the finger moves more than a certain threshold distance along the X axis.
>> + * @delta_y_threshold - 2-D position update interrupts are inhibited unless
>> + * the finger moves more than a certain threshold distance along the Y axis.
>> + * @velocity - When rel_ballistics is set, this register defines the
>> + * velocity ballistic parameter applied to all relative motion events.
>> + * @acceleration - When rel_ballistics is set, this register defines the
>> + * acceleration ballistic parameter applied to all relative motion events.
>> + * @sensor_max_x_pos - the maximum X coordinate reported by the sensor.
>> + * @sensor_max_y_pos - the maximum Y coordinate reported by the sensor.
>> + */
>> +union f11_2d_ctrl0_9 {
>> + struct {
>> + /* F11_2D_Ctrl0 */
>> + u8 reporting_mode:3;
>> + bool abs_pos_filt:1;
>> + bool rel_pos_filt:1;
>> + bool rel_ballistics:1;
>> + bool dribble:1;
>> + bool report_beyond_clip:1;
>> + /* F11_2D_Ctrl1 */
>> + u8 palm_detect_thres:4;
>> + u8 motion_sensitivity:2;
>> + bool man_track_en:1;
>> + bool man_tracked_finger:1;
>> + /* F11_2D_Ctrl2 and 3 */
>> + u8 delta_x_threshold:8;
>> + u8 delta_y_threshold:8;
>> + /* F11_2D_Ctrl4 and 5 */
>> + u8 velocity:8;
>> + u8 acceleration:8;
>> + /* F11_2D_Ctrl6 thru 9 */
>> + u16 sensor_max_x_pos:12;
>> + u8 ctrl7_reserved:4;
>> + u16 sensor_max_y_pos:12;
>> + u8 ctrl9_reserved:4;
>> + } __attribute__((__packed__));
>> + struct {
>> + u8 regs[10];
>> + u16 address;
>> + } __attribute__((__packed__));
>> +};
>> +
>> +/**
>> + * @single_tap_int_enable - enable tap gesture recognition.
>> + * @tap_n_hold_int_enable - enable tap-and-hold gesture recognition.
>> + * @double_tap_int_enable - enable double-tap gesture recognition.
>> + * @early_tap_int_enable - enable early tap notification.
>> + * @flick_int_enable - enable flick detection.
>> + * @press_int_enable - enable press gesture recognition.
>> + * @pinch_int_enable - enable pinch detection.
>> + */
>> +union f11_2d_ctrl10 {
>> + struct {
>> + bool single_tap_int_enable:1;
>> + bool tap_n_hold_int_enable:1;
>> + bool double_tap_int_enable:1;
>> + bool early_tap_int_enable:1;
>> + bool flick_int_enable:1;
>> + bool press_int_enable:1;
>> + bool pinch_int_enable:1;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +/**
>> + * @palm_detect_int_enable - enable palm detection feature.
>> + * @rotate_int_enable - enable rotate gesture detection.
>> + * @touch_shape_int_enable - enable the TouchShape feature.
>> + * @scroll_zone_int_enable - enable scroll zone reporting.
>> + * @multi_finger_scroll_int_enable - enable the multfinger scroll feature.
>> + */
>> +union f11_2d_ctrl11 {
>> + struct {
>> + bool palm_detect_int_enable:1;
>> + bool rotate_int_enable:1;
>> + bool touch_shape_int_enable:1;
>> + bool scroll_zone_int_enable:1;
>> + bool multi_finger_scroll_int_enable:1;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +union f11_2d_ctrl12 {
>> + struct {
>> + u8 sensor_map:7;
>> + bool xy_sel:1;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +/**
>> + * @sens_adjustment - allows a host to alter the overall sensitivity ofa
>> + * 2-D sensor. A positive value in this register will make the sensor more
>> + * sensitive than the factory defaults, and a negative value will make it
>> + * less sensitive.
>> + * @hyst_adjustment - increase the touch/no-touch hysteresis by 2 Z-units for
>> + * each one unit increment in this setting.
>> + */
>> +union f11_2d_ctrl14 {
>> + struct {
>> + s8 sens_adjustment:5;
>> + u8 hyst_adjustment:3;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +/**
>> + * @max_tap_time - the maximum duration of a tap, in 10-millisecond units.
>> + */
>> +union f11_2d_ctrl15 {
>> + struct {
>> + u8 max_tap_time:8;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +/**
>> + * @min_press_time - The minimum duration required for stationary finger(s) to
>> + * generate a press gesture, in 10-millisecond units.
>> + */
>> +union f11_2d_ctrl16 {
>> + struct {
>> + u8 min_press_time:8;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +/**
>> + * @max_tap_distance - Determines the maximum finger movement allowed during
>> + * a tap, in 0.1-millimeter units.
>> + */
>> +union f11_2d_ctrl17 {
>> + struct {
>> + u8 max_tap_distance:8;
>> + } __attribute__((__packed__));
>> + u8 reg;
>> +};
>> +
>> +/**
>> + * @min_flick_distance - the minimum finger movement for a flick gesture,
>> + * in 1-millimeter units.
>> + * @min_flick_speed - the minimum finger speed for a flick gesture, in
>> + * 10-millimeter/second units.
>> + */
>> +union f11_2d_ctrl18_19 {
>> + struct {
>> + u8 min_flick_distance:8;
>> + u8 min_flick_speed:8;
>> + } __attribute__((__packed__));
>> + u8 reg[2];
>> +};
>> +
>> +/**
>> + * @pen_detect_enable - enable reporting of stylus activity.
>> + * @pen_jitter_filter_enable - Setting this enables the stylus anti-jitter
>> + * filter.
>> + * @pen_z_threshold - This is the stylus-detection lower threshold. Smaller
>> + * values result in higher sensitivity.
>> + */
>> +union f11_2d_ctrl20_21 {
>> + struct {
>> + bool pen_detect_enable:1;
>> + bool pen_jitter_filter_enable:1;
>> + u8 ctrl20_reserved:6;
>> + u8 pen_z_threshold:8;
>> + } __attribute__((__packed__));
>> + u8 reg[2];
>> +};
>
> Given that most of the above will not be used in this driver, it can
> probably be compressed quite a bit.
I'm not sure why you say these will not be used. They are currently or
soon will be used by userspace control and configuration programs.
>
>> +
>> +/**
>> + * These are not accessible through sysfs yet.
>> + *
>> + * @proximity_detect_int_en - enable proximity detection feature.
>> + * @proximity_jitter_filter_en - enables an anti-jitter filter on proximity
>> + * data.
>> + * @proximity_detection_z_threshold - the threshold for finger-proximity
>> + * detection.
>> + * @proximity_delta_x_threshold - In reduced-reporting modes, this is the
>> + * threshold for proximate-finger movement in the direction parallel tothe
>> + * X-axis.
>> + * @proximity_delta_y_threshold - In reduced-reporting modes, this is the
>> + * threshold for proximate-finger movement in the direction parallel tothe
>> + * Y-axis.
>> + * * @proximity_delta_Z_threshold - In reduced-reporting modes, this isthe
>> + * threshold for proximate-finger movement in the direction parallel tothe
>> + * Z-axis.
>> + */
>> +union f11_2d_ctrl22_26 {
>> + struct {
>> + /* control 22 */
>> + bool proximity_detect_int_en:1;
>> + bool proximity_jitter_filter_en:1;
>> + u8 f11_2d_ctrl6_b3__7:6;
>> +
>> + /* control 23 */
>> + u8 proximity_detection_z_threshold;
>> +
>> + /* control 24 */
>> + u8 proximity_delta_x_threshold;
>> +
>> + /* control 25 */
>> + u8 proximity_delta_y_threshold;
>> +
>> + /* control 26 */
>> + u8 proximity_delta_z_threshold;
>> + } __attribute__((__packed__));
>> + u8 regs[5];
>> +};
>> +
>> +/**
>> + * @palm_detecy_sensitivity - When this value is small, smaller objectswill
>> + * be identified as palms; when this value is large, only larger objects will
>> + * be identified as palms. 0 represents the factory default.
>> + * @suppress_on_palm_detect - when set, all F11 interrupts except palm_detect
>> + * are suppressed while a palm is detected.
>> + */
>> +union f11_2d_ctrl27 {
>> + struct {
>> + s8 palm_detect_sensitivity:4;
>> + bool suppress_on_palm_detect:1;
>> + u8 f11_2d_ctrl27_b5__7:3;
>> + } __attribute__((__packed__));
>> + u8 regs[1];
>> +};
>> +
>> +/**
>> + * @multi_finger_scroll_mode - allows choice of multi-finger scroll mode and
>> + * determines whether and how X or Y displacements are reported.
>> + * @edge_motion_en - enables the edge_motion feature.
>> + * @multi_finger_scroll_momentum - controls the length of time that scrolling
>> + * continues after fingers have been lifted.
>> + */
>> +union f11_2d_ctrl28 {
>> + struct {
>> + u8 multi_finger_scroll_mode:2;
>> + bool edge_motion_en:1;
>> + bool f11_2d_ctrl28b_3:1;
>> + u8 multi_finger_scroll_momentum:4;
>> + } __attribute__((__packed__));
>> + u8 regs[1];
>> +};
>> +
>> +/**
>> + * @z_touch_threshold - Specifies the finger-arrival Z threshold. Largevalues
>> + * may cause smaller fingers to be rejected.
>> + * @z_touch_hysteresis - Specifies the difference between the finger-arrival
>> + * Z threshold and the finger-departure Z threshold.
>> + */
>> +union f11_2d_ctrl29_30 {
>> + struct {
>> + u8 z_touch_threshold;
>> + u8 z_touch_hysteresis;
>> + } __attribute__((__packed__));
>> + struct {
>> + u8 regs[2];
>> + u16 address;
>> + } __attribute__((__packed__));
>> +};
>> +
>> +
>> +struct f11_2d_ctrl {
>> + union f11_2d_ctrl0_9 *ctrl0_9;
>> + union f11_2d_ctrl10 *ctrl10;
>> + union f11_2d_ctrl11 *ctrl11;
>> + union f11_2d_ctrl12 *ctrl12;
>> + u8 ctrl12_size;
>> + union f11_2d_ctrl14 *ctrl14;
>> + union f11_2d_ctrl15 *ctrl15;
>> + union f11_2d_ctrl16 *ctrl16;
>> + union f11_2d_ctrl17 *ctrl17;
>> + union f11_2d_ctrl18_19 *ctrl18_19;
>> + union f11_2d_ctrl20_21 *ctrl20_21;
>> + union f11_2d_ctrl22_26 *ctrl22_26;
>> + union f11_2d_ctrl27 *ctrl27;
>> + union f11_2d_ctrl28 *ctrl28;
>> + union f11_2d_ctrl29_30 *ctrl29_30;
>> +};
>
> Will any of this data be used at all?
Yes.
>
>> +
>> +/**
>> + * @x_msb - top 8 bits of X finger position.
>> + * @y_msb - top 8 bits of Y finger position.
>> + * @x_lsb - bottom 4 bits of X finger position.
>> + * @y_lsb - bottom 4 bits of Y finger position.
>> + * @w_y - contact patch width along Y axis.
>> + * @w_x - contact patch width along X axis.
>> + * @z - finger Z value (proxy for pressure).
>> + */
>> +struct f11_2d_data_1_5 {
>> + u8 x_msb;
>> + u8 y_msb;
>> + u8 x_lsb:4;
>> + u8 y_lsb:4;
>> + u8 w_y:4;
>> + u8 w_x:4;
>> + u8 z;
>> +};
>> +
>> +/**
>> + * @delta_x - relative motion along X axis.
>> + * @delta_y - relative motion along Y axis.
>> + */
>> +struct f11_2d_data_6_7 {
>> + s8 delta_x;
>> + s8 delta_y;
>> +};
>> +
>> +/**
>> + * @single_tap - a single tap was recognized.
>> + * @tap_and_hold - a tap-and-hold gesture was recognized.
>> + * @double_tap - a double tap gesture was recognized.
>> + * @early_tap - a tap gesture might be happening.
>> + * @flick - a flick gesture was detected.
>> + * @press - a press gesture was recognized.
>> + * @pinch - a pinch gesture was detected.
>> + */
>> +struct f11_2d_data_8 {
>> + bool single_tap:1;
>> + bool tap_and_hold:1;
>> + bool double_tap:1;
>> + bool early_tap:1;
>> + bool flick:1;
>> + bool press:1;
>> + bool pinch:1;
>> +};
>> +
>> +/**
>> + * @palm_detect - a palm or other large object is in contact with the sensor.
>> + * @rotate - a rotate gesture was detected.
>> + * @shape - a TouchShape has been activated.
>> + * @scrollzone - scrolling data is available.
>> + * @finger_count - number of fingers involved in the reported gesture.
>> + */
>> +struct f11_2d_data_9 {
>> + bool palm_detect:1;
>> + bool rotate:1;
>> + bool shape:1;
>> + bool scrollzone:1;
>> + u8 finger_count:3;
>> +};
>> +
>> +/**
>> + * @pinch_motion - when a pinch gesture is detected, this is the changein
>> + * distance between the two fingers since this register was last read.
>> + */
>> +struct f11_2d_data_10 {
>> + s8 pinch_motion;
>> +};
>> +
>> +/**
>> + * @x_flick_dist - when a flick gesture is detected, the distance of flick
>> + * gesture in X direction.
>> + * @y_flick_dist - when a flick gesture is detected, the distance of flick
>> + * gesture in Y direction.
>> + * @flick_time - the total time of the flick gesture, in 10ms units.
>> + */
>> +struct f11_2d_data_10_12 {
>> + s8 x_flick_dist;
>> + s8 y_flick_dist;
>> + u8 flick_time;
>> +};
>> +
>> +/**
>> + * @motion - when a rotate gesture is detected, the accumulated distance
>> + * of the rotate motion. Clockwise motion is positive and counterclockwise
>> + * motion is negative.
>> + * @finger_separation - when a rotate gesture is detected, the distance
>> + * between the fingers.
>> + */
>> +struct f11_2d_data_11_12 {
>> + s8 motion;
>> + u8 finger_separation;
>> +};
>> +
>> +/**
>> + * @shape_n - a bitmask of the currently activate TouchShapes (if any).
>> + */
>> +struct f11_2d_data_13 {
>> + u8 shape_n;
>> +};
>> +
>> +/**
>> + * @horizontal - chiral scrolling distance in the X direction.
>> + * @vertical - chiral scrolling distance in the Y direction.
>> + */
>> +struct f11_2d_data_14_15 {
>> + s8 horizontal;
>> + s8 vertical;
>> +};
>> +
>> +/**
>> + * @x_low - scroll zone motion along the lower edge of the sensor.
>> + * @y_right - scroll zone motion along the right edge of the sensor.
>> + * @x_upper - scroll zone motion along the upper edge of the sensor.
>> + * @y_left - scroll zone motion along the left edge of the sensor.
>> + */
>> +struct f11_2d_data_14_17 {
>> + s8 x_low;
>> + s8 y_right;
>> + s8 x_upper;
>> + s8 y_left;
>> +};
>> +
>> +struct f11_2d_data {
>> + u8 *f_state;
>> + const struct f11_2d_data_1_5 *abs_pos;
>> + const struct f11_2d_data_6_7 *rel_pos;
>> + const struct f11_2d_data_8 *gest_1;
>> + const struct f11_2d_data_9 *gest_2;
>> + const struct f11_2d_data_10 *pinch;
>> + const struct f11_2d_data_10_12 *flick;
>> + const struct f11_2d_data_11_12 *rotate;
>> + const struct f11_2d_data_13 *shapes;
>> + const struct f11_2d_data_14_15 *multi_scroll;
>> + const struct f11_2d_data_14_17 *scroll_zones;
>> +};
>> +
>> +struct f11_2d_sensor {
>> + struct rmi_f11_2d_axis_alignment axis_align;
>> + struct f11_2d_sensor_query sens_query;
>> + struct f11_2d_data data;
>> + int prev_x[F11_MAX_NUM_OF_FINGERS];
>> + int prev_y[F11_MAX_NUM_OF_FINGERS];
>> + u16 max_x;
>> + u16 max_y;
>> + u8 nbr_fingers;
>> + u8 finger_tracker[F11_MAX_NUM_OF_FINGERS];
>> + u8 *data_pkt;
>> + int pkt_size;
>> + u8 sensor_index;
>> + u8 *button_map;
>> + struct rmi_f11_virtualbutton_map virtual_buttons;
>> + bool type_a;
>> + char input_name[MAX_NAME_LENGTH];
>> + char input_phys[MAX_NAME_LENGTH];
>> + struct input_dev *input;
>> + struct input_dev *mouse_input;
>> + struct rmi_function_container *fc;
>> +
>> +#ifdef CONFIG_RMI4_DEBUG
>> + struct dentry *debugfs_flip;
>> + struct dentry *debugfs_clip;
>> + struct dentry *debugfs_delta_threshold;
>> + struct dentry *debugfs_offset;
>> + struct dentry *debugfs_swap;
>> + struct dentry *debugfs_type_a;
>> +#endif
>> +};
>
> Up to this point in the file, very little is essential to the input deivce.
I really don't understand what you're saying here. If we remove the
things corresponding to the data reported by the sensor, how can that
data be read and reported to user space (whether by input subsystem or
via sysfs)? If we remove the configuration parameters, how should the
sensor be configured to operate correctly?
>
>> +
>> +struct f11_data {
>> + struct f11_2d_device_query dev_query;
>> + struct f11_2d_ctrl dev_controls;
>> + struct mutex dev_controls_mutex;
>> + u16 rezero_wait_ms;
>> + struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
>> +
>> +#ifdef CONFIG_RMI4_DEBUG
>> + struct dentry *debugfs_rezero_wait;
>> +#endif
>> +};
>> +
>> +enum finger_state_values {
>> + F11_NO_FINGER = 0x00,
>> + F11_PRESENT = 0x01,
>> + F11_INACCURATE = 0x02,
>> + F11_RESERVED = 0x03
>> +};
>> +
>> +/* ctrl sysfs files */
>> +show_store_union_struct_prototype(abs_pos_filt)
>> +show_store_union_struct_prototype(z_touch_threshold)
>> +show_store_union_struct_prototype(z_touch_hysteresis)
>> +
>> +#ifdef CONFIG_RMI4_DEBUG
>> +
>> +struct sensor_debugfs_data {
>> + bool done;
>> + struct f11_2d_sensor *sensor;
>> +};
>
> And this is really needed?
Yes - please see notes on configuration at the top of this email.
>
> [...]
>
>> +static void rmi_f11_abs_pos_report(struct f11_data *f11,
>> + struct f11_2d_sensor *sensor,
>> + u8 finger_state, u8 n_finger)
>> +{
>> + struct f11_2d_data *data = &sensor->data;
>> + struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
>> + u8 prev_state = sensor->finger_tracker[n_finger];
>
> No need to keep track of the old state.
We'll look into that. In previous versions of this code, some of the
user space implementations didn't behave correctly if we didn't do that.
However, since that time we've dropped support for older user spaces,
so maybe we don't need that any more.
>> + int x, y, z;
>> + int w_x, w_y, w_max, w_min, orient;
>> + int temp;
>> +
>> + if (prev_state && !finger_state) {
>> + /* this is a release */
>> + x = y = z = w_max = w_min = orient = 0;
>
> This data is not sent during a release.
OK.
>
>> + } else if (!prev_state && !finger_state) {
>> + /* nothing to report */
>> + return;
>> + } else {
[snip]
>> + x = max(axis_align->clip_X_low, x);
>> + y = max(axis_align->clip_Y_low, y);
>> + if (axis_align->clip_X_high)
>> + x = min(axis_align->clip_X_high, x);
>> + if (axis_align->clip_Y_high)
>> + y = min(axis_align->clip_Y_high, y);
>
> Why is the clipping configurable?
Because during prototyping of new products, engineers sometimes use a
sensor that is bigger than the display and not necessarily aligned to
the origin. This is frequent enough a use case that we include it in
the driver as a convenience feature in debugfs.
>
>> +
>> + }
>> +
>> + /* Some UIs ignore W of zero, so we fudge it to 1 for pens. */
>> + if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
>> + get_tool_type(sensor, finger_state) == MT_TOOL_PEN) {
>> + w_max = max(1, w_max);
>> + w_min = max(1, w_min);
>> + }
>
> Is this not true for all tool types?
In our experience, no. It ought to be true in an ideal world, but
unfortunately we aren't living in one :-(.
>> +
>> + if (sensor->type_a) {
>> + input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
>> + input_report_abs(sensor->input, ABS_MT_TOOL_TYPE,
>> + get_tool_type(sensor, finger_state));
>> + } else {
>> + input_mt_slot(sensor->input, n_finger);
>> + input_mt_report_slot_state(sensor->input,
>> + get_tool_type(sensor, finger_state), finger_state);
>> + }
>
> The driver should only report MT-B, please.
Is that valid even if we have a type A sensor?
>> + input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
>> + input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max);
>> + input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min);
>> + input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient);
>> + input_report_abs(sensor->input, ABS_MT_POSITION_X, x);
>> + input_report_abs(sensor->input, ABS_MT_POSITION_Y, y);
>
> Only if finger_state is true.
OK.
>
>> + dev_dbg(&sensor->fc->dev,
>> + "finger[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
>> + n_finger, finger_state, x, y, z, w_max, w_min);
>> + /* MT sync between fingers */
>> + if (sensor->type_a)
>> + input_mt_sync(sensor->input);
>> +
>> + sensor->finger_tracker[n_finger] = finger_state;
>> +}
>> +
>> +#ifdef CONFIG_RMI4_VIRTUAL_BUTTON
>> +static int rmi_f11_virtual_button_handler(struct f11_2d_sensor *sensor)
>> +{
>> + int i;
>> + int x;
>> + int y;
>> + struct rmi_button_map *virtualbutton_map;
>> +
>> + if (sensor->sens_query.has_gestures &&
>> + sensor->data.gest_1->single_tap) {
>> + virtualbutton_map = &sensor->virtualbutton_map;
>> + x = ((sensor->data.abs_pos[0].x_msb << 4) |
>> + sensor->data.abs_pos[0].x_lsb);
>> + y = ((sensor->data.abs_pos[0].y_msb << 4) |
>> + sensor->data.abs_pos[0].y_lsb);
>> + for (i = 0; i < virtualbutton_map->buttons; i++) {
>> + if (INBOX(x, y, virtualbutton_map->map[i])) {
>> + input_report_key(sensor->input,
>> + virtualbutton_map->map[i].code, 1);
>> + input_report_key(sensor->input,
>> + virtualbutton_map->map[i].code, 0);
>> + input_sync(sensor->input);
>> + return 0;
>> + }
>> + }
>> + }
>> + return 0;
>> +}
>> +#else
>> +#define rmi_f11_virtual_button_handler(sensor)
>> +#endif
>
> No virtual buttons, please, this can easily be mapped in userspace.
Not for all user spaces. But we can remove this from the next submission.
>
>> +static void rmi_f11_finger_handler(struct f11_data *f11,
>> + struct f11_2d_sensor *sensor)
>> +{
>> + const u8 *f_state = sensor->data.f_state;
>> + u8 finger_state;
>> + u8 finger_pressed_count;
>> + u8 i;
>> +
>> + for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
>> + /* Possible of having 4 fingers per f_statet register */
>> + finger_state = GET_FINGER_STATE(f_state, i);
>> +
>> + if (finger_state == F11_RESERVED) {
>> + pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
>> + i, finger_state);
>> + continue;
>> + } else if ((finger_state == F11_PRESENT) ||
>> + (finger_state == F11_INACCURATE)) {
>> + finger_pressed_count++;
>> + }
>> +
>> + if (sensor->data.abs_pos)
>> + rmi_f11_abs_pos_report(f11, sensor, finger_state, i);
>> +
>> + if (sensor->data.rel_pos)
>> + rmi_f11_rel_pos_report(sensor, i);
>> + }
>> + input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
>
> Please use input_mt_sync_frame() here instead.
OK
>
>> + input_sync(sensor->input);
>> +}
>
> Stopping here.