This is a new patch series which squashes all of the development
history of the RMI4 driver into patches based on functionality. The
first patch adds the core RMI4 functionality needed by all RMI4 devices
and then the additional patches add transport and function drivers for
supporting various devices.
Touchpads which are currently using hid-rmi should have the same
functionality, but now knowledge of RMI is handled in the core instead
of in hid-rmi. These patches also provide basic finger reporting for a
wide range of RMI4 touchscreens connected to I2C and SPI busses.
However, additional work may need to be done to implement product specific
features.
I tried to include all of the feedback I received from the previous
patches I posted. However, I did not come up with a satisfactory solution
for allowing function drivers to be built as modules. I think it is fine
to allow function drivers to be enabled or disabled in the core at build
time. However, if supporting function drivers as modules is a must have
for upstreaming I can continue to try to find a solution. Also, I went
ahead and removed support for polling.
Thanks,
Andrew
Andrew Duggan (10):
Input: synaptics-rmi4: Add support for Synaptics RMI4 devices
Input: synaptics-rmi4: Add I2C transport driver
Input: synaptics-rmi4: Add device tree support for RMI4 I2C devices
Input: synaptics-rmi4: Add support for 2D sensors and F11
Input: synaptics-rmi4: Add device tree support for 2d sensors and F11
Input: synaptics-rmi4: Add support for F12
Input: synaptics-rmi4: Add support for F30
Input: synaptics-rmi4: Add SPI transport driver
Input: synaptics-rmi4: Add device tree support to the SPI transport
driver
HID: rmi: Make hid-rmi a transport driver for synaptics-rmi4
.../bindings/input/rmi4/rmi_2d_sensor.txt | 55 +
.../devicetree/bindings/input/rmi4/rmi_f01.txt | 40 +
.../devicetree/bindings/input/rmi4/rmi_i2c.txt | 53 +
.../devicetree/bindings/input/rmi4/rmi_spi.txt | 57 +
.../devicetree/bindings/vendor-prefixes.txt | 1 +
drivers/hid/hid-rmi.c | 922 ++-----------
drivers/input/Kconfig | 2 +
drivers/input/Makefile | 2 +
drivers/input/rmi4/Kconfig | 94 ++
drivers/input/rmi4/Makefile | 15 +
drivers/input/rmi4/rmi_2d_sensor.c | 323 +++++
drivers/input/rmi4/rmi_2d_sensor.h | 88 ++
drivers/input/rmi4/rmi_bus.c | 419 ++++++
drivers/input/rmi4/rmi_bus.h | 195 +++
drivers/input/rmi4/rmi_driver.c | 1112 ++++++++++++++++
drivers/input/rmi4/rmi_driver.h | 126 ++
drivers/input/rmi4/rmi_f01.c | 570 ++++++++
drivers/input/rmi4/rmi_f11.c | 1354 ++++++++++++++++++++
drivers/input/rmi4/rmi_f12.c | 487 +++++++
drivers/input/rmi4/rmi_f30.c | 419 ++++++
drivers/input/rmi4/rmi_i2c.c | 270 ++++
drivers/input/rmi4/rmi_spi.c | 464 +++++++
include/linux/rmi.h | 415 ++++++
include/uapi/linux/input.h | 1 +
24 files changed, 6638 insertions(+), 846 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt
create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt
create mode 100644 drivers/input/rmi4/Kconfig
create mode 100644 drivers/input/rmi4/Makefile
create mode 100644 drivers/input/rmi4/rmi_2d_sensor.c
create mode 100644 drivers/input/rmi4/rmi_2d_sensor.h
create mode 100644 drivers/input/rmi4/rmi_bus.c
create mode 100644 drivers/input/rmi4/rmi_bus.h
create mode 100644 drivers/input/rmi4/rmi_driver.c
create mode 100644 drivers/input/rmi4/rmi_driver.h
create mode 100644 drivers/input/rmi4/rmi_f01.c
create mode 100644 drivers/input/rmi4/rmi_f11.c
create mode 100644 drivers/input/rmi4/rmi_f12.c
create mode 100644 drivers/input/rmi4/rmi_f30.c
create mode 100644 drivers/input/rmi4/rmi_i2c.c
create mode 100644 drivers/input/rmi4/rmi_spi.c
create mode 100644 include/linux/rmi.h
--
2.5.0
Synaptics uses the Register Mapped Interface (RMI) protocol as a
communications interface for their devices. This driver adds the core
functionality needed to interface with RMI4 devices.
RMI devices can be connected to the host via several transport protocols
and can supports a wide variety of functionality defined by RMI functions.
Support for transport protocols and RMI functions are implemented in
individual drivers. The RMI4 core driver uses a bus architecture to
facilitate the various combinations of transport and function drivers
needed by a particular device.
Signed-off-by: Andrew Duggan <[email protected]>
Signed-off-by: Christopher Heiny <[email protected]>
---
drivers/input/Kconfig | 2 +
drivers/input/Makefile | 2 +
drivers/input/rmi4/Kconfig | 26 +
drivers/input/rmi4/Makefile | 4 +
drivers/input/rmi4/rmi_bus.c | 318 ++++++++++++
drivers/input/rmi4/rmi_bus.h | 195 +++++++
drivers/input/rmi4/rmi_driver.c | 1085 +++++++++++++++++++++++++++++++++++++++
drivers/input/rmi4/rmi_driver.h | 102 ++++
drivers/input/rmi4/rmi_f01.c | 570 ++++++++++++++++++++
include/linux/rmi.h | 263 ++++++++++
include/uapi/linux/input.h | 1 +
11 files changed, 2568 insertions(+)
create mode 100644 drivers/input/rmi4/Kconfig
create mode 100644 drivers/input/rmi4/Makefile
create mode 100644 drivers/input/rmi4/rmi_bus.c
create mode 100644 drivers/input/rmi4/rmi_bus.h
create mode 100644 drivers/input/rmi4/rmi_driver.c
create mode 100644 drivers/input/rmi4/rmi_driver.h
create mode 100644 drivers/input/rmi4/rmi_f01.c
create mode 100644 include/linux/rmi.h
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a35532e..6261874 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -201,6 +201,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 0c9302c..595820b 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -26,3 +26,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+
+obj-$(CONFIG_RMI4_CORE) += rmi4/
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
new file mode 100644
index 0000000..45a9ff4
--- /dev/null
+++ b/drivers/input/rmi4/Kconfig
@@ -0,0 +1,26 @@
+#
+# RMI4 configuration
+#
+config RMI4_CORE
+ tristate "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_CORE
+ select DEBUG_FS
+ 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.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
new file mode 100644
index 0000000..b33768f
--- /dev/null
+++ b/drivers/input/rmi4/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_RMI4_CORE) += rmi_core.o
+rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
+
+ccflags-$(CONFIG_RMI4_DEBUG) += -DDEBUG
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
new file mode 100644
index 0000000..f3e1f2c
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#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>
+#include <linux/of.h>
+#include "rmi_bus.h"
+#include "rmi_driver.h"
+
+/*
+ * RMI Physical devices
+ *
+ * Physical RMI device consists of several functions serving particular
+ * purpose. For example F11 is a 2D touch sensor while F01 is a generic
+ * function present in every RMI device.
+ */
+
+static void rmi_release_device(struct device *dev)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ kfree(rmi_dev);
+}
+
+static struct device_type rmi_device_type = {
+ .name = "rmi_sensor",
+ .release = rmi_release_device,
+};
+
+bool rmi_is_physical_device(struct device *dev)
+{
+ return dev->type == &rmi_device_type;
+}
+
+/**
+ * rmi_register_transport_device - register a transport device connection
+ * on the RMI bus. Transport drivers provide communication from the devices
+ * on a bus (such as SPI, I2C, and so on) to the RMI4 sensor.
+ *
+ * @xport: the transport device to register
+ */
+int rmi_register_transport_device(struct rmi_transport_dev *xport)
+{
+ static atomic_t transport_device_count = ATOMIC_INIT(0);
+ struct rmi_device *rmi_dev;
+ int error;
+
+ rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
+ if (!rmi_dev)
+ return -ENOMEM;
+
+ device_initialize(&rmi_dev->dev);
+
+ rmi_dev->xport = xport;
+ rmi_dev->number = atomic_inc_return(&transport_device_count) - 1;
+
+ dev_set_name(&rmi_dev->dev, "rmi%02d", rmi_dev->number);
+
+ rmi_dev->dev.bus = &rmi_bus_type;
+ rmi_dev->dev.type = &rmi_device_type;
+
+ xport->rmi_dev = rmi_dev;
+
+ error = device_add(&rmi_dev->dev);
+ if (error)
+ goto err_put_device;
+
+ dev_dbg(xport->dev, "%s: Registered %s as %s.\n", __func__,
+ dev_name(rmi_dev->xport->dev), dev_name(&rmi_dev->dev));
+
+ return 0;
+
+err_put_device:
+ put_device(&rmi_dev->dev);
+ return error;
+}
+EXPORT_SYMBOL_GPL(rmi_register_transport_device);
+
+/**
+ * rmi_unregister_transport_device - unregister a transport device connection
+ * @xport: the transport driver to unregister
+ *
+ */
+void rmi_unregister_transport_device(struct rmi_transport_dev *xport)
+{
+ struct rmi_device *rmi_dev = xport->rmi_dev;
+
+ device_del(&rmi_dev->dev);
+ put_device(&rmi_dev->dev);
+}
+EXPORT_SYMBOL(rmi_unregister_transport_device);
+
+
+/* Function specific stuff */
+
+static void rmi_release_function(struct device *dev)
+{
+ struct rmi_function *fn = to_rmi_function(dev);
+
+ kfree(fn);
+}
+
+static struct device_type rmi_function_type = {
+ .name = "rmi_function",
+ .release = rmi_release_function,
+};
+
+bool rmi_is_function_device(struct device *dev)
+{
+ return dev->type == &rmi_function_type;
+}
+
+static int rmi_function_match(struct device *dev, struct device_driver *drv)
+{
+ struct rmi_function_handler *handler = to_rmi_function_handler(drv);
+ struct rmi_function *fn = to_rmi_function(dev);
+
+ return fn->fd.function_number == handler->func;
+}
+
+static int rmi_function_probe(struct device *dev)
+{
+ struct rmi_function *fn = to_rmi_function(dev);
+ struct rmi_function_handler *handler =
+ to_rmi_function_handler(dev->driver);
+ int error;
+
+ if (handler->probe) {
+ error = handler->probe(fn);
+ return error;
+ }
+
+ return 0;
+}
+
+static int rmi_function_remove(struct device *dev)
+{
+ struct rmi_function *fn = to_rmi_function(dev);
+ struct rmi_function_handler *handler =
+ to_rmi_function_handler(dev->driver);
+
+ if (handler->remove)
+ handler->remove(fn);
+
+ return 0;
+}
+
+int rmi_register_function(struct rmi_function *fn)
+{
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ int error;
+
+ device_initialize(&fn->dev);
+
+ dev_set_name(&fn->dev, "%s.fn%02x",
+ dev_name(&rmi_dev->dev), fn->fd.function_number);
+
+ fn->dev.parent = &rmi_dev->dev;
+ fn->dev.type = &rmi_function_type;
+ fn->dev.bus = &rmi_bus_type;
+
+ error = device_add(&fn->dev);
+ if (error) {
+ dev_err(&rmi_dev->dev,
+ "Failed device_register function device %s\n",
+ dev_name(&fn->dev));
+ goto err_put_device;
+ }
+
+ dev_dbg(&rmi_dev->dev, "Registered F%02X.\n", fn->fd.function_number);
+
+ return 0;
+
+err_put_device:
+ put_device(&fn->dev);
+ return error;
+}
+
+void rmi_unregister_function(struct rmi_function *fn)
+{
+ device_del(&fn->dev);
+
+ if (fn->dev.of_node)
+ of_node_put(fn->dev.of_node);
+
+ put_device(&fn->dev);
+}
+
+/**
+ * rmi_register_function_handler - register a handler for an RMI function
+ * @handler: RMI handler that should be registered.
+ * @module: pointer to module that implements the handler
+ * @mod_name: name of the module implementing the handler
+ *
+ * This function performs additional setup of RMI function handler and
+ * registers it with the RMI core so that it can be bound to
+ * RMI function devices.
+ */
+int __rmi_register_function_handler(struct rmi_function_handler *handler,
+ struct module *owner,
+ const char *mod_name)
+{
+ struct device_driver *driver = &handler->driver;
+ int error;
+
+ driver->bus = &rmi_bus_type;
+ driver->owner = owner;
+ driver->mod_name = mod_name;
+ driver->probe = rmi_function_probe;
+ driver->remove = rmi_function_remove;
+
+ error = driver_register(&handler->driver);
+ if (error) {
+ pr_err("driver_register() failed for %s, error: %d\n",
+ handler->driver.name, error);
+ return error;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__rmi_register_function_handler);
+
+/**
+ * rmi_unregister_function_handler - unregister given RMI function handler
+ * @handler: RMI handler that should be unregistered.
+ *
+ * This function unregisters given function handler from RMI core which
+ * causes it to be unbound from the function devices.
+ */
+void rmi_unregister_function_handler(struct rmi_function_handler *handler)
+{
+ driver_unregister(&handler->driver);
+}
+EXPORT_SYMBOL_GPL(rmi_unregister_function_handler);
+
+/* Bus specific stuff */
+
+static int rmi_bus_match(struct device *dev, struct device_driver *drv)
+{
+ bool physical = rmi_is_physical_device(dev);
+
+ /* First see if types are not compatible */
+ if (physical != rmi_is_physical_driver(drv))
+ return 0;
+
+ return physical || rmi_function_match(dev, drv);
+}
+
+struct bus_type rmi_bus_type = {
+ .match = rmi_bus_match,
+ .name = "rmi",
+};
+
+static int __init rmi_bus_init(void)
+{
+ int error;
+
+ error = bus_register(&rmi_bus_type);
+ if (error) {
+ pr_err("%s: error registering the RMI bus: %d\n",
+ __func__, error);
+ return error;
+ }
+
+ error = rmi_register_f01_handler();
+ if (error) {
+ pr_err("%s: error registering the RMI F01 handler: %d\n",
+ __func__, error);
+ goto err_unregister_bus;
+ }
+
+ error = rmi_register_physical_driver();
+ if (error) {
+ pr_err("%s: error registering the RMI physical driver: %d\n",
+ __func__, error);
+ goto err_unregister_f01;
+ }
+
+ return 0;
+
+err_unregister_f01:
+ rmi_unregister_f01_handler();
+err_unregister_bus:
+ bus_unregister(&rmi_bus_type);
+ return error;
+}
+module_init(rmi_bus_init);
+
+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.
+ */
+
+ rmi_unregister_physical_driver();
+ rmi_unregister_f01_handler();
+ bus_unregister(&rmi_bus_type);
+}
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]");
+MODULE_AUTHOR("Andrew Duggan <[email protected]");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h
new file mode 100644
index 0000000..f302851
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _RMI_BUS_H
+#define _RMI_BUS_H
+
+#include <linux/rmi.h>
+
+struct rmi_device;
+
+/**
+ * struct rmi_function - 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
+ *
+ * @node: entry in device's list of functions
+ */
+struct rmi_function {
+ struct rmi_function_descriptor fd;
+ struct rmi_device *rmi_dev;
+ struct device dev;
+ struct list_head node;
+
+ unsigned int num_of_irqs;
+ unsigned int irq_pos;
+ unsigned long irq_mask[];
+};
+
+#define to_rmi_function(d) container_of(d, struct rmi_function, dev)
+
+bool rmi_is_function_device(struct device *dev);
+
+int __must_check rmi_register_function(struct rmi_function *);
+void rmi_unregister_function(struct rmi_function *);
+
+/**
+ * struct rmi_function_handler - driver routines for a particular RMI function.
+ *
+ * @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 (*probe)(struct rmi_function *fn);
+ void (*remove)(struct rmi_function *fn);
+ int (*config)(struct rmi_function *fn);
+ int (*reset)(struct rmi_function *fn);
+ int (*attention)(struct rmi_function *fn, unsigned long *irq_bits);
+};
+
+#define to_rmi_function_handler(d) \
+ container_of(d, struct rmi_function_handler, driver)
+
+int __must_check __rmi_register_function_handler(struct rmi_function_handler *,
+ struct module *, const char *);
+#define rmi_register_function_handler(handler) \
+ __rmi_register_function_handler(handler, THIS_MODULE, KBUILD_MODNAME)
+
+void rmi_unregister_function_handler(struct rmi_function_handler *);
+
+
+
+#define to_rmi_driver(d) \
+ container_of(d, struct rmi_driver, driver)
+
+#define to_rmi_device(d) container_of(d, struct rmi_device, dev)
+
+static inline struct rmi_device_platform_data *
+rmi_get_platform_data(struct rmi_device *d)
+{
+ return &d->xport->pdata;
+}
+
+bool rmi_is_physical_device(struct device *dev);
+
+/**
+ * rmi_read - read a single byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to read from
+ * @buf: The read buffer
+ *
+ * Reads a single byte of data using the underlying transport protocol
+ * into memory pointed by @buf. It returns 0 on success or a negative
+ * error code.
+ */
+static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf)
+{
+ return d->xport->ops->read_block(d->xport, 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 underlying transport protocol
+ * into memory pointed by @buf. It returns 0 on success or a negative
+ * error code.
+ */
+static inline int rmi_read_block(struct rmi_device *d, u16 addr,
+ void *buf, size_t len)
+{
+ return d->xport->ops->read_block(d->xport, 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 single byte using the underlying transport protocol. It
+ * returns zero on success or a negative error code.
+ */
+static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data)
+{
+ return d->xport->ops->write_block(d->xport, 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 transport
+ * 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,
+ const void *buf, size_t len)
+{
+ return d->xport->ops->write_block(d->xport, addr, buf, len);
+}
+
+int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data));
+
+/**
+ * module_rmi_driver() - Helper macro for registering a function driver
+ * @__rmi_driver: rmi_function_handler struct
+ *
+ * Helper macro for RMI4 function drivers which do not do anything special
+ * in module init/exit. This eliminates a lot of boilerplate. Each module
+ * may only use this macro once, and calling it replaces module_init()
+ * and module_exit().
+ */
+#define module_rmi_driver(__rmi_driver) \
+ module_driver(__rmi_driver, \
+ rmi_register_function_handler, \
+ rmi_unregister_function_handler)
+
+
+extern struct bus_type rmi_bus_type;
+
+int rmi_of_property_read_u32(struct device *dev, u32 *result,
+ const char *prop, bool optional);
+int rmi_of_property_read_u16(struct device *dev, u16 *result,
+ const char *prop, bool optional);
+int rmi_of_property_read_u8(struct device *dev, u8 *result,
+ const char *prop, bool optional);
+#endif
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
new file mode 100644
index 0000000..0d74d26
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -0,0 +1,1085 @@
+/*
+ * Copyright (c) 2011-2015 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver provides the core support for a single RMI4-based device.
+ *
+ * The RMI4 specification can be found here (URL split for line length):
+ *
+ * http://www.synaptics.com/sites/default/files/
+ * 511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/bitmap.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/kconfig.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <uapi/linux/input.h>
+#include <linux/rmi.h>
+#include "rmi_bus.h"
+#include "rmi_driver.h"
+
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+#define RMI4_PAGE_MASK 0xFF00
+
+#define RMI_DEVICE_RESET_CMD 0x01
+#define DEFAULT_RESET_DELAY_MS 100
+
+static irqreturn_t rmi_irq_thread(int irq, void *p)
+{
+ struct rmi_transport_dev *xport = p;
+ struct rmi_device *rmi_dev = xport->rmi_dev;
+ struct rmi_driver *driver = rmi_dev->driver;
+ struct rmi_driver_data *data;
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ data->attn_count++;
+ if (driver && driver->irq_handler && rmi_dev)
+ driver->irq_handler(rmi_dev, irq);
+
+ return IRQ_HANDLED;
+}
+
+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->xport->ops->disable_device)
+ rmi_dev->xport->ops->disable_device(rmi_dev->xport);
+
+ if (rmi_dev->xport->irq > 0) {
+ disable_irq(rmi_dev->xport->irq);
+ free_irq(rmi_dev->xport->irq, rmi_dev->xport);
+ }
+
+ data->enabled = false;
+}
+
+static void rmi_free_function_list(struct rmi_device *rmi_dev)
+{
+ struct rmi_function *fn, *tmp;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+
+ data->f01_container = NULL;
+
+ /* Doing it in the reverse order so F01 will be removed last */
+ list_for_each_entry_safe_reverse(fn, tmp,
+ &data->function_list, node) {
+ list_del(&fn->node);
+ rmi_unregister_function(fn);
+ }
+}
+
+static int reset_one_function(struct rmi_function *fn)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fn || !fn->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fn->dev.driver);
+ if (fh->reset) {
+ retval = fh->reset(fn);
+ if (retval < 0)
+ dev_err(&fn->dev, "Reset failed with code %d.\n",
+ retval);
+ }
+
+ return retval;
+}
+
+static int configure_one_function(struct rmi_function *fn)
+{
+ struct rmi_function_handler *fh;
+ int retval = 0;
+
+ if (!fn || !fn->dev.driver)
+ return 0;
+
+ fh = to_rmi_function_handler(fn->dev.driver);
+ if (fh->config) {
+ retval = fh->config(fn);
+ if (retval < 0)
+ dev_err(&fn->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 *entry;
+ int retval;
+
+ list_for_each_entry(entry, &data->function_list, node) {
+ 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 *entry;
+ int retval;
+
+ list_for_each_entry(entry, &data->function_list, node) {
+ retval = configure_one_function(entry);
+ if (retval < 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static void process_one_interrupt(struct rmi_driver_data *data,
+ struct rmi_function *fn)
+{
+ struct rmi_function_handler *fh;
+
+ if (!fn || !fn->dev.driver)
+ return;
+
+ fh = to_rmi_function_handler(fn->dev.driver);
+ if (fn->irq_mask && fh->attention) {
+ bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask,
+ data->irq_count);
+ if (!bitmap_empty(data->fn_irq_bits, data->irq_count))
+ fh->attention(fn, data->fn_irq_bits);
+ }
+}
+
+int rmi_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 *entry;
+ int error;
+
+ if (!rmi_dev->xport->attn_data) {
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.data_base_addr + 1,
+ data->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);
+ bitmap_and(data->irq_status, data->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.
+ */
+ mutex_unlock(&data->irq_mutex);
+
+ /*
+ * It would be nice to be able to use irq_chip to handle these
+ * nested IRQs. Unfortunately, most of the current customers for
+ * this driver are using older kernels (3.0.x) that don't support
+ * the features required for that. Once they've shifted to more
+ * recent kernels (say, 3.3 and higher), this should be switched to
+ * use irq_chip.
+ */
+ list_for_each_entry(entry, &data->function_list, node)
+ if (entry->irq_mask)
+ process_one_interrupt(data, entry);
+
+ if (data->input)
+ input_sync(data->input);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests);
+
+static int enable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_transport_dev *xport;
+ int retval = 0;
+
+ if (data->enabled)
+ return 0;
+
+ if (rmi_dev->xport->ops->enable_device) {
+ retval = rmi_dev->xport->ops->enable_device(rmi_dev->xport);
+ if (retval)
+ return retval;
+ }
+
+ retval = rmi_driver_process_config_requests(rmi_dev);
+ if (retval < 0)
+ return retval;
+
+ xport = rmi_dev->xport;
+ if (xport->irq) {
+ retval = request_threaded_irq(xport->irq,
+ xport->hard_irq ? xport->hard_irq : NULL,
+ xport->irq_thread ?
+ xport->irq_thread : rmi_irq_thread,
+ xport->irq_flags,
+ dev_name(&rmi_dev->dev), xport);
+ if (retval)
+ return retval;
+ }
+
+ data->enabled = true;
+
+ return rmi_process_interrupt_requests(rmi_dev);
+}
+
+/**
+ * rmi_driver_set_input_params - set input device id and other data.
+ *
+ * @rmi_dev: Pointer to an RMI device
+ * @input: Pointer to input device
+ *
+ */
+static int rmi_driver_set_input_params(struct rmi_device *rmi_dev,
+ struct input_dev *input)
+{
+ input->name = SYNAPTICS_INPUT_DEVICE_NAME;
+ input->id.vendor = SYNAPTICS_VENDOR_ID;
+ input->id.bustype = BUS_RMI;
+ return 0;
+}
+
+static void rmi_driver_set_input_name(struct rmi_device *rmi_dev,
+ struct input_dev *input)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ char *device_name = rmi_f01_get_product_ID(data->f01_container);
+ char *name;
+
+ name = devm_kasprintf(&rmi_dev->dev, GFP_KERNEL,
+ "Synaptics %s", device_name);
+ if (!name)
+ return;
+
+ input->name = name;
+}
+
+static int rmi_driver_set_irq_bits(struct rmi_device *rmi_dev,
+ unsigned long *mask)
+{
+ int error = 0;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+
+ mutex_lock(&data->irq_mutex);
+ bitmap_or(data->new_irq_mask,
+ data->current_irq_mask, mask, data->irq_count);
+
+ error = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr + 1,
+ data->new_irq_mask, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to change enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ bitmap_copy(data->current_irq_mask, data->new_irq_mask,
+ data->num_of_irq_regs);
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return error;
+}
+
+static int rmi_driver_clear_irq_bits(struct rmi_device *rmi_dev,
+ unsigned long *mask)
+{
+ int error = 0;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct device *dev = &rmi_dev->dev;
+
+ mutex_lock(&data->irq_mutex);
+ bitmap_andnot(data->new_irq_mask,
+ data->current_irq_mask, mask, data->irq_count);
+
+ error = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr + 1,
+ data->new_irq_mask, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to change enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ bitmap_copy(data->current_irq_mask, data->new_irq_mask,
+ data->num_of_irq_regs);
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return error;
+}
+
+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 rmi_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_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr + 1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(&rmi_dev->dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ return error;
+ }
+
+ 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;
+
+ return 0;
+}
+
+int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
+ u16 pdt_address)
+{
+ u8 buf[RMI_PDT_ENTRY_SIZE];
+ int error;
+
+ error = rmi_read_block(rmi_dev, pdt_address, buf, RMI_PDT_ENTRY_SIZE);
+ if (error) {
+ dev_err(&rmi_dev->dev, "Read PDT entry at %#06x failed, code: %d.\n",
+ pdt_address, error);
+ return error;
+ }
+
+ entry->page_start = pdt_address & RMI4_PAGE_MASK;
+ entry->query_base_addr = buf[0];
+ entry->command_base_addr = buf[1];
+ entry->control_base_addr = buf[2];
+ entry->data_base_addr = buf[3];
+ entry->interrupt_source_count = buf[4] & RMI_PDT_INT_SOURCE_COUNT_MASK;
+ entry->function_version = (buf[4] & RMI_PDT_FUNCTION_VERSION_MASK) >> 5;
+ entry->function_number = buf[5];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_read_pdt_entry);
+
+static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
+ struct rmi_function_descriptor *fd)
+{
+ fd->query_base_addr = pdt->query_base_addr + pdt->page_start;
+ fd->command_base_addr = pdt->command_base_addr + pdt->page_start;
+ fd->control_base_addr = pdt->control_base_addr + pdt->page_start;
+ fd->data_base_addr = pdt->data_base_addr + pdt->page_start;
+ fd->function_number = pdt->function_number;
+ fd->interrupt_source_count = pdt->interrupt_source_count;
+ fd->function_version = pdt->function_version;
+}
+
+#define RMI_SCAN_CONTINUE 0
+#define RMI_SCAN_DONE 1
+
+static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
+ int page,
+ void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx,
+ const struct pdt_entry *entry))
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ struct pdt_entry pdt_entry;
+ 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;
+ u16 addr;
+ int error;
+ int retval;
+
+ for (addr = pdt_start; addr >= pdt_end; addr -= RMI_PDT_ENTRY_SIZE) {
+ error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, addr);
+ if (error)
+ return error;
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+
+ retval = callback(rmi_dev, ctx, &pdt_entry);
+ if (retval != RMI_SCAN_CONTINUE)
+ return retval;
+ }
+
+ return (data->f01_bootloader_mode || addr == pdt_start) ?
+ RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
+}
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
+ int (*callback)(struct rmi_device *rmi_dev,
+ void *ctx,
+ const struct pdt_entry *entry))
+{
+ int page;
+ int retval = RMI_SCAN_DONE;
+
+ for (page = 0; page <= RMI4_MAX_PAGE; page++) {
+ retval = rmi_scan_pdt_page(rmi_dev, page, ctx, callback);
+ if (retval != RMI_SCAN_CONTINUE)
+ break;
+ }
+
+ return retval < 0 ? retval : 0;
+}
+
+int rmi_read_register_desc(struct rmi_device *d, u16 addr,
+ struct rmi_register_descriptor *rdesc)
+{
+ int ret;
+ u8 size_presence_reg;
+ u8 buf[35];
+ int presense_offset = 1;
+ u8 *struct_buf;
+ int reg;
+ int offset = 0;
+ int map_offset = 0;
+ int i;
+ int b;
+
+ /*
+ * The first register of the register descriptor is the size of
+ * the register descriptor's presense register.
+ */
+ ret = rmi_read(d, addr, &size_presence_reg);
+ if (ret)
+ return ret;
+ ++addr;
+
+ if (size_presence_reg < 0 || size_presence_reg > 35)
+ return -EIO;
+
+ memset(buf, 0, sizeof(buf));
+
+ /*
+ * The presence register contains the size of the register structure
+ * and a bitmap which identified which packet registers are present
+ * for this particular register type (ie query, control, or data).
+ */
+ ret = rmi_read_block(d, addr, buf, size_presence_reg);
+ if (ret)
+ return ret;
+ ++addr;
+
+ if (buf[0] == 0) {
+ presense_offset = 3;
+ rdesc->struct_size = buf[1] | (buf[2] << 8);
+ } else {
+ rdesc->struct_size = buf[0];
+ }
+
+ for (i = presense_offset; i < size_presence_reg; i++) {
+ for (b = 0; b < 8; b++) {
+ if (buf[i] & (0x1 << b))
+ bitmap_set(rdesc->presense_map, map_offset, 1);
+ ++map_offset;
+ }
+ }
+
+ rdesc->num_registers = bitmap_weight(rdesc->presense_map,
+ RMI_REG_DESC_PRESENSE_BITS);
+
+ rdesc->registers = devm_kzalloc(&d->dev, rdesc->num_registers *
+ sizeof(struct rmi_register_desc_item),
+ GFP_KERNEL);
+ if (!rdesc->registers)
+ return -ENOMEM;
+
+ /*
+ * Allocate a temporary buffer to hold the register structure.
+ * I'm not using devm_kzalloc here since it will not be retained
+ * after exiting this function
+ */
+ struct_buf = kzalloc(rdesc->struct_size, GFP_KERNEL);
+ if (!struct_buf)
+ return -ENOMEM;
+
+ /*
+ * The register structure contains information about every packet
+ * register of this type. This includes the size of the packet
+ * register and a bitmap of all subpackets contained in the packet
+ * register.
+ */
+ ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size);
+ if (ret)
+ goto free_struct_buff;
+
+ reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS);
+ map_offset = 0;
+ for (i = 0; i < rdesc->num_registers; i++) {
+ struct rmi_register_desc_item *item = &rdesc->registers[i];
+ int reg_size = struct_buf[offset];
+
+ ++offset;
+ if (reg_size == 0) {
+ reg_size = struct_buf[offset] |
+ (struct_buf[offset + 1] << 8);
+ offset += 2;
+ }
+
+ if (reg_size == 0) {
+ reg_size = struct_buf[offset] |
+ (struct_buf[offset + 1] << 8) |
+ (struct_buf[offset + 2] << 16) |
+ (struct_buf[offset + 3] << 24);
+ offset += 4;
+ }
+
+ item->reg = reg;
+ item->reg_size = reg_size;
+
+ do {
+ for (b = 0; b < 7; b++) {
+ if (struct_buf[offset] & (0x1 << b))
+ bitmap_set(item->subpacket_map,
+ map_offset, 1);
+ ++map_offset;
+ }
+ } while (struct_buf[offset++] & 0x80);
+
+ item->num_subpackets = bitmap_weight(item->subpacket_map,
+ RMI_REG_DESC_SUBPACKET_BITS);
+
+ dev_dbg(&d->dev, "%s: reg: %d reg size: %ld subpackets: %d\n",
+ __func__, item->reg, item->reg_size,
+ item->num_subpackets);
+
+ reg = find_next_bit(rdesc->presense_map,
+ RMI_REG_DESC_PRESENSE_BITS, reg + 1);
+ }
+
+free_struct_buff:
+ kfree(struct_buf);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(rmi_read_register_desc);
+
+const struct rmi_register_desc_item *rmi_get_register_desc_item(
+ struct rmi_register_descriptor *rdesc, u16 reg)
+{
+ const struct rmi_register_desc_item *item;
+ int i;
+
+ for (i = 0; i < rdesc->num_registers; i++) {
+ item = &rdesc->registers[i];
+ if (item->reg == reg)
+ return item;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(rmi_get_register_desc_item);
+
+size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc)
+{
+ const struct rmi_register_desc_item *item;
+ int i;
+ size_t size = 0;
+
+ for (i = 0; i < rdesc->num_registers; i++) {
+ item = &rdesc->registers[i];
+ size += item->reg_size;
+ }
+ return size;
+}
+EXPORT_SYMBOL_GPL(rmi_register_desc_calc_size);
+
+/* Compute the register offset relative to the base address */
+int rmi_register_desc_calc_reg_offset(
+ struct rmi_register_descriptor *rdesc, u16 reg)
+{
+ const struct rmi_register_desc_item *item;
+ int offset = 0;
+ int i;
+
+ for (i = 0; i < rdesc->num_registers; i++) {
+ item = &rdesc->registers[i];
+ if (item->reg == reg)
+ return offset;
+ ++offset;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(rmi_register_desc_calc_reg_offset);
+
+bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
+ u8 subpacket)
+{
+ return find_next_bit(item->subpacket_map, RMI_REG_DESC_PRESENSE_BITS,
+ subpacket) == subpacket;
+}
+
+/* Indicates that flash programming is enabled (bootloader mode). */
+#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40))
+
+/*
+ * Given the PDT entry for F01, read the device status register to determine
+ * if we're stuck in bootloader mode or not.
+ *
+ */
+static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev,
+ const struct pdt_entry *pdt)
+{
+ int error;
+ u8 device_status;
+
+ error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start,
+ &device_status);
+ if (error) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read device status: %d.\n", error);
+ return error;
+ }
+
+ return RMI_F01_STATUS_BOOTLOADER(device_status);
+}
+
+static int rmi_count_irqs(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *pdt)
+{
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ int *irq_count = ctx;
+
+ *irq_count += pdt->interrupt_source_count;
+ if (pdt->function_number == 0x01) {
+ data->f01_bootloader_mode =
+ rmi_check_bootloader_mode(rmi_dev, pdt);
+ if (data->f01_bootloader_mode)
+ dev_warn(&rmi_dev->dev,
+ "WARNING: RMI4 device is in bootloader mode!\n");
+ }
+
+ return RMI_SCAN_CONTINUE;
+}
+
+static int rmi_initial_reset(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *pdt)
+{
+ int error;
+
+ if (pdt->function_number == 0x01) {
+ u16 cmd_addr = pdt->page_start + pdt->command_base_addr;
+ u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
+
+ if (rmi_dev->xport->ops->reset) {
+ error = rmi_dev->xport->ops->reset(rmi_dev->xport,
+ cmd_addr);
+ if (error)
+ return error;
+
+ return RMI_SCAN_DONE;
+ }
+
+ error = rmi_write_block(rmi_dev, cmd_addr, &cmd_buf, 1);
+ if (error) {
+ dev_err(&rmi_dev->dev,
+ "Initial reset failed. Code = %d.\n", error);
+ return error;
+ }
+
+ mdelay(pdata->reset_delay_ms ?: DEFAULT_RESET_DELAY_MS);
+
+ return RMI_SCAN_DONE;
+ }
+
+ /* F01 should always be on page 0. If we don't find it there, fail. */
+ return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV;
+}
+
+static int rmi_create_function(struct rmi_device *rmi_dev,
+ void *ctx, const struct pdt_entry *pdt)
+{
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev);
+ int *current_irq_count = ctx;
+ struct rmi_function *fn;
+ int i;
+ int error;
+
+ dev_dbg(dev, "Initializing F%02X.\n", pdt->function_number);
+
+ fn = kzalloc(sizeof(struct rmi_function) +
+ BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long),
+ GFP_KERNEL);
+ if (!fn) {
+ dev_err(dev, "Failed to allocate memory for F%02X\n",
+ pdt->function_number);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&fn->node);
+ rmi_driver_copy_pdt_to_fd(pdt, &fn->fd);
+
+ fn->rmi_dev = rmi_dev;
+
+ fn->num_of_irqs = pdt->interrupt_source_count;
+ fn->irq_pos = *current_irq_count;
+ *current_irq_count += fn->num_of_irqs;
+
+ for (i = 0; i < fn->num_of_irqs; i++)
+ set_bit(fn->irq_pos + i, fn->irq_mask);
+
+ error = rmi_register_function(fn);
+ if (error)
+ goto err_put_fn;
+
+ if (pdt->function_number == 0x01)
+ data->f01_container = fn;
+
+ list_add_tail(&fn->node, &data->function_list);
+
+ return RMI_SCAN_CONTINUE;
+
+err_put_fn:
+ put_device(&fn->dev);
+ return error;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rmi_driver_suspend(struct device *dev)
+{
+ struct rmi_driver_data *data;
+ int retval = 0;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+
+ mutex_lock(&data->suspend_mutex);
+
+ if (data->pre_suspend) {
+ retval = data->pre_suspend(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ disable_sensor(rmi_dev);
+
+ if (data->post_suspend)
+ retval = data->post_suspend(data->pm_data);
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+ struct rmi_driver_data *data;
+ int retval = 0;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ data = dev_get_drvdata(&rmi_dev->dev);
+ mutex_lock(&data->suspend_mutex);
+
+ if (data->pre_resume) {
+ retval = data->pre_resume(data->pm_data);
+ if (retval)
+ 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;
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend, rmi_driver_resume);
+
+static int rmi_driver_remove(struct device *dev)
+{
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ disable_sensor(rmi_dev);
+ rmi_free_function_list(rmi_dev);
+
+ return 0;
+}
+
+static int rmi_driver_probe(struct device *dev)
+{
+ struct rmi_driver *rmi_driver;
+ struct rmi_driver_data *data;
+ struct rmi_device_platform_data *pdata;
+ struct rmi_device *rmi_dev;
+ size_t size;
+ void *irq_memory;
+ int irq_count;
+ int retval;
+
+ dev_dbg(dev, "%s: Starting probe.\n", __func__);
+
+ if (!rmi_is_physical_device(dev)) {
+ dev_dbg(dev, "Not a physical device.\n");
+ return -ENODEV;
+ }
+
+ rmi_dev = to_rmi_device(dev);
+ rmi_driver = to_rmi_driver(dev->driver);
+ rmi_dev->driver = rmi_driver;
+
+ pdata = rmi_get_platform_data(rmi_dev);
+
+ data = devm_kzalloc(dev, sizeof(struct rmi_driver_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&data->function_list);
+ data->rmi_dev = rmi_dev;
+ dev_set_drvdata(&rmi_dev->dev, data);
+
+ /*
+ * Right before a warm boot, the sensor might be in some unusual state,
+ * such as F54 diagnostics, or F34 bootloader mode after a firmware
+ * or configuration update. In order to clear the sensor to a known
+ * state and/or apply any updates, we issue a initial reset to clear any
+ * previous settings and force it into normal operation.
+ *
+ * We have to do this before actually building the PDT because
+ * the reflash updates (if any) might cause various registers to move
+ * around.
+ *
+ * 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 accommodate 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.
+ */
+ retval = rmi_scan_pdt(rmi_dev, NULL, rmi_initial_reset);
+ if (retval < 0)
+ dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n");
+
+ retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &data->pdt_props);
+ 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 (code %d). Assuming 0x00.\n",
+ PDT_PROPERTIES_LOCATION, retval);
+ }
+
+ /*
+ * We need to count the IRQs and allocate their storage before scanning
+ * the PDT and creating the function entries, because adding a new
+ * function can trigger events that result in the IRQ related storage
+ * being accessed.
+ */
+ dev_dbg(dev, "Counting IRQs.\n");
+ irq_count = 0;
+ retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs);
+ if (retval < 0) {
+ dev_err(dev, "IRQ counting failed with code %d.\n", retval);
+ goto err;
+ }
+ data->irq_count = irq_count;
+ data->num_of_irq_regs = (data->irq_count + 7) / 8;
+
+ mutex_init(&data->irq_mutex);
+
+ size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
+ irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL);
+ if (!irq_memory) {
+ dev_err(dev, "Failed to allocate memory for irq masks.\n");
+ goto err;
+ }
+
+ data->irq_status = irq_memory + size * 0;
+ data->fn_irq_bits = irq_memory + size * 1;
+ data->current_irq_mask = irq_memory + size * 2;
+ data->new_irq_mask = irq_memory + size * 3;
+
+ if (rmi_dev->xport->input) {
+ /*
+ * The transport driver already has an input device.
+ * In some cases it is preferable to reuse the transport
+ * devices input device instead of creating a new one here.
+ * One example is some HID touchpads report "pass-through"
+ * button events are not reported by rmi registers.
+ */
+ data->input = rmi_dev->xport->input;
+ } else {
+ data->input = devm_input_allocate_device(dev);
+ if (!data->input) {
+ dev_err(dev, "%s: Failed to allocate input device.\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_destroy_functions;
+ }
+ rmi_driver_set_input_params(rmi_dev, data->input);
+ data->input->phys = devm_kasprintf(dev, GFP_KERNEL,
+ "%s/input0", dev_name(dev));
+ }
+
+ irq_count = 0;
+ dev_dbg(dev, "Creating functions.");
+ retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function);
+ if (retval < 0) {
+ dev_err(dev, "Function creation failed with code %d.\n",
+ retval);
+ goto err_destroy_functions;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "Missing F01 container!\n");
+ retval = -EINVAL;
+ goto err_destroy_functions;
+ }
+
+ 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_destroy_functions;
+ }
+
+#ifdef CONFIG_PM_SLEEP
+ 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);
+#endif
+
+ if (data->input) {
+ rmi_driver_set_input_name(rmi_dev, data->input);
+ if (!rmi_dev->xport->input) {
+ if (input_register_device(data->input)) {
+ dev_err(dev, "%s: Failed to register input device.\n",
+ __func__);
+ goto err_destroy_functions;
+ }
+ }
+ }
+
+ if (rmi_dev->xport->irq > 0)
+ if (!rmi_dev->xport->hard_irq)
+ rmi_dev->xport->irq_flags |= IRQF_ONESHOT;
+
+ if (data->f01_container->dev.driver)
+ /* Driver already bound, so enable ATTN now. */
+ return enable_sensor(rmi_dev);
+
+ return 0;
+
+err_destroy_functions:
+ rmi_free_function_list(rmi_dev);
+err:
+ return retval < 0 ? retval : 0;
+}
+
+static struct rmi_driver rmi_physical_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_physical",
+ .bus = &rmi_bus_type,
+ .pm = &rmi_driver_pm,
+ .probe = rmi_driver_probe,
+ .remove = rmi_driver_remove,
+ },
+ .irq_handler = rmi_driver_irq_handler,
+ .reset_handler = rmi_driver_reset_handler,
+ .clear_irq_bits = rmi_driver_clear_irq_bits,
+ .set_irq_bits = rmi_driver_set_irq_bits,
+ .set_input_params = rmi_driver_set_input_params,
+};
+
+bool rmi_is_physical_driver(struct device_driver *drv)
+{
+ return drv == &rmi_physical_driver.driver;
+}
+
+int __init rmi_register_physical_driver(void)
+{
+ int error;
+
+ error = driver_register(&rmi_physical_driver.driver);
+ if (error) {
+ pr_err("%s: driver register failed, code=%d.\n", __func__,
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+void __exit rmi_unregister_physical_driver(void)
+{
+ driver_unregister(&rmi_physical_driver.driver);
+}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
new file mode 100644
index 0000000..41dd041
--- /dev/null
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#include <linux/ctype.h>
+#include <linux/hrtimer.h>
+#include <linux/ktime.h>
+#include <linux/input.h>
+#include "rmi_bus.h"
+
+#define RMI_DRIVER_VERSION "2.0"
+
+#define SYNAPTICS_INPUT_DEVICE_NAME "Synaptics RMI4 Touch Sensor"
+#define SYNAPTICS_VENDOR_ID 0x06cb
+
+#define GROUP(_attrs) { \
+ .attrs = _attrs, \
+}
+
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+
+#define RMI_PDT_PROPS_HAS_BSR 0x02
+
+#define NAME_BUFFER_SIZE 256
+
+#define RMI_PDT_ENTRY_SIZE 6
+#define RMI_PDT_FUNCTION_VERSION_MASK 0x60
+#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07
+
+#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 {
+ u16 page_start;
+ u8 query_base_addr;
+ u8 command_base_addr;
+ u8 control_base_addr;
+ u8 data_base_addr;
+ u8 interrupt_source_count;
+ u8 function_version;
+ u8 function_number;
+};
+
+int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
+ u16 pdt_address);
+
+#define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE)
+#define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE)
+
+/* describes a single packet register */
+struct rmi_register_desc_item {
+ u16 reg;
+ unsigned long reg_size;
+ u8 num_subpackets;
+ unsigned long subpacket_map[BITS_TO_LONGS(
+ RMI_REG_DESC_SUBPACKET_BITS)];
+};
+
+/*
+ * describes the packet registers for a particular type
+ * (ie query, control, data)
+ */
+struct rmi_register_descriptor {
+ unsigned long struct_size;
+ unsigned long presense_map[BITS_TO_LONGS(RMI_REG_DESC_PRESENSE_BITS)];
+ u8 num_registers;
+ struct rmi_register_desc_item *registers;
+};
+
+int rmi_read_register_desc(struct rmi_device *d, u16 addr,
+ struct rmi_register_descriptor *rdesc);
+const struct rmi_register_desc_item *rmi_get_register_desc_item(
+ struct rmi_register_descriptor *rdesc, u16 reg);
+
+/*
+ * Calculate the total size of all of the registers described in the
+ * descriptor.
+ */
+size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc);
+int rmi_register_desc_calc_reg_offset(
+ struct rmi_register_descriptor *rdesc, u16 reg);
+bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item,
+ u8 subpacket);
+
+bool rmi_is_physical_driver(struct device_driver *);
+int rmi_register_physical_driver(void);
+void rmi_unregister_physical_driver(void);
+
+int rmi_register_f01_handler(void);
+void rmi_unregister_f01_handler(void);
+char *rmi_f01_get_product_ID(struct rmi_function *fn);
+#endif
diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
new file mode 100644
index 0000000..3978d6a
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/kconfig.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include "rmi_driver.h"
+
+#define RMI_PRODUCT_ID_LENGTH 10
+#define RMI_PRODUCT_INFO_LENGTH 2
+
+#define RMI_DATE_CODE_LENGTH 3
+
+#define PRODUCT_ID_OFFSET 0x10
+#define PRODUCT_INFO_OFFSET 0x1E
+
+
+/* Force a firmware reset of the sensor */
+#define RMI_F01_CMD_DEVICE_RESET 1
+
+/* Various F01_RMI_QueryX bits */
+
+#define RMI_F01_QRY1_CUSTOM_MAP (1 << 0)
+#define RMI_F01_QRY1_NON_COMPLIANT (1 << 1)
+#define RMI_F01_QRY1_HAS_LTS (1 << 2)
+#define RMI_F01_QRY1_HAS_SENSOR_ID (1 << 3)
+#define RMI_F01_QRY1_HAS_CHARGER_INP (1 << 4)
+#define RMI_F01_QRY1_HAS_ADJ_DOZE (1 << 5)
+#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF (1 << 6)
+#define RMI_F01_QRY1_HAS_PROPS_2 (1 << 7)
+
+#define RMI_F01_QRY5_YEAR_MASK 0x1f
+#define RMI_F01_QRY6_MONTH_MASK 0x0f
+#define RMI_F01_QRY7_DAY_MASK 0x1f
+
+#define RMI_F01_QRY2_PRODINFO_MASK 0x7f
+
+#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */
+
+struct f01_basic_properties {
+ u8 manufacturer_id;
+ bool has_lts;
+ bool has_adjustable_doze;
+ bool has_adjustable_doze_holdoff;
+ char dom[11]; /* YYYY/MM/DD + '\0' */
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+ u16 productinfo;
+};
+
+/* F01 device status bits */
+
+/* Most recent device status event */
+#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f)
+/* The device has lost its configuration for some reason. */
+#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80))
+
+/* Control register bits */
+
+/*
+ * Sleep mode controls power management on the device and affects all
+ * functions of the device.
+ */
+#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03
+
+#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
+
+/*
+ * 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.
+ */
+#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2)
+
+/*
+ * When this bit is set, the touch controller employs a noise-filtering
+ * algorithm designed for use with a connected battery charger.
+ */
+#define RMI_F01_CRTL0_CHARGER_BIT (1 << 5)
+
+/*
+ * 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.
+ */
+#define RMI_F01_CRTL0_REPORTRATE_BIT (1 << 6)
+
+/*
+ * Written by the host as an indicator that the device has been
+ * successfully configured.
+ */
+#define RMI_F01_CRTL0_CONFIGURED_BIT (1 << 7)
+
+/**
+ * @ctrl0 - see the bit definitions above.
+ * @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 {
+ u8 ctrl0;
+ u8 doze_interval;
+ u8 wakeup_threshold;
+ u8 doze_holdoff;
+};
+
+struct f01_data {
+ struct f01_basic_properties properties;
+
+ struct f01_device_control device_control;
+
+ u16 doze_interval_addr;
+ u16 wakeup_threshold_addr;
+ u16 doze_holdoff_addr;
+
+#ifdef CONFIG_PM_SLEEP
+ bool suspended;
+ bool old_nosleep;
+#endif
+
+ unsigned int num_of_irq_regs;
+};
+
+static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
+ u16 query_base_addr,
+ struct f01_basic_properties *props)
+{
+ u8 basic_query[RMI_F01_BASIC_QUERY_LEN];
+ int error;
+
+ error = rmi_read_block(rmi_dev, query_base_addr,
+ basic_query, sizeof(basic_query));
+ if (error) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read device query registers: %d\n", error);
+ return error;
+ }
+
+ /* Now parse what we got */
+ props->manufacturer_id = basic_query[0];
+
+ props->has_lts = basic_query[1] & RMI_F01_QRY1_HAS_LTS;
+ props->has_adjustable_doze =
+ basic_query[1] & RMI_F01_QRY1_HAS_ADJ_DOZE;
+ props->has_adjustable_doze_holdoff =
+ basic_query[1] & RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF;
+
+ snprintf(props->dom, sizeof(props->dom), "20%02d/%02d/%02d",
+ basic_query[5] & RMI_F01_QRY5_YEAR_MASK,
+ basic_query[6] & RMI_F01_QRY6_MONTH_MASK,
+ basic_query[7] & RMI_F01_QRY7_DAY_MASK);
+
+ memcpy(props->product_id, &basic_query[11],
+ RMI_PRODUCT_ID_LENGTH);
+ props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+
+ props->productinfo =
+ ((basic_query[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) |
+ (basic_query[3] & RMI_F01_QRY2_PRODINFO_MASK);
+
+ return 0;
+}
+
+char *rmi_f01_get_product_ID(struct rmi_function *fn)
+{
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+
+ return f01->properties.product_id;
+}
+
+#ifdef CONFIG_OF
+static int rmi_f01_of_probe(struct device *dev,
+ struct rmi_device_platform_data *pdata)
+{
+ int retval;
+
+ retval = rmi_of_property_read_u32(dev,
+ (u32 *)&pdata->power_management.nosleep,
+ "syna,nosleep-mode", 1);
+ if (retval)
+ return retval;
+
+ retval = rmi_of_property_read_u8(dev,
+ &pdata->power_management.wakeup_threshold,
+ "syna,wakeup-threshold", 1);
+ if (retval)
+ return retval;
+
+ retval = rmi_of_property_read_u8(dev,
+ &pdata->power_management.doze_holdoff,
+ "syna,doze-holdoff", 1);
+ if (retval)
+ return retval;
+
+ retval = rmi_of_property_read_u8(dev,
+ &pdata->power_management.doze_interval,
+ "syna,doze-interval", 1);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+#else
+static inline int rmi_f01_of_probe(struct device *dev,
+ struct rmi_device_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
+static int rmi_f01_probe(struct rmi_function *fn)
+{
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
+ struct f01_data *f01;
+ int error;
+ u16 ctrl_base_addr = fn->fd.control_base_addr;
+ u8 device_status;
+ u8 temp;
+ int retval;
+
+ if (fn->dev.of_node) {
+ retval = rmi_f01_of_probe(&fn->dev, pdata);
+ if (retval)
+ return retval;
+ }
+
+ f01 = devm_kzalloc(&fn->dev, sizeof(struct f01_data), GFP_KERNEL);
+ if (!f01)
+ return -ENOMEM;
+
+ f01->num_of_irq_regs = driver_data->num_of_irq_regs;
+
+ /*
+ * Set the configured bit and (optionally) other important stuff
+ * in the device control register.
+ */
+
+ error = rmi_read(rmi_dev, fn->fd.control_base_addr,
+ &f01->device_control.ctrl0);
+ if (error) {
+ dev_err(&fn->dev, "Failed to read F01 control: %d\n", error);
+ return error;
+ }
+
+ switch (pdata->power_management.nosleep) {
+ case RMI_F01_NOSLEEP_DEFAULT:
+ break;
+ case RMI_F01_NOSLEEP_OFF:
+ f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
+ break;
+ case RMI_F01_NOSLEEP_ON:
+ f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+ 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 ((f01->device_control.ctrl0 & RMI_F01_CTRL0_SLEEP_MODE_MASK) !=
+ RMI_SLEEP_MODE_NORMAL) {
+ dev_warn(&fn->dev,
+ "WARNING: Non-zero sleep mode found. Clearing...\n");
+ f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
+ }
+
+ f01->device_control.ctrl0 |= RMI_F01_CRTL0_CONFIGURED_BIT;
+
+ error = rmi_write(rmi_dev, fn->fd.control_base_addr,
+ f01->device_control.ctrl0);
+ if (error) {
+ dev_err(&fn->dev, "Failed to write F01 control: %d\n", error);
+ return error;
+ }
+
+ /* Dummy read in order to clear irqs */
+ error = rmi_read(rmi_dev, fn->fd.data_base_addr + 1, &temp);
+ if (error < 0) {
+ dev_err(&fn->dev, "Failed to read Interrupt Status.\n");
+ return error;
+ }
+
+ error = rmi_f01_read_properties(rmi_dev, fn->fd.query_base_addr,
+ &f01->properties);
+ if (error < 0) {
+ dev_err(&fn->dev, "Failed to read F01 properties.\n");
+ return error;
+ }
+
+ dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s\n",
+ f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown",
+ f01->properties.product_id);
+
+ /* Advance to interrupt control registers, then skip over them. */
+ ctrl_base_addr++;
+ ctrl_base_addr += f01->num_of_irq_regs;
+
+ /* read control register */
+ if (f01->properties.has_adjustable_doze) {
+ f01->doze_interval_addr = ctrl_base_addr;
+ ctrl_base_addr++;
+
+ if (pdata->power_management.doze_interval) {
+ f01->device_control.doze_interval =
+ pdata->power_management.doze_interval;
+ error = rmi_write(rmi_dev, f01->doze_interval_addr,
+ f01->device_control.doze_interval);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to configure F01 doze interval register: %d\n",
+ error);
+ return error;
+ }
+ } else {
+ error = rmi_read(rmi_dev, f01->doze_interval_addr,
+ &f01->device_control.doze_interval);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to read F01 doze interval register: %d\n",
+ error);
+ return error;
+ }
+ }
+
+ f01->wakeup_threshold_addr = ctrl_base_addr;
+ ctrl_base_addr++;
+
+ if (pdata->power_management.wakeup_threshold) {
+ f01->device_control.wakeup_threshold =
+ pdata->power_management.wakeup_threshold;
+ error = rmi_write(rmi_dev, f01->wakeup_threshold_addr,
+ f01->device_control.wakeup_threshold);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to configure F01 wakeup threshold register: %d\n",
+ error);
+ return error;
+ }
+ } else {
+ error = rmi_read(rmi_dev, f01->wakeup_threshold_addr,
+ &f01->device_control.wakeup_threshold);
+ if (error < 0) {
+ dev_err(&fn->dev,
+ "Failed to read F01 wakeup threshold register: %d\n",
+ error);
+ return error;
+ }
+ }
+ }
+
+ if (f01->properties.has_lts)
+ ctrl_base_addr++;
+
+ if (f01->properties.has_adjustable_doze_holdoff) {
+ f01->doze_holdoff_addr = ctrl_base_addr;
+ ctrl_base_addr++;
+
+ if (pdata->power_management.doze_holdoff) {
+ f01->device_control.doze_holdoff =
+ pdata->power_management.doze_holdoff;
+ error = rmi_write(rmi_dev, f01->doze_holdoff_addr,
+ f01->device_control.doze_holdoff);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to configure F01 doze holdoff register: %d\n",
+ error);
+ return error;
+ }
+ } else {
+ error = rmi_read(rmi_dev, f01->doze_holdoff_addr,
+ &f01->device_control.doze_holdoff);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to read F01 doze holdoff register: %d\n",
+ error);
+ return error;
+ }
+ }
+ }
+
+ error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status);
+ if (error < 0) {
+ dev_err(&fn->dev,
+ "Failed to read device status: %d\n", error);
+ return error;
+ }
+
+ if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
+ dev_err(&fn->dev,
+ "Device was reset during configuration process, status: %#02x!\n",
+ RMI_F01_STATUS_CODE(device_status));
+ return -EINVAL;
+ }
+
+ dev_set_drvdata(&fn->dev, f01);
+
+ return 0;
+}
+
+static int rmi_f01_config(struct rmi_function *fn)
+{
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+ int error;
+
+ error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr,
+ f01->device_control.ctrl0);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to write device_control register: %d\n", error);
+ return error;
+ }
+
+ if (f01->properties.has_adjustable_doze) {
+ error = rmi_write(fn->rmi_dev, f01->doze_interval_addr,
+ f01->device_control.doze_interval);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to write doze interval: %d\n", error);
+ return error;
+ }
+
+ error = rmi_write_block(fn->rmi_dev,
+ f01->wakeup_threshold_addr,
+ &f01->device_control.wakeup_threshold,
+ sizeof(u8));
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to write wakeup threshold: %d\n",
+ error);
+ return error;
+ }
+ }
+
+ if (f01->properties.has_adjustable_doze_holdoff) {
+ error = rmi_write(fn->rmi_dev, f01->doze_holdoff_addr,
+ f01->device_control.doze_holdoff);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to write doze holdoff: %d\n", error);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rmi_f01_suspend(struct device *dev)
+{
+ struct rmi_function *fn = to_rmi_function(dev);
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+ int error;
+
+ f01->old_nosleep =
+ f01->device_control.ctrl0 & RMI_F01_CRTL0_NOSLEEP_BIT;
+ f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT;
+
+ f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
+ if (device_may_wakeup(fn->rmi_dev->xport->dev))
+ f01->device_control.ctrl0 |= RMI_SLEEP_MODE_RESERVED1;
+ else
+ f01->device_control.ctrl0 |= RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+ error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr,
+ f01->device_control.ctrl0);
+ if (error) {
+ dev_err(&fn->dev, "Failed to write sleep mode: %d.\n", error);
+ if (f01->old_nosleep)
+ f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+ f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
+ f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
+ return error;
+ }
+
+ return 0;
+}
+
+static int rmi_f01_resume(struct device *dev)
+{
+ struct rmi_function *fn = to_rmi_function(dev);
+ struct f01_data *f01 = dev_get_drvdata(&fn->dev);
+ int error;
+
+ if (f01->old_nosleep)
+ f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT;
+
+ f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK;
+ f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL;
+
+ error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr,
+ f01->device_control.ctrl0);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to restore normal operation: %d.\n", error);
+ return error;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(rmi_f01_pm_ops, rmi_f01_suspend, rmi_f01_resume);
+
+static int rmi_f01_attention(struct rmi_function *fn,
+ unsigned long *irq_bits)
+{
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ int error;
+ u8 device_status;
+
+ error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status);
+ if (error) {
+ dev_err(&fn->dev,
+ "Failed to read device status: %d.\n", error);
+ return error;
+ }
+
+ if (RMI_F01_STATUS_UNCONFIGURED(device_status)) {
+ dev_warn(&fn->dev, "Device reset detected.\n");
+ error = rmi_dev->driver->reset_handler(rmi_dev);
+ if (error) {
+ dev_err(&fn->dev, "Device reset failed: %d\n", error);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+static struct rmi_function_handler rmi_f01_handler = {
+ .driver = {
+ .name = "rmi_f01",
+ .pm = &rmi_f01_pm_ops,
+ /*
+ * Do not allow user unbinding F01 as it is critical
+ * function.
+ */
+ .suppress_bind_attrs = true,
+ },
+ .func = 0x01,
+ .probe = rmi_f01_probe,
+ .config = rmi_f01_config,
+ .attention = rmi_f01_attention,
+};
+
+int __init rmi_register_f01_handler(void)
+{
+ return rmi_register_function_handler(&rmi_f01_handler);
+}
+
+void rmi_unregister_f01_handler(void)
+{
+ rmi_unregister_function_handler(&rmi_f01_handler);
+}
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
new file mode 100644
index 0000000..c0429be
--- /dev/null
+++ b/include/linux/rmi.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _RMI_H
+#define _RMI_H
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#define NAME_BUFFER_SIZE 256
+
+/**
+ * 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_device_platform_data - system specific configuration info.
+ *
+ * @irq_flags - this is used to specify intrerrupt type flags.
+ *
+ * @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.
+ *
+ *
+ * @pre_suspend - this will be called before any other suspend operations are
+ * done.
+ * @power_management - overrides default touch sensor doze mode settings (see
+ * above)
+ *
+ * @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 {
+ int irq_flags;
+
+ int reset_delay_ms;
+
+ /* function handler pdata */
+ struct rmi_f01_power_management power_management;
+
+#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_device;
+
+/**
+ * struct rmi_transport_dev - represent an RMI transport device
+ *
+ * @dev: Pointer to the communication device, e.g. i2c or spi
+ * @rmi_dev: Pointer to the RMI device
+ * @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
+ * @proto_name: name of the transport protocol (SPI, i2c, etc)
+ * @ops: pointer to transport operations implementation
+ *
+ * The RMI transport device implements the glue between different communication
+ * buses such as I2C and SPI.
+ *
+ */
+struct rmi_transport_dev {
+ struct device *dev;
+ struct rmi_device *rmi_dev;
+
+ int irq;
+ int irq_flags;
+
+ irqreturn_t (*irq_thread)(int irq, void *p);
+ irqreturn_t (*hard_irq)(int irq, void *p);
+
+ const char *proto_name;
+ const struct rmi_transport_ops *ops;
+
+ struct rmi_device_platform_data pdata;
+
+ struct input_dev *input;
+
+ void *attn_data;
+ int attn_size;
+};
+
+/**
+ * struct rmi_transport_ops - defines transport protocol operations.
+ *
+ * @write_block: Writing a block of data to the specified address
+ * @read_block: Read a block of data from the specified address.
+ */
+struct rmi_transport_ops {
+ int (*write_block)(struct rmi_transport_dev *xport, u16 addr,
+ const void *buf, size_t len);
+ int (*read_block)(struct rmi_transport_dev *xport, u16 addr,
+ void *buf, size_t len);
+
+ int (*enable_device)(struct rmi_transport_dev *xport);
+ void (*disable_device)(struct rmi_transport_dev *xport);
+ int (*reset)(struct rmi_transport_dev *xport, u16 reset_addr);
+};
+
+/**
+ * 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.
+ * @clear_irq_bits: Clear the specified bits in the current interrupt mask.
+ * @set_irq_bist: Set the specified bits in the current interrupt mask.
+ * @store_productid: Callback for cache product id from function 01
+ * @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);
+ int (*clear_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask);
+ int (*set_irq_bits)(struct rmi_device *rmi_dev, unsigned long *mask);
+ int (*store_productid)(struct rmi_device *rmi_dev);
+ int (*set_input_params)(struct rmi_device *rmi_dev,
+ struct input_dev *input);
+ void *data;
+};
+
+/**
+ * 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
+ * @xport: Pointer to the transport interface
+ *
+ */
+struct rmi_device {
+ struct device dev;
+ int number;
+
+ struct rmi_driver *driver;
+ struct rmi_transport_dev *xport;
+
+};
+
+struct rmi_driver_data {
+ struct list_head function_list;
+
+ struct rmi_device *rmi_dev;
+
+ struct rmi_function *f01_container;
+ bool f01_bootloader_mode;
+
+ u32 attn_count;
+ bool polling;
+ int irq;
+ int irq_flags;
+ int num_of_irq_regs;
+ int irq_count;
+ unsigned long *irq_status;
+ unsigned long *fn_irq_bits;
+ unsigned long *current_irq_mask;
+ unsigned long *new_irq_mask;
+ struct mutex irq_mutex;
+ struct input_dev *input;
+
+ u8 pdt_props;
+ u8 bsr;
+
+ bool enabled;
+#ifdef CONFIG_PM_SLEEP
+ 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
+
+ void *data;
+};
+
+int rmi_register_transport_device(struct rmi_transport_dev *xport);
+void rmi_unregister_transport_device(struct rmi_transport_dev *xport);
+int rmi_process_interrupt_requests(struct rmi_device *rmi_dev);
+
+#endif
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 731417c..ae3f640 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -972,6 +972,7 @@ struct input_keymap_entry {
#define BUS_GSC 0x1A
#define BUS_ATARI 0x1B
#define BUS_SPI 0x1C
+#define BUS_RMI 0x1D
/*
* MT_TOOL types
--
2.5.0
Add the transport driver for devices using RMI4 over I2C.
Signed-off-by: Andrew Duggan <[email protected]>
Signed-off-by: Christopher Heiny <[email protected]>
---
drivers/input/rmi4/Kconfig | 11 ++
drivers/input/rmi4/Makefile | 3 +
drivers/input/rmi4/rmi_i2c.c | 260 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 274 insertions(+)
create mode 100644 drivers/input/rmi4/rmi_i2c.c
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index 45a9ff4..e1c07fc 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -24,3 +24,14 @@ config RMI4_DEBUG
if you are actively developing/debugging RMI4 features.
If unsure, say N.
+
+config RMI4_I2C
+ tristate "RMI4 I2C Support"
+ depends on RMI4_CORE && 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.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index b33768f..0bb318d 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -1,4 +1,7 @@
obj-$(CONFIG_RMI4_CORE) += rmi_core.o
rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
+# Transports
+obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
+
ccflags-$(CONFIG_RMI4_DEBUG) += -DDEBUG
diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
new file mode 100644
index 0000000..c6e680d
--- /dev/null
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define BUFFER_SIZE_INCREMENT 32
+
+/**
+ * struct rmi_i2c_xport - stores information for i2c communication
+ *
+ * @xport: The transport interface structure
+ *
+ * @page_mutex: Locks current page to avoid changing pages in unexpected ways.
+ * @page: Keeps track of the current virtual page
+ *
+ * @tx_buf: Buffer used for transmitting data to the sensor over i2c.
+ * @tx_buf_size: Size of the buffer
+ */
+struct rmi_i2c_xport {
+ struct rmi_transport_dev xport;
+ struct i2c_client *client;
+
+ struct mutex page_mutex;
+ int page;
+
+ u8 *tx_buf;
+ size_t tx_buf_size;
+};
+
+#define RMI_PAGE_SELECT_REGISTER 0xff
+#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
+
+/*
+ * rmi_set_page - Set RMI page
+ * @xport: The pointer to the rmi_transport_dev struct
+ * @page: The new page address.
+ *
+ * RMI devices have 16-bit addressing, but some of the transport
+ * 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_i2c_xport *rmi_i2c, u8 page)
+{
+ struct i2c_client *client = rmi_i2c->client;
+ u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
+ int retval;
+
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ dev_err(&client->dev,
+ "%s: set page failed: %d.", __func__, retval);
+ return (retval < 0) ? retval : -EIO;
+ }
+
+ rmi_i2c->page = page;
+ return 0;
+}
+
+static int rmi_i2c_write_block(struct rmi_transport_dev *xport, u16 addr,
+ const void *buf, size_t len)
+{
+ struct rmi_i2c_xport *rmi_i2c =
+ container_of(xport, struct rmi_i2c_xport, xport);
+ struct i2c_client *client = rmi_i2c->client;
+ size_t tx_size = len + 1;
+ int retval;
+
+ mutex_lock(&rmi_i2c->page_mutex);
+
+ if (!rmi_i2c->tx_buf || rmi_i2c->tx_buf_size < tx_size) {
+ if (rmi_i2c->tx_buf)
+ devm_kfree(&client->dev, rmi_i2c->tx_buf);
+ rmi_i2c->tx_buf_size = tx_size + BUFFER_SIZE_INCREMENT;
+ rmi_i2c->tx_buf = devm_kzalloc(&client->dev,
+ rmi_i2c->tx_buf_size,
+ GFP_KERNEL);
+ if (!rmi_i2c->tx_buf) {
+ rmi_i2c->tx_buf_size = 0;
+ retval = -ENOMEM;
+ goto exit;
+ }
+ }
+
+ rmi_i2c->tx_buf[0] = addr & 0xff;
+ memcpy(rmi_i2c->tx_buf + 1, buf, len);
+
+ if (RMI_I2C_PAGE(addr) != rmi_i2c->page) {
+ retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr));
+ if (retval)
+ goto exit;
+ }
+
+ retval = i2c_master_send(client, rmi_i2c->tx_buf, tx_size);
+ if (retval == tx_size)
+ retval = 0;
+ else if (retval >= 0)
+ retval = -EIO;
+
+exit:
+ dev_dbg(&client->dev,
+ "write %zd bytes at %#06x: %d (%*ph)\n",
+ len, addr, retval, (int)len, buf);
+
+ mutex_unlock(&rmi_i2c->page_mutex);
+ return retval;
+}
+
+static int rmi_i2c_read_block(struct rmi_transport_dev *xport, u16 addr,
+ void *buf, size_t len)
+{
+ struct rmi_i2c_xport *rmi_i2c =
+ container_of(xport, struct rmi_i2c_xport, xport);
+ struct i2c_client *client = rmi_i2c->client;
+ u8 addr_offset = addr & 0xff;
+ int retval;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .len = sizeof(addr_offset),
+ .buf = &addr_offset,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ },
+ };
+
+ mutex_lock(&rmi_i2c->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != rmi_i2c->page) {
+ retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr));
+ if (retval)
+ goto exit;
+ }
+
+ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (retval == ARRAY_SIZE(msgs))
+ retval = 0; /* success */
+ else if (retval >= 0)
+ retval = -EIO;
+
+exit:
+ dev_dbg(&client->dev,
+ "read %zd bytes at %#06x: %d (%*ph)\n",
+ len, addr, retval, (int)len, buf);
+
+ mutex_unlock(&rmi_i2c->page_mutex);
+ return retval;
+}
+
+static const struct rmi_transport_ops rmi_i2c_ops = {
+ .write_block = rmi_i2c_write_block,
+ .read_block = rmi_i2c_read_block,
+};
+
+static int rmi_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rmi_device_platform_data *pdata;
+ struct rmi_device_platform_data *client_pdata =
+ dev_get_platdata(&client->dev);
+ struct rmi_i2c_xport *rmi_i2c;
+ int retval;
+
+ rmi_i2c = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_xport),
+ GFP_KERNEL);
+ if (!rmi_i2c)
+ return -ENOMEM;
+
+ pdata = &rmi_i2c->xport.pdata;
+
+ if (client_pdata)
+ *pdata = *client_pdata;
+
+ dev_dbg(&client->dev, "Probing %s.\n", dev_name(&client->dev));
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev,
+ "adapter does not support required functionality.\n");
+ return -ENODEV;
+ }
+
+ rmi_i2c->client = client;
+ mutex_init(&rmi_i2c->page_mutex);
+
+ rmi_i2c->xport.dev = &client->dev;
+ rmi_i2c->xport.proto_name = "i2c";
+ rmi_i2c->xport.ops = &rmi_i2c_ops;
+ rmi_i2c->xport.irq = client->irq;
+ rmi_i2c->xport.irq_flags = pdata->irq_flags;
+
+ /*
+ * 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_i2c, 0);
+ if (retval) {
+ dev_err(&client->dev, "Failed to set page select to 0.\n");
+ return retval;
+ }
+
+ retval = rmi_register_transport_device(&rmi_i2c->xport);
+ if (retval) {
+ dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n",
+ client->addr);
+ return retval;
+ }
+
+ i2c_set_clientdata(client, rmi_i2c);
+
+ dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n",
+ client->addr);
+ return 0;
+}
+
+static int rmi_i2c_remove(struct i2c_client *client)
+{
+ struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client);
+
+ rmi_unregister_transport_device(&rmi_i2c->xport);
+
+ 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 = rmi_i2c_remove,
+};
+
+module_i2c_driver(rmi_i2c_driver);
+
+MODULE_AUTHOR("Christopher Heiny <[email protected]>");
+MODULE_DESCRIPTION("RMI I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(RMI_DRIVER_VERSION);
--
2.5.0
Add devicetree binding for I2C devices and add bindings for optional
parameters in the function drivers. Parameters for function drivers are
defined in child nodes for each of the functions.
Signed-off-by: Andrew Duggan <[email protected]>
---
.../devicetree/bindings/input/rmi4/rmi_f01.txt | 39 ++++++++++++
.../devicetree/bindings/input/rmi4/rmi_i2c.txt | 53 ++++++++++++++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
drivers/input/rmi4/rmi_bus.c | 71 ++++++++++++++++++++++
drivers/input/rmi4/rmi_driver.c | 28 +++++++++
drivers/input/rmi4/rmi_i2c.c | 12 +++-
6 files changed, 203 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
new file mode 100644
index 0000000..df34dd5
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
@@ -0,0 +1,39 @@
+Synaptics RMI4 F01 Device Binding
+
+The Synaptics RMI4 core is able to support RMI4 devices using differnet
+transports and differnet functions. This file describes the device tree
+bindings for devices which contain Function 1. Complete documentation
+for transports and other functions can be found in:
+Documentation/devicetree/bindings/input/rmi4.
+
+Additional documentation for F01 can be found at:
+http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
+
+Optional Properties:
+- syna,nosleep-mode: If set the device will run at full power without sleeping.
+ nosleep has 3 modes, 0 will not change the default
+ setting, 1 will disable nosleep (allow sleeping),
+ and 2 will enable nosleep (disabling sleep).
+- syna,wakeup-threshold: Defines the amplitude of the disturbance to the
+ background capacitance that will cause the
+ device to wake from dozing.
+- syna,doze-holdoff: The delay to wait after the last finger lift and the
+ first doze cycle (in 0.1 second units).
+- syna,doze-interval: The time period that the device sleeps between finger
+ activity (in 10 ms units).
+
+
+Example of a RMI4 I2C device with F01:
+ Example:
+ &i2c1 {
+ rmi-i2c-dev@2c {
+ compatible = "syna,rmi-i2c";
+
+ ...
+
+ rmi-f01@1 {
+ reg = <0x1>;
+ syna,nosleep-mode = <1>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
new file mode 100644
index 0000000..0f4a8e1
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
@@ -0,0 +1,53 @@
+Synaptics RMI4 I2C Device Binding
+
+The Synaptics RMI4 core is able to support RMI4 devices using differnet
+transports and differnet functions. This file describes the device tree
+bindings for devices using the I2C tranport driver. Complete documentation
+for other transports and functions cen be found ini
+Documentation/devicetree/bindings/input/rmi4.
+
+Required Properties:
+- compatible: syna,rmi-i2c
+- reg: I2C address
+- #address-cells: Set to 1 to indicate that the function child nodes
+ consist of only on uint32 value.
+- #size-cells: Set to 0 to indicate that the function child nodes do not
+ have a size property.
+
+Optional Properties:
+- interrupts: interrupt which the rmi device is connected to.
+- interrupt-parent: The interrupt controller.
+See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
+
+- syna,reset-delay-ms: The number of milliseconds to wait after resetting the
+ device.
+
+Function Parameters:
+Parameters specific to RMI functions are contained in child nodes of the rmi device
+ node. Documentation for the parameters of each function can be found in:
+Documentation/devicetree/bindings/input/rmi4/rmi_f*.txt.
+
+
+
+Example:
+ &i2c1 {
+ rmi-i2c-dev@2c {
+ compatible = "syna,rmi-i2c";
+ reg = <0x2c>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ interrupt-parent = <&gpio>;
+ interrupts = <4 2>;
+
+ rmi-f01@1 {
+ reg = <0x1>;
+ syna,nosleep-mode = <1>;
+ };
+
+ rmi-f11@11 {
+ reg = <0x11>;
+ syna,flip-y;
+ syna,sensor-type = <2>;
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 82d2ac9..fd075b8 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -212,6 +212,7 @@ sprd Spreadtrum Communications Inc.
st STMicroelectronics
ste ST-Ericsson
stericsson ST-Ericsson
+syna Synaptics Inc.
synology Synology, Inc.
tbs TBS Technologies
tcl Toby Churchill Ltd.
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index f3e1f2c..aa12f4a 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -130,6 +130,21 @@ static int rmi_function_match(struct device *dev, struct device_driver *drv)
return fn->fd.function_number == handler->func;
}
+#ifdef CONFIG_OF
+static void rmi_function_of_probe(struct rmi_function *fn)
+{
+ char of_name[8];
+
+ snprintf(of_name, sizeof(of_name), "rmi-f%02x",
+ fn->fd.function_number);
+ fn->dev.of_node = of_find_node_by_name(
+ fn->rmi_dev->xport->dev->of_node, of_name);
+}
+#else
+static inline void rmi_function_of_probe(struct rmi_function *fn)
+{}
+#endif
+
static int rmi_function_probe(struct device *dev)
{
struct rmi_function *fn = to_rmi_function(dev);
@@ -137,6 +152,8 @@ static int rmi_function_probe(struct device *dev)
to_rmi_function_handler(dev->driver);
int error;
+ rmi_function_of_probe(fn);
+
if (handler->probe) {
error = handler->probe(fn);
return error;
@@ -263,6 +280,60 @@ struct bus_type rmi_bus_type = {
.name = "rmi",
};
+int rmi_of_property_read_u32(struct device *dev, u32 *result,
+ const char *prop, bool optional)
+{
+ int retval;
+ u32 val = 0;
+
+ retval = of_property_read_u32(dev->of_node, prop, &val);
+ if (retval && (!optional && retval == -EINVAL)) {
+ dev_err(dev, "Failed to get %s value: %d\n",
+ prop, retval);
+ return retval;
+ }
+ *result = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_of_property_read_u32);
+
+int rmi_of_property_read_u16(struct device *dev, u16 *result,
+ const char *prop, bool optional)
+{
+ int retval;
+ u16 val = 0;
+
+ retval = of_property_read_u16(dev->of_node, prop, &val);
+ if (retval && (!optional && retval == -EINVAL)) {
+ dev_err(dev, "Failed to get %s value: %d\n",
+ prop, retval);
+ return retval;
+ }
+ *result = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_of_property_read_u16);
+
+int rmi_of_property_read_u8(struct device *dev, u8 *result,
+ const char *prop, bool optional)
+{
+ int retval;
+ u8 val = 0;
+
+ retval = of_property_read_u8(dev->of_node, prop, &val);
+ if (retval && (!optional && retval == -EINVAL)) {
+ dev_err(dev, "Failed to get %s value: %d\n",
+ prop, retval);
+ return retval;
+ }
+ *result = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_of_property_read_u8);
+
static int __init rmi_bus_init(void)
{
int error;
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 0d74d26..3a4697f 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -20,6 +20,7 @@
#include <linux/kconfig.h>
#include <linux/pm.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <uapi/linux/input.h>
#include <linux/rmi.h>
#include "rmi_bus.h"
@@ -864,6 +865,27 @@ static int rmi_driver_remove(struct device *dev)
return 0;
}
+#ifdef CONFIG_OF
+static int rmi_driver_of_probe(struct device *dev,
+ struct rmi_device_platform_data *pdata)
+{
+ int retval;
+
+ retval = rmi_of_property_read_u32(dev, &pdata->reset_delay_ms,
+ "syna,reset-delay-ms", 1);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+#else
+static inline int rmi_driver_of_probe(struct device *dev,
+ struct rmi_device_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+
static int rmi_driver_probe(struct device *dev)
{
struct rmi_driver *rmi_driver;
@@ -888,6 +910,12 @@ static int rmi_driver_probe(struct device *dev)
pdata = rmi_get_platform_data(rmi_dev);
+ if (rmi_dev->xport->dev->of_node) {
+ retval = rmi_driver_of_probe(rmi_dev->xport->dev, pdata);
+ if (retval)
+ return retval;
+ }
+
data = devm_kzalloc(dev, sizeof(struct rmi_driver_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c
index c6e680d..32f7cae 100644
--- a/drivers/input/rmi4/rmi_i2c.c
+++ b/drivers/input/rmi4/rmi_i2c.c
@@ -9,6 +9,7 @@
#include <linux/i2c.h>
#include <linux/rmi.h>
+#include <linux/of.h>
#include "rmi_driver.h"
#define BUFFER_SIZE_INCREMENT 32
@@ -168,6 +169,14 @@ static const struct rmi_transport_ops rmi_i2c_ops = {
.read_block = rmi_i2c_read_block,
};
+#ifdef CONFIG_OF
+static const struct of_device_id rmi_i2c_of_match[] = {
+ { .compatible = "syna,rmi-i2c" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rmi_i2c_of_match);
+#endif
+
static int rmi_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -184,7 +193,7 @@ static int rmi_i2c_probe(struct i2c_client *client,
pdata = &rmi_i2c->xport.pdata;
- if (client_pdata)
+ if (!client->dev.of_node && client_pdata)
*pdata = *client_pdata;
dev_dbg(&client->dev, "Probing %s.\n", dev_name(&client->dev));
@@ -246,6 +255,7 @@ static struct i2c_driver rmi_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "rmi_i2c",
+ .of_match_table = of_match_ptr(rmi_i2c_of_match),
},
.id_table = rmi_id,
.probe = rmi_i2c_probe,
--
2.5.0
RMI4 currently defines two functions for reporting data for 2D sensors
(F11 and F12). This patch adds the common functionality which is shared
by devices with 2D reporting along with implementing functionality for
F11.
Signed-off-by: Andrew Duggan <[email protected]>
Signed-off-by: Christopher Heiny <[email protected]>
---
drivers/input/rmi4/Kconfig | 29 +
drivers/input/rmi4/Makefile | 5 +
drivers/input/rmi4/rmi_2d_sensor.c | 220 ++++++
drivers/input/rmi4/rmi_2d_sensor.h | 85 +++
drivers/input/rmi4/rmi_bus.c | 13 +-
drivers/input/rmi4/rmi_driver.h | 8 +
drivers/input/rmi4/rmi_f11.c | 1349 ++++++++++++++++++++++++++++++++++++
include/linux/rmi.h | 83 +++
8 files changed, 1790 insertions(+), 2 deletions(-)
create mode 100644 drivers/input/rmi4/rmi_2d_sensor.c
create mode 100644 drivers/input/rmi4/rmi_2d_sensor.h
create mode 100644 drivers/input/rmi4/rmi_f11.c
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index e1c07fc..152310a 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -35,3 +35,32 @@ config RMI4_I2C
If unsure, say Y.
This feature is not currently available as a loadable module.
+
+config RMI4_2D_SENSOR
+ bool
+ depends on RMI4_CORE
+
+config RMI4_F11
+ bool "RMI4 Function 11 (2D pointing)"
+ select RMI4_2D_SENSOR
+ 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.
+
+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.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 0bb318d..5ff604f 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -1,6 +1,11 @@
obj-$(CONFIG_RMI4_CORE) += rmi_core.o
rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o
+rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o
+
+# Function drivers
+rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
+
# Transports
obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c
new file mode 100644
index 0000000..e3c7b7f
--- /dev/null
+++ b/drivers/input/rmi4/rmi_2d_sensor.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+#include "rmi_2d_sensor.h"
+
+#define RMI_2D_REL_POS_MIN -128
+#define RMI_2D_REL_POS_MAX 127
+
+/* maximum ABS_MT_POSITION displacement (in mm) */
+#define DMAX 10
+
+void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor,
+ struct rmi_2d_sensor_abs_object *obj,
+ int slot)
+{
+ struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align;
+
+ /* we keep the previous values if the finger is released */
+ if (obj->type == RMI_2D_OBJECT_NONE)
+ return;
+
+ if (axis_align->swap_axes)
+ swap(obj->x, obj->y);
+
+ if (axis_align->flip_x)
+ obj->x = sensor->max_x - obj->x;
+
+ if (axis_align->flip_y)
+ obj->y = sensor->max_y - obj->y;
+
+ /*
+ * 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 applied before clipping occurs,
+ * or we could get funny values that are outside of
+ * clipping boundaries.
+ */
+ obj->x += axis_align->offset_x;
+ obj->y += axis_align->offset_y;
+
+ obj->x = max(axis_align->clip_x_low, obj->x);
+ obj->y = max(axis_align->clip_y_low, obj->y);
+
+ if (axis_align->clip_x_high)
+ obj->x = min(sensor->max_x, obj->x);
+
+ if (axis_align->clip_y_high)
+ obj->y = min(sensor->max_y, obj->y);
+
+ sensor->tracking_pos[slot].x = obj->x;
+ sensor->tracking_pos[slot].y = obj->y;
+}
+EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_process);
+
+void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor,
+ struct rmi_2d_sensor_abs_object *obj,
+ int slot)
+{
+ struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align;
+ struct input_dev *input = sensor->input;
+ int wide, major, minor;
+
+ if (sensor->kernel_tracking)
+ input_mt_slot(input, sensor->tracking_slots[slot]);
+ else
+ input_mt_slot(input, slot);
+
+ input_mt_report_slot_state(input, obj->mt_tool,
+ obj->type != RMI_2D_OBJECT_NONE);
+
+ if (obj->type != RMI_2D_OBJECT_NONE) {
+ obj->x = sensor->tracking_pos[slot].x;
+ obj->y = sensor->tracking_pos[slot].y;
+
+ if (axis_align->swap_axes)
+ swap(obj->wx, obj->wy);
+
+ wide = (obj->wx > obj->wy);
+ major = max(obj->wx, obj->wy);
+ minor = min(obj->wx, obj->wy);
+
+ if (obj->type == RMI_2D_OBJECT_STYLUS) {
+ major = max(1, major);
+ minor = max(1, minor);
+ }
+
+ input_event(sensor->input, EV_ABS, ABS_MT_POSITION_X, obj->x);
+ input_event(sensor->input, EV_ABS, ABS_MT_POSITION_Y, obj->y);
+ input_event(sensor->input, EV_ABS, ABS_MT_ORIENTATION, wide);
+ input_event(sensor->input, EV_ABS, ABS_MT_PRESSURE, obj->z);
+ input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
+ input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
+
+ dev_dbg(&sensor->input->dev, "%s: obj[%d]: type: 0x%02x X: %d Y: %d Z: %d WX: %d WY: %d\n",
+ __func__, slot, obj->type, obj->x, obj->y, obj->z,
+ obj->wx, obj->wy);
+ }
+}
+EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_report);
+
+void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y)
+{
+ struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align;
+
+ x = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)x));
+ y = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)y));
+
+ if (axis_align->swap_axes)
+ swap(x, y);
+
+ if (axis_align->flip_x)
+ x = min(RMI_2D_REL_POS_MAX, -x);
+
+ if (axis_align->flip_y)
+ y = min(RMI_2D_REL_POS_MAX, -y);
+
+ if (x || y) {
+ input_report_rel(sensor->input, REL_X, x);
+ input_report_rel(sensor->input, REL_Y, y);
+ }
+}
+EXPORT_SYMBOL_GPL(rmi_2d_sensor_rel_report);
+
+static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor)
+{
+ struct input_dev *input = sensor->input;
+ int res_x;
+ int res_y;
+ int input_flags = 0;
+
+ if (sensor->report_abs) {
+ if (sensor->axis_align.swap_axes)
+ swap(sensor->max_x, sensor->max_y);
+
+ sensor->min_x = sensor->axis_align.clip_x_low;
+ if (sensor->axis_align.clip_x_high)
+ sensor->max_x = min(sensor->max_x,
+ sensor->axis_align.clip_x_high);
+
+ sensor->min_y = sensor->axis_align.clip_y_low;
+ if (sensor->axis_align.clip_y_high)
+ sensor->max_y = min(sensor->max_y,
+ sensor->axis_align.clip_y_high);
+
+ set_bit(EV_ABS, input->evbit);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, sensor->max_x,
+ 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, sensor->max_y,
+ 0, 0);
+
+ if (sensor->x_mm && sensor->y_mm) {
+ res_x = (sensor->max_x - sensor->min_x) / sensor->x_mm;
+ res_y = (sensor->max_y - sensor->min_y) / sensor->y_mm;
+
+ input_abs_set_res(input, ABS_X, res_x);
+ input_abs_set_res(input, ABS_Y, res_y);
+
+ input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
+ input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
+
+ if (!sensor->dmax)
+ sensor->dmax = DMAX * res_x;
+ }
+
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
+ input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
+
+ if (sensor->sensor_type == rmi_sensor_touchpad)
+ input_flags = INPUT_MT_POINTER;
+ else
+ input_flags = INPUT_MT_DIRECT;
+
+ if (sensor->kernel_tracking)
+ input_flags |= INPUT_MT_TRACK;
+
+ input_mt_init_slots(input, sensor->nbr_fingers, input_flags);
+ }
+
+ if (sensor->report_rel) {
+ set_bit(EV_REL, input->evbit);
+ set_bit(REL_X, input->relbit);
+ set_bit(REL_Y, input->relbit);
+ }
+
+ if (sensor->topbuttonpad)
+ set_bit(INPUT_PROP_TOPBUTTONPAD, input->propbit);
+}
+EXPORT_SYMBOL_GPL(rmi_2d_sensor_set_input_params);
+
+int rmi_2d_sensor_configure_input(struct rmi_function *fn,
+ struct rmi_2d_sensor *sensor)
+{
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev);
+
+ if (!drv_data->input)
+ return -ENODEV;
+
+ sensor->input = drv_data->input;
+ rmi_2d_sensor_set_input_params(sensor);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rmi_2d_sensor_configure_input);
diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h
new file mode 100644
index 0000000..bc31351
--- /dev/null
+++ b/drivers/input/rmi4/rmi_2d_sensor.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#ifndef _RMI_2D_SENSOR_H
+#define _RMI_2D_SENSOR_H
+
+enum rmi_2d_sensor_object_type {
+ RMI_2D_OBJECT_NONE,
+ RMI_2D_OBJECT_FINGER,
+ RMI_2D_OBJECT_STYLUS,
+ RMI_2D_OBJECT_PALM,
+ RMI_2D_OBJECT_UNCLASSIFIED,
+};
+
+struct rmi_2d_sensor_abs_object {
+ enum rmi_2d_sensor_object_type type;
+ int mt_tool;
+ u16 x;
+ u16 y;
+ u8 z;
+ u8 wx;
+ u8 wy;
+};
+
+/**
+ * @axis_align - controls parameters that are useful in system prototyping
+ * and bring up.
+ * @max_x - The maximum X coordinate that will be reported by this sensor.
+ * @max_y - The maximum Y coordinate that will be reported by this sensor.
+ * @nbr_fingers - How many fingers can this sensor report?
+ * @data_pkt - buffer for data reported by this sensor.
+ * @pkt_size - number of bytes in that buffer.
+ * @attn_size - Size of the HID attention report (only contains abs data).
+ * position when two fingers are on the device. When this is true, we
+ * assume we have one of those sensors and report events appropriately.
+ * @sensor_type - indicates whether we're touchscreen or touchpad.
+ * @input - input device for absolute pointing stream
+ * @input_phys - buffer for the absolute phys name for this sensor.
+ */
+struct rmi_2d_sensor {
+ struct rmi_2d_axis_alignment axis_align;
+ struct input_mt_pos *tracking_pos;
+ int *tracking_slots;
+ bool kernel_tracking;
+ struct rmi_2d_sensor_abs_object *objs;
+ int dmax;
+ u16 min_x;
+ u16 max_x;
+ u16 min_y;
+ u16 max_y;
+ u8 nbr_fingers;
+ u8 *data_pkt;
+ int pkt_size;
+ int attn_size;
+ bool topbuttonpad;
+ enum rmi_sensor_type sensor_type;
+ struct input_dev *input;
+ bool unified_input;
+ struct rmi_function *fn;
+ char input_phys[32];
+ u8 report_abs;
+ u8 report_rel;
+ u8 x_mm;
+ u8 y_mm;
+};
+
+void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor,
+ struct rmi_2d_sensor_abs_object *obj,
+ int slot);
+
+void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor,
+ struct rmi_2d_sensor_abs_object *obj,
+ int slot);
+
+void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y);
+
+int rmi_2d_sensor_configure_input(struct rmi_function *fn,
+ struct rmi_2d_sensor *sensor);
+#endif /* _RMI_2D_SENSOR_H */
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index aa12f4a..21666ae 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -352,15 +352,23 @@ static int __init rmi_bus_init(void)
goto err_unregister_bus;
}
+ error = rmi_register_f11_handler();
+ if (error) {
+ pr_err("%s: error registering the RMI F11 handler: %d\n",
+ __func__, error);
+ goto err_unregister_f01;
+ }
+
error = rmi_register_physical_driver();
if (error) {
pr_err("%s: error registering the RMI physical driver: %d\n",
__func__, error);
- goto err_unregister_f01;
+ goto err_unregister_f11;
}
return 0;
-
+err_unregister_f11:
+ rmi_unregister_f11_handler();
err_unregister_f01:
rmi_unregister_f01_handler();
err_unregister_bus:
@@ -377,6 +385,7 @@ static void __exit rmi_bus_exit(void)
*/
rmi_unregister_physical_driver();
+ rmi_unregister_f11_handler();
rmi_unregister_f01_handler();
bus_unregister(&rmi_bus_type);
}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 41dd041..ab9d866 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -99,4 +99,12 @@ void rmi_unregister_physical_driver(void);
int rmi_register_f01_handler(void);
void rmi_unregister_f01_handler(void);
char *rmi_f01_get_product_ID(struct rmi_function *fn);
+
+#ifdef CONFIG_RMI4_F11
+int rmi_register_f11_handler(void);
+void rmi_unregister_f11_handler(void);
+#else
+static inline int rmi_register_f11_handler(void) { return 0; }
+static inline void rmi_unregister_f11_handler(void) {}
+#endif
#endif
diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c
new file mode 100644
index 0000000..0d1e0e5
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f11.c
@@ -0,0 +1,1349 @@
+/*
+ * Copyright (c) 2011-2015 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 version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#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 <linux/of.h>
+#include "rmi_driver.h"
+#include "rmi_2d_sensor.h"
+
+#define F11_MAX_NUM_OF_FINGERS 10
+#define F11_MAX_NUM_OF_TOUCH_SHAPES 16
+
+#define FINGER_STATE_MASK 0x03
+
+#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
+#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
+
+#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
+
+/** A note about RMI4 F11 register structure.
+ *
+ * The properties for
+ * a given sensor are described by its query registers. The number of query
+ * registers and the layout of their contents are described by the F11 device
+ * queries as well as the sensor query information.
+ *
+ * Similarly, each sensor has control registers that govern its behavior. The
+ * size and layout of the control registers for a given sensor can be determined
+ * by parsing that sensors query registers.
+ *
+ * And in a likewise fashion, each sensor has data registers where it reports
+ * its touch data and other interesting stuff. The size and layout of a
+ * sensors data registers must be determined by parsing its query registers.
+ *
+ * The short story is that we need to read and parse a lot of query
+ * registers in order to determine the attributes of a sensor. Then
+ * we need to use that data to compute the size of the control and data
+ * registers for sensor.
+ *
+ * The end result is that we have a number of structs that aren't used to
+ * directly generate the input events, but their size, location and contents
+ * are critical to determining where the data we are interested in lives.
+ *
+ * At this time, the driver does not yet comprehend all possible F11
+ * configuration options, but it should be sufficient to cover 99% of RMI4 F11
+ * devices currently in the field.
+ */
+
+/* maximum ABS_MT_POSITION displacement (in mm) */
+#define DMAX 10
+
+/**
+ * @rezero - writing this to the F11 command register will cause the sensor to
+ * calibrate to the current capacitive state.
+ */
+#define RMI_F11_REZERO 0x01
+
+#define RMI_F11_HAS_QUERY9 (1 << 3)
+#define RMI_F11_HAS_QUERY11 (1 << 4)
+#define RMI_F11_HAS_QUERY12 (1 << 5)
+#define RMI_F11_HAS_QUERY27 (1 << 6)
+#define RMI_F11_HAS_QUERY28 (1 << 7)
+
+/** Defs for Query 1 */
+
+#define RMI_F11_NR_FINGERS_MASK 0x07
+#define RMI_F11_HAS_REL (1 << 3)
+#define RMI_F11_HAS_ABS (1 << 4)
+#define RMI_F11_HAS_GESTURES (1 << 5)
+#define RMI_F11_HAS_SENSITIVITY_ADJ (1 << 6)
+#define RMI_F11_CONFIGURABLE (1 << 7)
+
+/** Defs for Query 2, 3, and 4. */
+#define RMI_F11_NR_ELECTRODES_MASK 0x7F
+
+/** Defs for Query 5 */
+
+#define RMI_F11_ABS_DATA_SIZE_MASK 0x03
+#define RMI_F11_HAS_ANCHORED_FINGER (1 << 2)
+#define RMI_F11_HAS_ADJ_HYST (1 << 3)
+#define RMI_F11_HAS_DRIBBLE (1 << 4)
+#define RMI_F11_HAS_BENDING_CORRECTION (1 << 5)
+#define RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION (1 << 6)
+#define RMI_F11_HAS_JITTER_FILTER (1 << 7)
+
+/** Defs for Query 7 */
+#define RMI_F11_HAS_SINGLE_TAP (1 << 0)
+#define RMI_F11_HAS_TAP_AND_HOLD (1 << 1)
+#define RMI_F11_HAS_DOUBLE_TAP (1 << 2)
+#define RMI_F11_HAS_EARLY_TAP (1 << 3)
+#define RMI_F11_HAS_FLICK (1 << 4)
+#define RMI_F11_HAS_PRESS (1 << 5)
+#define RMI_F11_HAS_PINCH (1 << 6)
+#define RMI_F11_HAS_CHIRAL (1 << 7)
+
+/** Defs for Query 8 */
+#define RMI_F11_HAS_PALM_DET (1 << 0)
+#define RMI_F11_HAS_ROTATE (1 << 1)
+#define RMI_F11_HAS_TOUCH_SHAPES (1 << 2)
+#define RMI_F11_HAS_SCROLL_ZONES (1 << 3)
+#define RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES (1 << 4)
+#define RMI_F11_HAS_MF_SCROLL (1 << 5)
+#define RMI_F11_HAS_MF_EDGE_MOTION (1 << 6)
+#define RMI_F11_HAS_MF_SCROLL_INERTIA (1 << 7)
+
+/** Defs for Query 9. */
+#define RMI_F11_HAS_PEN (1 << 0)
+#define RMI_F11_HAS_PROXIMITY (1 << 1)
+#define RMI_F11_HAS_PALM_DET_SENSITIVITY (1 << 2)
+#define RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT (1 << 3)
+#define RMI_F11_HAS_TWO_PEN_THRESHOLDS (1 << 4)
+#define RMI_F11_HAS_CONTACT_GEOMETRY (1 << 5)
+#define RMI_F11_HAS_PEN_HOVER_DISCRIMINATION (1 << 6)
+#define RMI_F11_HAS_PEN_FILTERS (1 << 7)
+
+/** Defs for Query 10. */
+#define RMI_F11_NR_TOUCH_SHAPES_MASK 0x1F
+
+/** Defs for Query 11 */
+
+#define RMI_F11_HAS_Z_TUNING (1 << 0)
+#define RMI_F11_HAS_ALGORITHM_SELECTION (1 << 1)
+#define RMI_F11_HAS_W_TUNING (1 << 2)
+#define RMI_F11_HAS_PITCH_INFO (1 << 3)
+#define RMI_F11_HAS_FINGER_SIZE (1 << 4)
+#define RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS (1 << 5)
+#define RMI_F11_HAS_XY_CLIP (1 << 6)
+#define RMI_F11_HAS_DRUMMING_FILTER (1 << 7)
+
+/** Defs for Query 12. */
+
+#define RMI_F11_HAS_GAPLESS_FINGER (1 << 0)
+#define RMI_F11_HAS_GAPLESS_FINGER_TUNING (1 << 1)
+#define RMI_F11_HAS_8BIT_W (1 << 2)
+#define RMI_F11_HAS_ADJUSTABLE_MAPPING (1 << 3)
+#define RMI_F11_HAS_INFO2 (1 << 4)
+#define RMI_F11_HAS_PHYSICAL_PROPS (1 << 5)
+#define RMI_F11_HAS_FINGER_LIMIT (1 << 6)
+#define RMI_F11_HAS_LINEAR_COEFF (1 << 7)
+
+/** Defs for Query 13. */
+
+#define RMI_F11_JITTER_WINDOW_MASK 0x1F
+#define RMI_F11_JITTER_FILTER_MASK 0x60
+#define RMI_F11_JITTER_FILTER_SHIFT 5
+
+/** Defs for Query 14. */
+#define RMI_F11_LIGHT_CONTROL_MASK 0x03
+#define RMI_F11_IS_CLEAR (1 << 2)
+#define RMI_F11_CLICKPAD_PROPS_MASK 0x18
+#define RMI_F11_CLICKPAD_PROPS_SHIFT 3
+#define RMI_F11_MOUSE_BUTTONS_MASK 0x60
+#define RMI_F11_MOUSE_BUTTONS_SHIFT 5
+#define RMI_F11_HAS_ADVANCED_GESTURES (1 << 7)
+
+#define RMI_F11_QUERY_SIZE 4
+#define RMI_F11_QUERY_GESTURE_SIZE 2
+
+#define F11_LIGHT_CTL_NONE 0x00
+#define F11_LUXPAD 0x01
+#define F11_DUAL_MODE 0x02
+
+#define F11_NOT_CLICKPAD 0x00
+#define F11_HINGED_CLICKPAD 0x01
+#define F11_UNIFORM_CLICKPAD 0x02
+
+/**
+ * Query registers 1 through 4 are always present.
+ *
+ * @nr_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.
+ *
+ * Query 5 is present if the has_abs bit is set.
+ *
+ * @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.
+ * @has_bending_correction - Bending related data registers 28 and 36, and
+ * control register 52..57 are present.
+ * @has_large_object_suppression - control register 58 and data register 28
+ * exist.
+ * @has_jitter_filter - query 13 and control 73..76 exist.
+ *
+ * Gesture information queries 7 and 8 are present if has_gestures bit is set.
+ *
+ * @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_mf_scroll - the multifinger_scrolling bit will be set when
+ * more than one finger is involved in a scrolling action.
+ *
+ * Convenience for checking bytes in the gesture info registers. This is done
+ * often enough that we put it here to declutter the conditionals
+ *
+ * @query7_nonzero - true if none of the query 7 bits are set
+ * @query8_nonzero - true if none of the query 8 bits are set
+ *
+ * Query 9 is present if the has_query9 is set.
+ *
+ * @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.
+ *
+ * Touch shape info (query 10) is present if has_touch_shapes is set.
+ *
+ * @nr_touch_shapes - the total number of touch shapes supported.
+ *
+ * Query 11 is present if the has_query11 bit is set in query 0.
+ *
+ * @has_z_tuning - if set, the sensor supports Z tuning and registers
+ * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist.
+ * @has_algorithm_selection - controls choice of noise suppression algorithm
+ * @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_finger_size - 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_XY_clip - the inactive outside borders of the sensor can be
+ * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist.
+ * @has_drumming_filter - 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.
+ *
+ * Query 12 is present if hasQuery12 bit is set.
+ *
+ * @has_gapless_finger - control registers relating to gapless finger are
+ * present.
+ * @has_gapless_finger_tuning - additional control and data registers relating
+ * to gapless finger are present.
+ * @has_8bit_w - larger W value reporting is supported.
+ * @has_adjustable_mapping - TBD
+ * @has_info2 - the general info query14 is present
+ * @has_physical_props - additional queries describing the physical properties
+ * of the sensor are present.
+ * @has_finger_limit - indicates that F11 Ctrl 80 exists.
+ * @has_linear_coeff - indicates that F11 Ctrl 81 exists.
+ *
+ * Query 13 is present if Query 5's has_jitter_filter bit is set.
+ * @jitter_window_size - used by Design Studio 4.
+ * @jitter_filter_type - used by Design Studio 4.
+ *
+ * Query 14 is present if query 12's has_general_info2 flag is set.
+ *
+ * @light_control - Indicates what light/led control features are present, if
+ * any.
+ * @is_clear - if set, this is a clear sensor (indicating direct pointing
+ * application), otherwise it's opaque (indicating indirect pointing).
+ * @clickpad_props - specifies if this is a clickpad, and if so what sort of
+ * mechanism it uses
+ * @mouse_buttons - specifies the number of mouse buttons present (if any).
+ * @has_advanced_gestures - advanced driver gestures are supported.
+ */
+struct f11_2d_sensor_queries {
+ /* query1 */
+ u8 nr_fingers;
+ bool has_rel;
+ bool has_abs;
+ bool has_gestures;
+ bool has_sensitivity_adjust;
+ bool configurable;
+
+ /* query2 */
+ u8 nr_x_electrodes;
+
+ /* query3 */
+ u8 nr_y_electrodes;
+
+ /* query4 */
+ u8 max_electrodes;
+
+ /* query5 */
+ u8 abs_data_size;
+ bool has_anchored_finger;
+ bool has_adj_hyst;
+ bool has_dribble;
+ bool has_bending_correction;
+ bool has_large_object_suppression;
+ bool has_jitter_filter;
+
+ u8 f11_2d_query6;
+
+ /* query 7 */
+ bool has_single_tap;
+ bool has_tap_n_hold;
+ bool has_double_tap;
+ bool has_early_tap;
+ bool has_flick;
+ bool has_press;
+ bool has_pinch;
+ bool has_chiral;
+
+ bool query7_nonzero;
+
+ /* query 8 */
+ bool has_palm_det;
+ bool has_rotate;
+ bool has_touch_shapes;
+ bool has_scroll_zones;
+ bool has_individual_scroll_zones;
+ bool has_mf_scroll;
+ bool has_mf_edge_motion;
+ bool has_mf_scroll_inertia;
+
+ bool query8_nonzero;
+
+ /* Query 9 */
+ bool has_pen;
+ bool has_proximity;
+ bool has_palm_det_sensitivity;
+ bool has_suppress_on_palm_detect;
+ bool has_two_pen_thresholds;
+ bool has_contact_geometry;
+ bool has_pen_hover_discrimination;
+ bool has_pen_filters;
+
+ /* Query 10 */
+ u8 nr_touch_shapes;
+
+ /* Query 11. */
+ bool has_z_tuning;
+ bool has_algorithm_selection;
+ bool has_w_tuning;
+ bool has_pitch_info;
+ bool has_finger_size;
+ bool has_segmentation_aggressiveness;
+ bool has_XY_clip;
+ bool has_drumming_filter;
+
+ /* Query 12 */
+ bool has_gapless_finger;
+ bool has_gapless_finger_tuning;
+ bool has_8bit_w;
+ bool has_adjustable_mapping;
+ bool has_info2;
+ bool has_physical_props;
+ bool has_finger_limit;
+ bool has_linear_coeff_2;
+
+ /* Query 13 */
+ u8 jitter_window_size;
+ u8 jitter_filter_type;
+
+ /* Query 14 */
+ u8 light_control;
+ bool is_clear;
+ u8 clickpad_props;
+ u8 mouse_buttons;
+ bool has_advanced_gestures;
+
+ /* Query 15 - 18 */
+ u16 x_sensor_size_mm;
+ u16 y_sensor_size_mm;
+};
+
+/* Defs for Ctrl0. */
+#define RMI_F11_REPORT_MODE_MASK 0x07
+#define RMI_F11_ABS_POS_FILT (1 << 3)
+#define RMI_F11_REL_POS_FILT (1 << 4)
+#define RMI_F11_REL_BALLISTICS (1 << 5)
+#define RMI_F11_DRIBBLE (1 << 6)
+#define RMI_F11_REPORT_BEYOND_CLIP (1 << 7)
+
+/* Defs for Ctrl1. */
+#define RMI_F11_PALM_DETECT_THRESH_MASK 0x0F
+#define RMI_F11_MOTION_SENSITIVITY_MASK 0x30
+#define RMI_F11_MANUAL_TRACKING (1 << 6)
+#define RMI_F11_MANUAL_TRACKED_FINGER (1 << 7)
+
+#define RMI_F11_DELTA_X_THRESHOLD 2
+#define RMI_F11_DELTA_Y_THRESHOLD 3
+
+#define RMI_F11_CTRL_REG_COUNT 12
+
+struct f11_2d_ctrl {
+ u8 ctrl0_11[RMI_F11_CTRL_REG_COUNT];
+ u16 ctrl0_11_address;
+};
+
+#define RMI_F11_ABS_BYTES 5
+#define RMI_F11_REL_BYTES 2
+
+/* Defs for Data 8 */
+
+#define RMI_F11_SINGLE_TAP (1 << 0)
+#define RMI_F11_TAP_AND_HOLD (1 << 1)
+#define RMI_F11_DOUBLE_TAP (1 << 2)
+#define RMI_F11_EARLY_TAP (1 << 3)
+#define RMI_F11_FLICK (1 << 4)
+#define RMI_F11_PRESS (1 << 5)
+#define RMI_F11_PINCH (1 << 6)
+
+/* Defs for Data 9 */
+
+#define RMI_F11_PALM_DETECT (1 << 0)
+#define RMI_F11_ROTATE (1 << 1)
+#define RMI_F11_SHAPE (1 << 2)
+#define RMI_F11_SCROLLZONE (1 << 3)
+#define RMI_F11_GESTURE_FINGER_COUNT_MASK 0x70
+
+/** Handy pointers into our data buffer.
+ *
+ * @f_state - start of finger state registers.
+ * @abs_pos - start of absolute position registers (if present).
+ * @rel_pos - start of relative data registers (if present).
+ * @gest_1 - gesture flags (if present).
+ * @gest_2 - gesture flags & finger count (if present).
+ * @pinch - pinch motion register (if present).
+ * @flick - flick distance X & Y, flick time (if present).
+ * @rotate - rotate motion and finger separation.
+ * @multi_scroll - chiral deltas for X and Y (if present).
+ * @scroll_zones - scroll deltas for 4 regions (if present).
+ */
+struct f11_2d_data {
+ u8 *f_state;
+ u8 *abs_pos;
+ s8 *rel_pos;
+ u8 *gest_1;
+ u8 *gest_2;
+ s8 *pinch;
+ u8 *flick;
+ u8 *rotate;
+ u8 *shapes;
+ s8 *multi_scroll;
+ s8 *scroll_zones;
+};
+
+/** Data pertaining to F11 in general. For per-sensor data, see struct
+ * f11_2d_sensor.
+ *
+ * @dev_query - F11 device specific query registers.
+ * @dev_controls - F11 device specific control registers.
+ * @dev_controls_mutex - lock for the control registers.
+ * @rezero_wait_ms - if nonzero, upon resume we will wait this many
+ * milliseconds before rezeroing the sensor(s). This is useful in systems with
+ * poor electrical behavior on resume, where the initial calibration of the
+ * sensor(s) coming out of sleep state may be bogus.
+ * @sensors - per sensor data structures.
+ */
+struct f11_data {
+ bool has_query9;
+ bool has_query11;
+ bool has_query12;
+ bool has_query27;
+ bool has_query28;
+ bool has_acm;
+ struct f11_2d_ctrl dev_controls;
+ struct mutex dev_controls_mutex;
+ u16 rezero_wait_ms;
+ struct rmi_2d_sensor sensor;
+ struct f11_2d_sensor_queries sens_query;
+ struct f11_2d_data data;
+ struct rmi_2d_sensor_platform_data sensor_pdata;
+ unsigned long *abs_mask;
+ unsigned long *rel_mask;
+ unsigned long *result_bits;
+};
+
+enum f11_finger_state {
+ F11_NO_FINGER = 0x00,
+ F11_PRESENT = 0x01,
+ F11_INACCURATE = 0x02,
+ F11_RESERVED = 0x03
+};
+
+/** F11_INACCURATE state is overloaded to indicate pen present. */
+#define F11_PEN F11_INACCURATE
+
+static int rmi_f11_get_tool_type(struct f11_data *f11,
+ enum f11_finger_state finger_state)
+{
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ f11->sens_query.has_pen &&
+ finger_state == F11_PEN)
+ return MT_TOOL_PEN;
+
+ return MT_TOOL_FINGER;
+}
+
+static void rmi_f11_rel_pos_report(struct f11_data *f11, u8 n_finger)
+{
+ struct rmi_2d_sensor *sensor = &f11->sensor;
+ struct f11_2d_data *data = &f11->data;
+ s8 x, y;
+
+ x = data->rel_pos[n_finger * 2];
+ y = data->rel_pos[n_finger * 2 + 1];
+
+ rmi_2d_sensor_rel_report(sensor, x, y);
+}
+
+static void rmi_f11_abs_pos_process(struct f11_data *f11,
+ struct rmi_2d_sensor *sensor,
+ struct rmi_2d_sensor_abs_object *obj,
+ enum f11_finger_state finger_state,
+ u8 n_finger)
+{
+ struct f11_2d_data *data = &f11->data;
+ u8 *pos_data = &data->abs_pos[n_finger * RMI_F11_ABS_BYTES];
+ int tool_type = rmi_f11_get_tool_type(f11, finger_state);
+
+ switch (finger_state) {
+ case F11_PEN:
+ if (IS_ENABLED(CONFIG_RMI4_F11_PEN) &&
+ f11->sens_query.has_pen)
+ obj->type = RMI_2D_OBJECT_STYLUS;
+ break;
+ case F11_PRESENT:
+ obj->type = RMI_2D_OBJECT_FINGER;
+ break;
+ default:
+ obj->type = RMI_2D_OBJECT_NONE;
+ }
+
+ obj->mt_tool = tool_type;
+ obj->x = (pos_data[0] << 4) | (pos_data[2] & 0x0F);
+ obj->y = (pos_data[1] << 4) | (pos_data[2] >> 4);
+ obj->z = pos_data[4];
+ obj->wx = pos_data[3] & 0x0f;
+ obj->wy = pos_data[3] >> 4;
+
+ rmi_2d_sensor_abs_process(sensor, obj, n_finger);
+}
+
+static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger)
+{
+ return (f_state[n_finger / 4] >> (2 * (n_finger % 4))) &
+ FINGER_STATE_MASK;
+}
+
+static void rmi_f11_finger_handler(struct f11_data *f11,
+ struct rmi_2d_sensor *sensor,
+ unsigned long *irq_bits, int num_irq_regs)
+{
+ const u8 *f_state = f11->data.f_state;
+ u8 finger_state;
+ u8 i;
+
+ int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask,
+ num_irq_regs * 8);
+ int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask,
+ num_irq_regs * 8);
+
+ for (i = 0; i < sensor->nbr_fingers; i++) {
+ /* Possible of having 4 fingers per f_statet register */
+ finger_state = rmi_f11_parse_finger_state(f_state, i);
+ if (finger_state == F11_RESERVED) {
+ pr_err("Invalid finger state[%d]: 0x%02x", i,
+ finger_state);
+ continue;
+ }
+
+ if (abs_bits)
+ rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i],
+ finger_state, i);
+
+ if (rel_bits)
+ rmi_f11_rel_pos_report(f11, i);
+ }
+
+ if (abs_bits) {
+ /*
+ * the absolute part is made in 2 parts to allow the kernel
+ * tracking to take place.
+ */
+ if (sensor->kernel_tracking)
+ input_mt_assign_slots(sensor->input,
+ sensor->tracking_slots,
+ sensor->tracking_pos,
+ sensor->nbr_fingers,
+ sensor->dmax);
+
+ for (i = 0; i < sensor->nbr_fingers; i++) {
+ finger_state = rmi_f11_parse_finger_state(f_state, i);
+ if (finger_state == F11_RESERVED)
+ /* no need to send twice the error */
+ continue;
+
+ rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i);
+ }
+
+ input_mt_sync_frame(sensor->input);
+ }
+}
+
+static int f11_2d_construct_data(struct f11_data *f11)
+{
+ struct rmi_2d_sensor *sensor = &f11->sensor;
+ struct f11_2d_sensor_queries *query = &f11->sens_query;
+ struct f11_2d_data *data = &f11->data;
+ int i;
+
+ sensor->nbr_fingers = (query->nr_fingers == 5 ? 10 :
+ query->nr_fingers + 1);
+
+ sensor->pkt_size = DIV_ROUND_UP(sensor->nbr_fingers, 4);
+
+ if (query->has_abs) {
+ sensor->pkt_size += (sensor->nbr_fingers * 5);
+ sensor->attn_size = sensor->pkt_size;
+ }
+
+ if (query->has_rel)
+ sensor->pkt_size += (sensor->nbr_fingers * 2);
+
+ /* Check if F11_2D_Query7 is non-zero */
+ if (query->query7_nonzero)
+ sensor->pkt_size += sizeof(u8);
+
+ /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
+ if (query->query7_nonzero || query->query8_nonzero)
+ 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 +=
+ DIV_ROUND_UP(query->nr_touch_shapes + 1, 8);
+
+ sensor->data_pkt = devm_kzalloc(&sensor->fn->dev, sensor->pkt_size,
+ GFP_KERNEL);
+ if (!sensor->data_pkt)
+ return -ENOMEM;
+
+ data->f_state = sensor->data_pkt;
+ i = DIV_ROUND_UP(sensor->nbr_fingers, 4);
+
+ if (query->has_abs) {
+ data->abs_pos = &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * RMI_F11_ABS_BYTES);
+ }
+
+ if (query->has_rel) {
+ data->rel_pos = &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * RMI_F11_REL_BYTES);
+ }
+
+ if (query->query7_nonzero) {
+ data->gest_1 = &sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->query7_nonzero || query->query8_nonzero) {
+ data->gest_2 = &sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_pinch) {
+ data->pinch = &sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_flick) {
+ if (query->has_pinch) {
+ data->flick = data->pinch;
+ i += 2;
+ } else {
+ data->flick = &sensor->data_pkt[i];
+ i += 3;
+ }
+ }
+
+ if (query->has_rotate) {
+ if (query->has_flick) {
+ data->rotate = data->flick + 1;
+ } else {
+ data->rotate = &sensor->data_pkt[i];
+ i += 2;
+ }
+ }
+
+ if (query->has_touch_shapes)
+ data->shapes = &sensor->data_pkt[i];
+
+ return 0;
+}
+
+static int f11_read_control_regs(struct rmi_function *fn,
+ struct f11_2d_ctrl *ctrl, u16 ctrl_base_addr) {
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ int error = 0;
+
+ ctrl->ctrl0_11_address = ctrl_base_addr;
+ error = rmi_read_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_11,
+ RMI_F11_CTRL_REG_COUNT);
+ if (error < 0) {
+ dev_err(&fn->dev, "Failed to read ctrl0, code: %d.\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int f11_write_control_regs(struct rmi_function *fn,
+ struct f11_2d_sensor_queries *query,
+ struct f11_2d_ctrl *ctrl,
+ u16 ctrl_base_addr)
+{
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ int error;
+
+ error = rmi_write_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_11,
+ RMI_F11_CTRL_REG_COUNT);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+ struct f11_data *f11,
+ struct f11_2d_sensor_queries *sensor_query,
+ u16 query_base_addr)
+{
+ int query_size;
+ int rc;
+ u8 query_buf[RMI_F11_QUERY_SIZE];
+ bool has_query36 = false;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr, query_buf,
+ RMI_F11_QUERY_SIZE);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->nr_fingers = query_buf[0] & RMI_F11_NR_FINGERS_MASK;
+ sensor_query->has_rel = !!(query_buf[0] & RMI_F11_HAS_REL);
+ sensor_query->has_abs = !!(query_buf[0] & RMI_F11_HAS_ABS);
+ sensor_query->has_gestures = !!(query_buf[0] & RMI_F11_HAS_GESTURES);
+ sensor_query->has_sensitivity_adjust =
+ !!(query_buf[0] && RMI_F11_HAS_SENSITIVITY_ADJ);
+ sensor_query->configurable = !!(query_buf[0] & RMI_F11_CONFIGURABLE);
+
+ sensor_query->nr_x_electrodes =
+ query_buf[1] & RMI_F11_NR_ELECTRODES_MASK;
+ sensor_query->nr_y_electrodes =
+ query_buf[2] & RMI_F11_NR_ELECTRODES_MASK;
+ sensor_query->max_electrodes =
+ query_buf[3] & RMI_F11_NR_ELECTRODES_MASK;
+
+ query_size = RMI_F11_QUERY_SIZE;
+
+ if (sensor_query->has_abs) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->abs_data_size =
+ query_buf[0] & RMI_F11_ABS_DATA_SIZE_MASK;
+ sensor_query->has_anchored_finger =
+ !!(query_buf[0] & RMI_F11_HAS_ANCHORED_FINGER);
+ sensor_query->has_adj_hyst =
+ !!(query_buf[0] & RMI_F11_HAS_ADJ_HYST);
+ sensor_query->has_dribble =
+ !!(query_buf[0] & RMI_F11_HAS_DRIBBLE);
+ sensor_query->has_bending_correction =
+ !!(query_buf[0] & RMI_F11_HAS_BENDING_CORRECTION);
+ sensor_query->has_large_object_suppression =
+ !!(query_buf[0] && RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION);
+ sensor_query->has_jitter_filter =
+ !!(query_buf[0] & RMI_F11_HAS_JITTER_FILTER);
+ query_size++;
+ }
+
+ if (sensor_query->has_rel) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &sensor_query->f11_2d_query6);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (sensor_query->has_gestures) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ query_buf, RMI_F11_QUERY_GESTURE_SIZE);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->has_single_tap =
+ !!(query_buf[0] & RMI_F11_HAS_SINGLE_TAP);
+ sensor_query->has_tap_n_hold =
+ !!(query_buf[0] & RMI_F11_HAS_TAP_AND_HOLD);
+ sensor_query->has_double_tap =
+ !!(query_buf[0] & RMI_F11_HAS_DOUBLE_TAP);
+ sensor_query->has_early_tap =
+ !!(query_buf[0] & RMI_F11_HAS_EARLY_TAP);
+ sensor_query->has_flick =
+ !!(query_buf[0] & RMI_F11_HAS_FLICK);
+ sensor_query->has_press =
+ !!(query_buf[0] & RMI_F11_HAS_PRESS);
+ sensor_query->has_pinch =
+ !!(query_buf[0] & RMI_F11_HAS_PINCH);
+ sensor_query->has_chiral =
+ !!(query_buf[0] & RMI_F11_HAS_CHIRAL);
+
+ /* query 8 */
+ sensor_query->has_palm_det =
+ !!(query_buf[1] & RMI_F11_HAS_PALM_DET);
+ sensor_query->has_rotate =
+ !!(query_buf[1] & RMI_F11_HAS_ROTATE);
+ sensor_query->has_touch_shapes =
+ !!(query_buf[1] & RMI_F11_HAS_TOUCH_SHAPES);
+ sensor_query->has_scroll_zones =
+ !!(query_buf[1] & RMI_F11_HAS_SCROLL_ZONES);
+ sensor_query->has_individual_scroll_zones =
+ !!(query_buf[1] & RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES);
+ sensor_query->has_mf_scroll =
+ !!(query_buf[1] & RMI_F11_HAS_MF_SCROLL);
+ sensor_query->has_mf_edge_motion =
+ !!(query_buf[1] & RMI_F11_HAS_MF_EDGE_MOTION);
+ sensor_query->has_mf_scroll_inertia =
+ !!(query_buf[1] & RMI_F11_HAS_MF_SCROLL_INERTIA);
+
+ sensor_query->query7_nonzero = !!(query_buf[0]);
+ sensor_query->query8_nonzero = !!(query_buf[1]);
+
+ query_size += 2;
+ }
+
+ if (f11->has_query9) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->has_pen =
+ !!(query_buf[0] & RMI_F11_HAS_PEN);
+ sensor_query->has_proximity =
+ !!(query_buf[0] & RMI_F11_HAS_PROXIMITY);
+ sensor_query->has_palm_det_sensitivity =
+ !!(query_buf[0] & RMI_F11_HAS_PALM_DET_SENSITIVITY);
+ sensor_query->has_suppress_on_palm_detect =
+ !!(query_buf[0] & RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT);
+ sensor_query->has_two_pen_thresholds =
+ !!(query_buf[0] & RMI_F11_HAS_TWO_PEN_THRESHOLDS);
+ sensor_query->has_contact_geometry =
+ !!(query_buf[0] & RMI_F11_HAS_CONTACT_GEOMETRY);
+ sensor_query->has_pen_hover_discrimination =
+ !!(query_buf[0] & RMI_F11_HAS_PEN_HOVER_DISCRIMINATION);
+ sensor_query->has_pen_filters =
+ !!(query_buf[0] & RMI_F11_HAS_PEN_FILTERS);
+
+ query_size++;
+ }
+
+ if (sensor_query->has_touch_shapes) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->nr_touch_shapes = query_buf[0] &
+ RMI_F11_NR_TOUCH_SHAPES_MASK;
+
+ query_size++;
+ }
+
+ if (f11->has_query11) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->has_z_tuning =
+ !!(query_buf[0] & RMI_F11_HAS_Z_TUNING);
+ sensor_query->has_algorithm_selection =
+ !!(query_buf[0] & RMI_F11_HAS_ALGORITHM_SELECTION);
+ sensor_query->has_w_tuning =
+ !!(query_buf[0] & RMI_F11_HAS_W_TUNING);
+ sensor_query->has_pitch_info =
+ !!(query_buf[0] & RMI_F11_HAS_PITCH_INFO);
+ sensor_query->has_finger_size =
+ !!(query_buf[0] & RMI_F11_HAS_FINGER_SIZE);
+ sensor_query->has_segmentation_aggressiveness =
+ !!(query_buf[0] &
+ RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS);
+ sensor_query->has_XY_clip =
+ !!(query_buf[0] & RMI_F11_HAS_XY_CLIP);
+ sensor_query->has_drumming_filter =
+ !!(query_buf[0] & RMI_F11_HAS_DRUMMING_FILTER);
+
+ query_size++;
+ }
+
+ if (f11->has_query12) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->has_gapless_finger =
+ !!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER);
+ sensor_query->has_gapless_finger_tuning =
+ !!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER_TUNING);
+ sensor_query->has_8bit_w =
+ !!(query_buf[0] & RMI_F11_HAS_8BIT_W);
+ sensor_query->has_adjustable_mapping =
+ !!(query_buf[0] & RMI_F11_HAS_ADJUSTABLE_MAPPING);
+ sensor_query->has_info2 =
+ !!(query_buf[0] & RMI_F11_HAS_INFO2);
+ sensor_query->has_physical_props =
+ !!(query_buf[0] & RMI_F11_HAS_PHYSICAL_PROPS);
+ sensor_query->has_finger_limit =
+ !!(query_buf[0] & RMI_F11_HAS_FINGER_LIMIT);
+ sensor_query->has_linear_coeff_2 =
+ !!(query_buf[0] & RMI_F11_HAS_LINEAR_COEFF);
+
+ query_size++;
+ }
+
+ if (sensor_query->has_jitter_filter) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->jitter_window_size = query_buf[0] &
+ RMI_F11_JITTER_WINDOW_MASK;
+ sensor_query->jitter_filter_type = (query_buf[0] &
+ RMI_F11_JITTER_FILTER_MASK) >>
+ RMI_F11_JITTER_FILTER_SHIFT;
+
+ query_size++;
+ }
+
+ if (sensor_query->has_info2) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->light_control =
+ query_buf[0] & RMI_F11_LIGHT_CONTROL_MASK;
+ sensor_query->is_clear =
+ !!(query_buf[0] & RMI_F11_IS_CLEAR);
+ sensor_query->clickpad_props =
+ (query_buf[0] & RMI_F11_CLICKPAD_PROPS_MASK) >>
+ RMI_F11_CLICKPAD_PROPS_SHIFT;
+ sensor_query->mouse_buttons =
+ (query_buf[0] & RMI_F11_MOUSE_BUTTONS_MASK) >>
+ RMI_F11_MOUSE_BUTTONS_SHIFT;
+ sensor_query->has_advanced_gestures =
+ !!(query_buf[0] & RMI_F11_HAS_ADVANCED_GESTURES);
+
+ query_size++;
+ }
+
+ if (sensor_query->has_physical_props) {
+ rc = rmi_read_block(rmi_dev, query_base_addr
+ + query_size, query_buf, 4);
+ if (rc < 0)
+ return rc;
+
+ sensor_query->x_sensor_size_mm =
+ (query_buf[0] | (query_buf[1] << 8)) / 10;
+ sensor_query->y_sensor_size_mm =
+ (query_buf[2] | (query_buf[3] << 8)) / 10;
+
+ /*
+ * query 15 - 18 contain the size of the sensor
+ * and query 19 - 26 contain bezel dimensions
+ */
+ query_size += 12;
+ }
+
+ if (f11->has_query27)
+ ++query_size;
+
+ if (f11->has_query28) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ query_buf);
+ if (rc < 0)
+ return rc;
+
+ has_query36 = !!(query_buf[0] & BIT(6));
+ }
+
+ if (has_query36) {
+ query_size += 2;
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ query_buf);
+ if (rc < 0)
+ return rc;
+
+ if (!!(query_buf[0] & BIT(5)))
+ f11->has_acm = true;
+ }
+
+ return query_size;
+}
+
+static int rmi_f11_initialize(struct rmi_function *fn)
+{
+ struct rmi_device *rmi_dev = fn->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;
+ int rc;
+ const struct rmi_device_platform_data *pdata =
+ rmi_get_platform_data(rmi_dev);
+ struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
+ struct rmi_2d_sensor *sensor;
+ u8 buf;
+ int mask_size;
+
+ dev_dbg(&fn->dev, "Initializing F11 values.\n");
+
+ mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long);
+
+ /*
+ ** init instance data, fill in values and create any sysfs files
+ */
+ f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data) + mask_size * 3,
+ GFP_KERNEL);
+ if (!f11)
+ return -ENOMEM;
+
+ if (pdata->sensor_pdata)
+ f11->sensor_pdata = *pdata->sensor_pdata;
+
+ f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait;
+
+ f11->abs_mask = (unsigned long *)((char *)f11
+ + sizeof(struct f11_data));
+ f11->rel_mask = (unsigned long *)((char *)f11
+ + sizeof(struct f11_data) + mask_size);
+ f11->result_bits = (unsigned long *)((char *)f11
+ + sizeof(struct f11_data) + mask_size * 2);
+
+ set_bit(fn->irq_pos, f11->abs_mask);
+ set_bit(fn->irq_pos + 1, f11->rel_mask);
+
+ query_base_addr = fn->fd.query_base_addr;
+ control_base_addr = fn->fd.control_base_addr;
+
+ rc = rmi_read(rmi_dev, query_base_addr, &buf);
+ if (rc < 0)
+ return rc;
+
+ f11->has_query9 = !!(buf & RMI_F11_HAS_QUERY9);
+ f11->has_query11 = !!(buf & RMI_F11_HAS_QUERY11);
+ f11->has_query12 = !!(buf & RMI_F11_HAS_QUERY12);
+ f11->has_query27 = !!(buf & RMI_F11_HAS_QUERY27);
+ f11->has_query28 = !!(buf & RMI_F11_HAS_QUERY28);
+
+ query_offset = (query_base_addr + 1);
+ sensor = &f11->sensor;
+ sensor->fn = fn;
+
+ rc = rmi_f11_get_query_parameters(rmi_dev, f11,
+ &f11->sens_query, query_offset);
+ if (rc < 0)
+ return rc;
+ query_offset += rc;
+
+ rc = f11_read_control_regs(fn, &f11->dev_controls,
+ control_base_addr);
+ if (rc < 0) {
+ dev_err(&fn->dev,
+ "Failed to read F11 control params.\n");
+ return rc;
+ }
+
+ if (f11->sens_query.has_info2) {
+ if (f11->sens_query.is_clear)
+ f11->sensor.sensor_type = rmi_sensor_touchscreen;
+ else
+ f11->sensor.sensor_type = rmi_sensor_touchpad;
+ }
+
+ sensor->report_abs = f11->sens_query.has_abs;
+
+ sensor->axis_align =
+ f11->sensor_pdata.axis_align;
+
+ sensor->topbuttonpad = f11->sensor_pdata.topbuttonpad;
+ sensor->kernel_tracking = f11->sensor_pdata.kernel_tracking;
+ sensor->dmax = f11->sensor_pdata.dmax;
+
+ if (f11->sens_query.has_physical_props) {
+ sensor->x_mm = f11->sens_query.x_sensor_size_mm;
+ sensor->y_mm = f11->sens_query.y_sensor_size_mm;
+ } else {
+ sensor->x_mm = f11->sensor_pdata.x_mm;
+ sensor->y_mm = f11->sensor_pdata.y_mm;
+ }
+
+ if (sensor->sensor_type == rmi_sensor_default)
+ sensor->sensor_type =
+ f11->sensor_pdata.sensor_type;
+
+ sensor->report_abs = sensor->report_abs
+ && !(f11->sensor_pdata.disable_report_mask
+ & RMI_F11_DISABLE_ABS_REPORT);
+
+ if (!sensor->report_abs)
+ /*
+ * If device doesn't have abs or if it has been disables
+ * fallback to reporting rel data.
+ */
+ sensor->report_rel = f11->sens_query.has_rel;
+
+ 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)
+ swap(max_x_pos, max_y_pos);
+
+ sensor->max_x = max_x_pos;
+ sensor->max_y = max_y_pos;
+
+ rc = f11_2d_construct_data(f11);
+ if (rc < 0)
+ return rc;
+
+ if (f11->has_acm)
+ f11->sensor.attn_size += f11->sensor.nbr_fingers * 2;
+
+ /* allocate the in-kernel tracking buffers */
+ sensor->tracking_pos = devm_kzalloc(&fn->dev,
+ sizeof(struct input_mt_pos) * sensor->nbr_fingers,
+ GFP_KERNEL);
+ sensor->tracking_slots = devm_kzalloc(&fn->dev,
+ sizeof(int) * sensor->nbr_fingers, GFP_KERNEL);
+ sensor->objs = devm_kzalloc(&fn->dev,
+ sizeof(struct rmi_2d_sensor_abs_object)
+ * sensor->nbr_fingers, GFP_KERNEL);
+ if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs)
+ return -ENOMEM;
+
+ ctrl = &f11->dev_controls;
+ if (sensor->axis_align.delta_x_threshold)
+ ctrl->ctrl0_11[RMI_F11_DELTA_X_THRESHOLD] =
+ sensor->axis_align.delta_x_threshold;
+
+ if (sensor->axis_align.delta_y_threshold)
+ ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] =
+ sensor->axis_align.delta_y_threshold;
+
+ if (f11->sens_query.has_dribble)
+ ctrl->ctrl0_11[0] = ctrl->ctrl0_11[0] & ~BIT(6);
+
+ if (f11->sens_query.has_palm_det)
+ ctrl->ctrl0_11[11] = ctrl->ctrl0_11[11] & ~BIT(0);
+
+ rc = f11_write_control_regs(fn, &f11->sens_query,
+ &f11->dev_controls, fn->fd.query_base_addr);
+ if (rc)
+ dev_warn(&fn->dev, "Failed to write control registers\n");
+
+ mutex_init(&f11->dev_controls_mutex);
+
+ dev_set_drvdata(&fn->dev, f11);
+
+ return 0;
+}
+
+static int rmi_f11_config(struct rmi_function *fn)
+{
+ struct f11_data *f11 = dev_get_drvdata(&fn->dev);
+ struct rmi_driver *drv = fn->rmi_dev->driver;
+ struct rmi_2d_sensor *sensor = &f11->sensor;
+ int rc;
+
+ if (!sensor->report_abs)
+ drv->clear_irq_bits(fn->rmi_dev, f11->abs_mask);
+ else
+ drv->set_irq_bits(fn->rmi_dev, f11->abs_mask);
+
+ if (!sensor->report_rel)
+ drv->clear_irq_bits(fn->rmi_dev, f11->rel_mask);
+ else
+ drv->set_irq_bits(fn->rmi_dev, f11->rel_mask);
+
+ rc = f11_write_control_regs(fn, &f11->sens_query,
+ &f11->dev_controls, fn->fd.query_base_addr);
+ if (rc < 0)
+ return rc;
+
+ return 0;
+}
+
+static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits)
+{
+ struct rmi_device *rmi_dev = fn->rmi_dev;
+ struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
+ struct f11_data *f11 = dev_get_drvdata(&fn->dev);
+ u16 data_base_addr = fn->fd.data_base_addr;
+ u16 data_base_addr_offset = 0;
+ int error;
+
+ if (rmi_dev->xport->attn_data) {
+ memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data,
+ f11->sensor.attn_size);
+ rmi_dev->xport->attn_data += f11->sensor.attn_size;
+ rmi_dev->xport->attn_size -= f11->sensor.attn_size;
+ } else {
+ error = rmi_read_block(rmi_dev,
+ data_base_addr + data_base_addr_offset,
+ f11->sensor.data_pkt,
+ f11->sensor.pkt_size);
+ if (error < 0)
+ return error;
+ }
+
+ rmi_f11_finger_handler(f11, &f11->sensor, irq_bits,
+ drvdata->num_of_irq_regs);
+ data_base_addr_offset += f11->sensor.pkt_size;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int rmi_f11_resume(struct device *dev)
+{
+ struct rmi_function *fn = to_rmi_function(dev);
+ struct f11_data *f11 = dev_get_drvdata(&fn->dev);
+ int error;
+
+ dev_dbg(&fn->dev, "Resuming...\n");
+ if (!f11->rezero_wait_ms)
+ return 0;
+
+ mdelay(f11->rezero_wait_ms);
+
+ error = rmi_write(fn->rmi_dev, fn->fd.command_base_addr,
+ RMI_F11_REZERO);
+ if (error) {
+ dev_err(&fn->dev,
+ "%s: failed to issue rezero command, error = %d.",
+ __func__, error);
+ return error;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(rmi_f11_pm_ops, NULL, rmi_f11_resume);
+
+static int rmi_f11_probe(struct rmi_function *fn)
+{
+ int error;
+ struct f11_data *f11;
+
+ error = rmi_f11_initialize(fn);
+ if (error)
+ return error;
+
+ f11 = dev_get_drvdata(&fn->dev);
+ error = rmi_2d_sensor_configure_input(fn, &f11->sensor);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static struct rmi_function_handler rmi_f11_handler = {
+ .driver = {
+ .name = "rmi_f11",
+ .pm = &rmi_f11_pm_ops,
+ },
+ .func = 0x11,
+ .probe = rmi_f11_probe,
+ .config = rmi_f11_config,
+ .attention = rmi_f11_attention,
+};
+
+int __init rmi_register_f11_handler(void)
+{
+ return rmi_register_function_handler(&rmi_f11_handler);
+}
+
+void rmi_unregister_f11_handler(void)
+{
+ rmi_unregister_function_handler(&rmi_f11_handler);
+}
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index c0429be..ca71ce6 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -20,6 +20,88 @@
#define NAME_BUFFER_SIZE 256
/**
+ * struct rmi_2d_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_2d_axis_alignment {
+ bool swap_axes;
+ bool flip_x;
+ bool flip_y;
+ u16 clip_x_low;
+ u16 clip_y_low;
+ u16 clip_x_high;
+ u16 clip_y_high;
+ u16 offset_x;
+ u16 offset_y;
+ u8 delta_x_threshold;
+ u8 delta_y_threshold;
+};
+
+/** This is used to override any hints an F11 2D sensor might have provided
+ * as to what type of sensor it is.
+ *
+ * @rmi_f11_sensor_default - do not override, determine from F11_2D_QUERY14 if
+ * available.
+ * @rmi_f11_sensor_touchscreen - treat the sensor as a touchscreen (direct
+ * pointing).
+ * @rmi_f11_sensor_touchpad - thread the sensor as a touchpad (indirect
+ * pointing).
+ */
+enum rmi_sensor_type {
+ rmi_sensor_default = 0,
+ rmi_sensor_touchscreen,
+ rmi_sensor_touchpad
+};
+
+#define RMI_F11_DISABLE_ABS_REPORT BIT(0)
+
+/**
+ * struct rmi_2d_sensor_data - overrides defaults for a 2D sensor.
+ * @axis_align - provides axis alignment overrides (see above).
+ * @sensor_type - Forces the driver to treat the sensor as an indirect
+ * pointing device (touchpad) rather than a direct pointing device
+ * (touchscreen). This is useful when F11_2D_QUERY14 register is not
+ * available.
+ * @disable_report_mask - Force data to not be reported even if it is supported
+ * by the firware.
+ * @topbuttonpad - Used with the "5 buttons touchpads" found on the Lenovo 40
+ * series
+ * @kernel_tracking - most moderns RMI f11 firmwares implement Multifinger
+ * Type B protocol. However, there are some corner cases where the user
+ * triggers some jumps by tapping with two fingers on the touchpad.
+ * Use this setting and dmax to filter out these jumps.
+ * Also, when using an old sensor using MF Type A behavior, set to true to
+ * report an actual MT protocol B.
+ * @dmax - the maximum distance (in sensor units) the kernel tracking allows two
+ * distincts fingers to be considered the same.
+ */
+struct rmi_2d_sensor_platform_data {
+ struct rmi_2d_axis_alignment axis_align;
+ enum rmi_sensor_type sensor_type;
+ int x_mm;
+ int y_mm;
+ int disable_report_mask;
+ u16 rezero_wait;
+ bool topbuttonpad;
+ bool kernel_tracking;
+ int dmax;
+};
+
+/**
* struct rmi_f01_power - override default power management settings.
*
*/
@@ -83,6 +165,7 @@ struct rmi_device_platform_data {
int reset_delay_ms;
/* function handler pdata */
+ struct rmi_2d_sensor_platform_data *sensor_pdata;
struct rmi_f01_power_management power_management;
#ifdef CONFIG_PM
--
2.5.0
Hi Andrew,
On Nov 25 2015 or thereabouts, Andrew Duggan wrote:
> This is a new patch series which squashes all of the development
> history of the RMI4 driver into patches based on functionality. The
> first patch adds the core RMI4 functionality needed by all RMI4 devices
> and then the additional patches add transport and function drivers for
> supporting various devices.
Thanks for this hard work. I did not do a full review of the series yet,
but caught some general design questions that I am writing here.
>
> Touchpads which are currently using hid-rmi should have the same
> functionality, but now knowledge of RMI is handled in the core instead
I tried applying the series on top of a 4.4-rc2, and git am failed. I
think it's good to keep this one in the series, but we might want to
postpone its application when rmi4 hits Linus' tree so Jiri will be able
to take it without conflicting with Dmitry's tree. Well, Jiri and Dmitry
can sort this out :)
> of in hid-rmi. These patches also provide basic finger reporting for a
> wide range of RMI4 touchscreens connected to I2C and SPI busses.
> However, additional work may need to be done to implement product specific
> features.
>
> I tried to include all of the feedback I received from the previous
> patches I posted. However, I did not come up with a satisfactory solution
> for allowing function drivers to be built as modules. I think it is fine
> to allow function drivers to be enabled or disabled in the core at build
I agree. I do not like the implementation however (I know, I contributed
a lot to it). How about we keep a static array of struct
rmi_function_handler? If we add a .registered file in struct
rmi_function_handler, we could simplify the registering/unregistering of
the functions more easily by looping through the array.
> time. However, if supporting function drivers as modules is a must have
> for upstreaming I can continue to try to find a solution. Also, I went
> ahead and removed support for polling.
Thanks for removing polling.
I have 2 other general concerns for rmi_bus:
- interrupts:
the current implementation has 2 type of interrupt handling depending on
the transport driver: either generic irqs or specific ones that are
triggered by the transport driver.
I think it would make sense to remove the generic irq handling in rmi4_bus
and let the transport driver handle it. This way, it will be easier for
the transport driver to decide whether or not forwarding the interrupts to
the bus.
This is what is done with the HID bus for the record. The transport
driver calls hid_input_report() when an irq has been processed.
- suspend/resume:
with the smbus implementation, we have quite some troubles with the
suspend/resume part. The reason is that both the rmi_bus and the serio
bus are registering to the pm subsystem and this leads to races between
them. Our current solution is to disable the pm registering in rmi_bus
and let the transport driver handle it at his level (which is in fine
triggered by the serio pm resume actually, not its own pm registration).
I wonder if having the pm functions directly in the bus is a good idea
and if we should not let the transport driver handle those. We should
still keep the rmi specific functions in rmi_core/bus, I am just talking
about the registration of the bus to the subsystem.
[just thinking out loud: maybe we encounter problems of ordering due to
the fact that the bus is registered to the pm subsystem. Maybe if the
device itself in the transport driver registers, there will be guarantee
that the serio resume is called before the rmi_smbus one, which would
solve our problems].
Cheers,
Benjamin
>
> Thanks,
> Andrew
>
> Andrew Duggan (10):
> Input: synaptics-rmi4: Add support for Synaptics RMI4 devices
> Input: synaptics-rmi4: Add I2C transport driver
> Input: synaptics-rmi4: Add device tree support for RMI4 I2C devices
> Input: synaptics-rmi4: Add support for 2D sensors and F11
> Input: synaptics-rmi4: Add device tree support for 2d sensors and F11
> Input: synaptics-rmi4: Add support for F12
> Input: synaptics-rmi4: Add support for F30
> Input: synaptics-rmi4: Add SPI transport driver
> Input: synaptics-rmi4: Add device tree support to the SPI transport
> driver
> HID: rmi: Make hid-rmi a transport driver for synaptics-rmi4
>
> .../bindings/input/rmi4/rmi_2d_sensor.txt | 55 +
> .../devicetree/bindings/input/rmi4/rmi_f01.txt | 40 +
> .../devicetree/bindings/input/rmi4/rmi_i2c.txt | 53 +
> .../devicetree/bindings/input/rmi4/rmi_spi.txt | 57 +
> .../devicetree/bindings/vendor-prefixes.txt | 1 +
> drivers/hid/hid-rmi.c | 922 ++-----------
> drivers/input/Kconfig | 2 +
> drivers/input/Makefile | 2 +
> drivers/input/rmi4/Kconfig | 94 ++
> drivers/input/rmi4/Makefile | 15 +
> drivers/input/rmi4/rmi_2d_sensor.c | 323 +++++
> drivers/input/rmi4/rmi_2d_sensor.h | 88 ++
> drivers/input/rmi4/rmi_bus.c | 419 ++++++
> drivers/input/rmi4/rmi_bus.h | 195 +++
> drivers/input/rmi4/rmi_driver.c | 1112 ++++++++++++++++
> drivers/input/rmi4/rmi_driver.h | 126 ++
> drivers/input/rmi4/rmi_f01.c | 570 ++++++++
> drivers/input/rmi4/rmi_f11.c | 1354 ++++++++++++++++++++
> drivers/input/rmi4/rmi_f12.c | 487 +++++++
> drivers/input/rmi4/rmi_f30.c | 419 ++++++
> drivers/input/rmi4/rmi_i2c.c | 270 ++++
> drivers/input/rmi4/rmi_spi.c | 464 +++++++
> include/linux/rmi.h | 415 ++++++
> include/uapi/linux/input.h | 1 +
> 24 files changed, 6638 insertions(+), 846 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt
> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt
> create mode 100644 drivers/input/rmi4/Kconfig
> create mode 100644 drivers/input/rmi4/Makefile
> create mode 100644 drivers/input/rmi4/rmi_2d_sensor.c
> create mode 100644 drivers/input/rmi4/rmi_2d_sensor.h
> create mode 100644 drivers/input/rmi4/rmi_bus.c
> create mode 100644 drivers/input/rmi4/rmi_bus.h
> create mode 100644 drivers/input/rmi4/rmi_driver.c
> create mode 100644 drivers/input/rmi4/rmi_driver.h
> create mode 100644 drivers/input/rmi4/rmi_f01.c
> create mode 100644 drivers/input/rmi4/rmi_f11.c
> create mode 100644 drivers/input/rmi4/rmi_f12.c
> create mode 100644 drivers/input/rmi4/rmi_f30.c
> create mode 100644 drivers/input/rmi4/rmi_i2c.c
> create mode 100644 drivers/input/rmi4/rmi_spi.c
> create mode 100644 include/linux/rmi.h
>
> --
> 2.5.0
>
On Wed, Nov 25, 2015 at 04:07:21PM -0800, Andrew Duggan wrote:
> Add devicetree binding for I2C devices and add bindings for optional
> parameters in the function drivers. Parameters for function drivers are
> defined in child nodes for each of the functions.
>
> Signed-off-by: Andrew Duggan <[email protected]>
> ---
> .../devicetree/bindings/input/rmi4/rmi_f01.txt | 39 ++++++++++++
> .../devicetree/bindings/input/rmi4/rmi_i2c.txt | 53 ++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.txt | 1 +
> drivers/input/rmi4/rmi_bus.c | 71 ++++++++++++++++++++++
> drivers/input/rmi4/rmi_driver.c | 28 +++++++++
> drivers/input/rmi4/rmi_i2c.c | 12 +++-
> 6 files changed, 203 insertions(+), 1 deletion(-)
> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
>
> diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
> new file mode 100644
> index 0000000..df34dd5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
> @@ -0,0 +1,39 @@
> +Synaptics RMI4 F01 Device Binding
> +
> +The Synaptics RMI4 core is able to support RMI4 devices using differnet
> +transports and differnet functions. This file describes the device tree
s/differnet/different/
> +bindings for devices which contain Function 1. Complete documentation
> +for transports and other functions can be found in:
> +Documentation/devicetree/bindings/input/rmi4.
> +
> +Additional documentation for F01 can be found at:
> +http://www.synaptics.com/sites/default/files/511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf
> +
> +Optional Properties:
> +- syna,nosleep-mode: If set the device will run at full power without sleeping.
> + nosleep has 3 modes, 0 will not change the default
> + setting, 1 will disable nosleep (allow sleeping),
> + and 2 will enable nosleep (disabling sleep).
> +- syna,wakeup-threshold: Defines the amplitude of the disturbance to the
> + background capacitance that will cause the
> + device to wake from dozing.
> +- syna,doze-holdoff: The delay to wait after the last finger lift and the
> + first doze cycle (in 0.1 second units).
> +- syna,doze-interval: The time period that the device sleeps between finger
> + activity (in 10 ms units).
Use millisec units and -msec suffix.
> +
> +
> +Example of a RMI4 I2C device with F01:
> + Example:
> + &i2c1 {
> + rmi-i2c-dev@2c {
> + compatible = "syna,rmi-i2c";
> +
> + ...
> +
> + rmi-f01@1 {
> + reg = <0x1>;
> + syna,nosleep-mode = <1>;
> + };
> + };
> + };
> diff --git a/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt b/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
> new file mode 100644
> index 0000000..0f4a8e1
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
> @@ -0,0 +1,53 @@
> +Synaptics RMI4 I2C Device Binding
> +
> +The Synaptics RMI4 core is able to support RMI4 devices using differnet
> +transports and differnet functions. This file describes the device tree
s/differnet/different/
> +bindings for devices using the I2C tranport driver. Complete documentation
> +for other transports and functions cen be found ini
> +Documentation/devicetree/bindings/input/rmi4.
> +
> +Required Properties:
> +- compatible: syna,rmi-i2c
This is not very specific. Perhaps at least "rmi4" instead of just rmi.
> +- reg: I2C address
> +- #address-cells: Set to 1 to indicate that the function child nodes
> + consist of only on uint32 value.
> +- #size-cells: Set to 0 to indicate that the function child nodes do not
> + have a size property.
> +
> +Optional Properties:
> +- interrupts: interrupt which the rmi device is connected to.
> +- interrupt-parent: The interrupt controller.
> +See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
> +
> +- syna,reset-delay-ms: The number of milliseconds to wait after resetting the
"-msec" is the more commonly used abreviation in bindings.
> + device.
> +
> +Function Parameters:
> +Parameters specific to RMI functions are contained in child nodes of the rmi device
> + node. Documentation for the parameters of each function can be found in:
> +Documentation/devicetree/bindings/input/rmi4/rmi_f*.txt.
> +
> +
> +
> +Example:
> + &i2c1 {
> + rmi-i2c-dev@2c {
> + compatible = "syna,rmi-i2c";
> + reg = <0x2c>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> + interrupt-parent = <&gpio>;
> + interrupts = <4 2>;
> +
> + rmi-f01@1 {
> + reg = <0x1>;
> + syna,nosleep-mode = <1>;
> + };
> +
> + rmi-f11@11 {
> + reg = <0x11>;
> + syna,flip-y;
> + syna,sensor-type = <2>;
> + };
> + };
> + };
On 11/26/2015 02:41 AM, Benjamin Tissoires wrote:
> Hi Andrew,
>
> On Nov 25 2015 or thereabouts, Andrew Duggan wrote:
>> This is a new patch series which squashes all of the development
>> history of the RMI4 driver into patches based on functionality. The
>> first patch adds the core RMI4 functionality needed by all RMI4 devices
>> and then the additional patches add transport and function drivers for
>> supporting various devices.
> Thanks for this hard work. I did not do a full review of the series yet,
> but caught some general design questions that I am writing here.
>
>> Touchpads which are currently using hid-rmi should have the same
>> functionality, but now knowledge of RMI is handled in the core instead
> I tried applying the series on top of a 4.4-rc2, and git am failed. I
> think it's good to keep this one in the series, but we might want to
> postpone its application when rmi4 hits Linus' tree so Jiri will be able
> to take it without conflicting with Dmitry's tree. Well, Jiri and Dmitry
> can sort this out :)
I forgot there were in changes to hid-rmi in 4.4. I can fix it up to
work with whatever version the core ends up being applied to.
>> of in hid-rmi. These patches also provide basic finger reporting for a
>> wide range of RMI4 touchscreens connected to I2C and SPI busses.
>> However, additional work may need to be done to implement product specific
>> features.
>>
>> I tried to include all of the feedback I received from the previous
>> patches I posted. However, I did not come up with a satisfactory solution
>> for allowing function drivers to be built as modules. I think it is fine
>> to allow function drivers to be enabled or disabled in the core at build
> I agree. I do not like the implementation however (I know, I contributed
> a lot to it). How about we keep a static array of struct
> rmi_function_handler? If we add a .registered file in struct
> rmi_function_handler, we could simplify the registering/unregistering of
> the functions more easily by looping through the array.
Sounds like a good idea. Right you it takes several lines in multiple
files to register and unregister function drivers and its really easy to
forget something.
>> time. However, if supporting function drivers as modules is a must have
>> for upstreaming I can continue to try to find a solution. Also, I went
>> ahead and removed support for polling.
> Thanks for removing polling.
>
> I have 2 other general concerns for rmi_bus:
> - interrupts:
> the current implementation has 2 type of interrupt handling depending on
> the transport driver: either generic irqs or specific ones that are
> triggered by the transport driver.
>
> I think it would make sense to remove the generic irq handling in rmi4_bus
> and let the transport driver handle it. This way, it will be easier for
> the transport driver to decide whether or not forwarding the interrupts to
> the bus.
>
> This is what is done with the HID bus for the record. The transport
> driver calls hid_input_report() when an irq has been processed.
The driver was handling the IRQs originally to avoid duplicating a lot
of code in each of the transport devices. But, now that the custom GPIO
code has been removed the reasoning for handling it in rmi_driver is
less compelling. Plus, now we have SMBus and HID transports which don't
handle the IRQs directly. It might also be better to enable and disable
IRQs in the transport drivers pm callbacks.
> - suspend/resume:
> with the smbus implementation, we have quite some troubles with the
> suspend/resume part. The reason is that both the rmi_bus and the serio
> bus are registering to the pm subsystem and this leads to races between
> them. Our current solution is to disable the pm registering in rmi_bus
> and let the transport driver handle it at his level (which is in fine
> triggered by the serio pm resume actually, not its own pm registration).
The interaction between the SMBus and the PS/2 interface does complicate
things. I have not noticed any similar issues with PS/2 + HID/I2C.
Probably, because PS/2 + HID/I2C was designed to work without any driver
interaction.
> I wonder if having the pm functions directly in the bus is a good idea
> and if we should not let the transport driver handle those. We should
> still keep the rmi specific functions in rmi_core/bus, I am just talking
> about the registration of the bus to the subsystem.
I guess we could just call rmi_driver_suspend/resume() from the
transports and have it loop through all of the function drivers and call
their suspend/resume functions. So far it has just made sense to use the
pm subsystem instead of doing things manually. But, none of the other
transports have dependencies on devices in separate driver stacks.
> [just thinking out loud: maybe we encounter problems of ordering due to
> the fact that the bus is registered to the pm subsystem. Maybe if the
> device itself in the transport driver registers, there will be guarantee
> that the serio resume is called before the rmi_smbus one, which would
> solve our problems].
Well if rmi_smbus can register itself with the pm subsystem, could we
just make the transport device the parent of the bus device? Would that
ensure that all of the pm callbacks get called together? Sounds like
some more investigating is needed for figuring out how best to handle
suspend/resume.
Thanks,
Andrew
> Cheers,
> Benjamin
>
>> Thanks,
>> Andrew
>>
>> Andrew Duggan (10):
>> Input: synaptics-rmi4: Add support for Synaptics RMI4 devices
>> Input: synaptics-rmi4: Add I2C transport driver
>> Input: synaptics-rmi4: Add device tree support for RMI4 I2C devices
>> Input: synaptics-rmi4: Add support for 2D sensors and F11
>> Input: synaptics-rmi4: Add device tree support for 2d sensors and F11
>> Input: synaptics-rmi4: Add support for F12
>> Input: synaptics-rmi4: Add support for F30
>> Input: synaptics-rmi4: Add SPI transport driver
>> Input: synaptics-rmi4: Add device tree support to the SPI transport
>> driver
>> HID: rmi: Make hid-rmi a transport driver for synaptics-rmi4
>>
>> .../bindings/input/rmi4/rmi_2d_sensor.txt | 55 +
>> .../devicetree/bindings/input/rmi4/rmi_f01.txt | 40 +
>> .../devicetree/bindings/input/rmi4/rmi_i2c.txt | 53 +
>> .../devicetree/bindings/input/rmi4/rmi_spi.txt | 57 +
>> .../devicetree/bindings/vendor-prefixes.txt | 1 +
>> drivers/hid/hid-rmi.c | 922 ++-----------
>> drivers/input/Kconfig | 2 +
>> drivers/input/Makefile | 2 +
>> drivers/input/rmi4/Kconfig | 94 ++
>> drivers/input/rmi4/Makefile | 15 +
>> drivers/input/rmi4/rmi_2d_sensor.c | 323 +++++
>> drivers/input/rmi4/rmi_2d_sensor.h | 88 ++
>> drivers/input/rmi4/rmi_bus.c | 419 ++++++
>> drivers/input/rmi4/rmi_bus.h | 195 +++
>> drivers/input/rmi4/rmi_driver.c | 1112 ++++++++++++++++
>> drivers/input/rmi4/rmi_driver.h | 126 ++
>> drivers/input/rmi4/rmi_f01.c | 570 ++++++++
>> drivers/input/rmi4/rmi_f11.c | 1354 ++++++++++++++++++++
>> drivers/input/rmi4/rmi_f12.c | 487 +++++++
>> drivers/input/rmi4/rmi_f30.c | 419 ++++++
>> drivers/input/rmi4/rmi_i2c.c | 270 ++++
>> drivers/input/rmi4/rmi_spi.c | 464 +++++++
>> include/linux/rmi.h | 415 ++++++
>> include/uapi/linux/input.h | 1 +
>> 24 files changed, 6638 insertions(+), 846 deletions(-)
>> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_2d_sensor.txt
>> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_f01.txt
>> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_i2c.txt
>> create mode 100644 Documentation/devicetree/bindings/input/rmi4/rmi_spi.txt
>> create mode 100644 drivers/input/rmi4/Kconfig
>> create mode 100644 drivers/input/rmi4/Makefile
>> create mode 100644 drivers/input/rmi4/rmi_2d_sensor.c
>> create mode 100644 drivers/input/rmi4/rmi_2d_sensor.h
>> create mode 100644 drivers/input/rmi4/rmi_bus.c
>> create mode 100644 drivers/input/rmi4/rmi_bus.h
>> create mode 100644 drivers/input/rmi4/rmi_driver.c
>> create mode 100644 drivers/input/rmi4/rmi_driver.h
>> create mode 100644 drivers/input/rmi4/rmi_f01.c
>> create mode 100644 drivers/input/rmi4/rmi_f11.c
>> create mode 100644 drivers/input/rmi4/rmi_f12.c
>> create mode 100644 drivers/input/rmi4/rmi_f30.c
>> create mode 100644 drivers/input/rmi4/rmi_i2c.c
>> create mode 100644 drivers/input/rmi4/rmi_spi.c
>> create mode 100644 include/linux/rmi.h
>>
>> --
>> 2.5.0
>>
On Thu, Nov 26, 2015 at 1:07 AM, Andrew Duggan <[email protected]> wrote:
> I tried to include all of the feedback I received from the previous
> patches I posted. However, I did not come up with a satisfactory solution
> for allowing function drivers to be built as modules. I think it is fine
> to allow function drivers to be enabled or disabled in the core at build
> time.
I am happy with this. If people need something else they can put
in the work, I'm just happy to have my hardware working.
A large slew of the systems using this will be Androids with
tailored kernels anyway, they will all build this in.
Yours,
Linus Walleij