summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDmitry Torokhov <dmitry.torokhov@gmail.com>2016-03-11 09:51:25 -0800
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2016-03-11 09:51:25 -0800
commit62d5bdf972ebcfc99f72f734ae979713e4ca6450 (patch)
tree690d4210d454808196c32efca408b46ffec9a5d3 /drivers
parente52d83986c5a0b843e45aa89b84d224ba7b9b8ca (diff)
parent48147b9768b83265bf2e1211bcadeca035011380 (diff)
Merge branch 'synaptics-rmi4' into next
Bring in support for devices using Synaptics RMI4 protocol, including RMI4 bus, 2D sensor and button handlers, and SPI and I2C interface drivers.
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/Kconfig2
-rw-r--r--drivers/input/Makefile2
-rw-r--r--drivers/input/rmi4/Kconfig63
-rw-r--r--drivers/input/rmi4/Makefile13
-rw-r--r--drivers/input/rmi4/rmi_2d_sensor.c329
-rw-r--r--drivers/input/rmi4/rmi_2d_sensor.h87
-rw-r--r--drivers/input/rmi4/rmi_bus.c419
-rw-r--r--drivers/input/rmi4/rmi_bus.h182
-rw-r--r--drivers/input/rmi4/rmi_driver.c1055
-rw-r--r--drivers/input/rmi4/rmi_driver.h105
-rw-r--r--drivers/input/rmi4/rmi_f01.c624
-rw-r--r--drivers/input/rmi4/rmi_f11.c1317
-rw-r--r--drivers/input/rmi4/rmi_f12.c457
-rw-r--r--drivers/input/rmi4/rmi_f30.c407
-rw-r--r--drivers/input/rmi4/rmi_i2c.c397
-rw-r--r--drivers/input/rmi4/rmi_spi.c589
16 files changed, 6048 insertions, 0 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index a35532ec00e4..6261874c07c9 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 0c9302ca9954..595820bbabe9 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 000000000000..f73df2495fed
--- /dev/null
+++ b/drivers/input/rmi4/Kconfig
@@ -0,0 +1,63 @@
+#
+# 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.
+
+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.
+
+config RMI4_SPI
+ tristate "RMI4 SPI Support"
+ depends on RMI4_CORE && SPI
+ help
+ Say Y here if you want to support RMI4 devices connected to a SPI
+ bus.
+
+ If unsure, say N.
+
+config RMI4_2D_SENSOR
+ bool
+ depends on RMI4_CORE
+
+config RMI4_F11
+ bool "RMI4 Function 11 (2D pointing)"
+ select RMI4_2D_SENSOR
+ depends on RMI4_CORE
+ 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_F12
+ bool "RMI4 Function 12 (2D pointing)"
+ select RMI4_2D_SENSOR
+ depends on RMI4_CORE
+ help
+ Say Y here if you want to add support for RMI4 function 12.
+
+ Function 12 provides 2D multifinger pointing for touchscreens and
+ touchpads. For sensors that support relative pointing, F12 also
+ provides mouse input.
+
+config RMI4_F30
+ bool "RMI4 Function 30 (GPIO LED)"
+ depends on RMI4_CORE
+ help
+ Say Y here if you want to add support for RMI4 function 30.
+
+ Function 30 provides GPIO and LED support for RMI4 devices. This
+ includes support for buttons on TouchPads and ClickPads.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
new file mode 100644
index 000000000000..95c00a783992
--- /dev/null
+++ b/drivers/input/rmi4/Makefile
@@ -0,0 +1,13 @@
+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
+rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
+rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
+
+# Transports
+obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
+obj-$(CONFIG_RMI4_SPI) += rmi_spi.o
diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c
new file mode 100644
index 000000000000..e97bd7fabccc
--- /dev/null
+++ b/drivers/input/rmi4/rmi_2d_sensor.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2011-2016 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);
+
+ rmi_dbg(RMI_DEBUG_2D_SENSOR, &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);
+
+#ifdef CONFIG_OF
+int rmi_2d_sensor_of_probe(struct device *dev,
+ struct rmi_2d_sensor_platform_data *pdata)
+{
+ int retval;
+ u32 val;
+
+ pdata->axis_align.swap_axes = of_property_read_bool(dev->of_node,
+ "touchscreen-swapped-x-y");
+
+ pdata->axis_align.flip_x = of_property_read_bool(dev->of_node,
+ "touchscreen-inverted-x");
+
+ pdata->axis_align.flip_y = of_property_read_bool(dev->of_node,
+ "touchscreen-inverted-y");
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-low", 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.clip_x_low = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-low", 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.clip_y_low = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-high", 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.clip_x_high = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-high", 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.clip_y_high = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,offset-x", 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.offset_x = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,offset-y", 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.offset_y = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,delta-x-threshold",
+ 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.delta_x_threshold = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,delta-y-threshold",
+ 1);
+ if (retval)
+ return retval;
+
+ pdata->axis_align.delta_y_threshold = val;
+
+ retval = rmi_of_property_read_u32(dev, (u32 *)&pdata->sensor_type,
+ "syna,sensor-type", 1);
+ if (retval)
+ return retval;
+
+ retval = rmi_of_property_read_u32(dev, &val, "touchscreen-x-mm", 1);
+ if (retval)
+ return retval;
+
+ pdata->x_mm = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "touchscreen-y-mm", 1);
+ if (retval)
+ return retval;
+
+ pdata->y_mm = val;
+
+ retval = rmi_of_property_read_u32(dev, &val,
+ "syna,disable-report-mask", 1);
+ if (retval)
+ return retval;
+
+ pdata->disable_report_mask = val;
+
+ retval = rmi_of_property_read_u32(dev, &val, "syna,rezero-wait-ms",
+ 1);
+ if (retval)
+ return retval;
+
+ pdata->rezero_wait = val;
+
+ return 0;
+}
+#else
+inline int rmi_2d_sensor_of_probe(struct device *dev,
+ struct rmi_2d_sensor_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif
+EXPORT_SYMBOL_GPL(rmi_2d_sensor_of_probe);
diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h
new file mode 100644
index 000000000000..77fcdfef003c
--- /dev/null
+++ b/drivers/input/rmi4/rmi_2d_sensor.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2011-2016 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;
+ struct rmi_function *fn;
+ char input_phys[32];
+ u8 report_abs;
+ u8 report_rel;
+ u8 x_mm;
+ u8 y_mm;
+};
+
+int rmi_2d_sensor_of_probe(struct device *dev,
+ struct rmi_2d_sensor_platform_data *pdata);
+
+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
new file mode 100644
index 000000000000..b368b0515c5a
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2011-2016 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"
+
+static int debug_flags;
+module_param(debug_flags, int, 0644);
+MODULE_PARM_DESC(debug_flags, "control debugging information");
+
+void rmi_dbg(int flags, struct device *dev, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ if (flags & debug_flags) {
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ dev_printk(KERN_DEBUG, dev, "%pV", &vaf);
+
+ va_end(args);
+ }
+}
+EXPORT_SYMBOL_GPL(rmi_dbg);
+
+/*
+ * 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 = "rmi4_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, "rmi4-%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;
+
+ rmi_dbg(RMI_DEBUG_CORE, 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 = "rmi4_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;
+}
+
+#ifdef CONFIG_OF
+static void rmi_function_of_probe(struct rmi_function *fn)
+{
+ char of_name[9];
+
+ snprintf(of_name, sizeof(of_name), "rmi4-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);
+ struct rmi_function_handler *handler =
+ to_rmi_function_handler(dev->driver);
+ int error;
+
+ rmi_function_of_probe(fn);
+
+ 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;
+ }
+
+ rmi_dbg(RMI_DEBUG_CORE, &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 = "rmi4",
+};
+
+static struct rmi_function_handler *fn_handlers[] = {
+ &rmi_f01_handler,
+#ifdef CONFIG_RMI4_F11
+ &rmi_f11_handler,
+#endif
+#ifdef CONFIG_RMI4_F12
+ &rmi_f12_handler,
+#endif
+#ifdef CONFIG_RMI4_F30
+ &rmi_f30_handler,
+#endif
+};
+
+static void __rmi_unregister_function_handlers(int start_idx)
+{
+ int i;
+
+ for (i = start_idx; i >= 0; i--)
+ rmi_unregister_function_handler(fn_handlers[i]);
+}
+
+static void rmi_unregister_function_handlers(void)
+{
+ __rmi_unregister_function_handlers(ARRAY_SIZE(fn_handlers) - 1);
+}
+
+static int rmi_register_function_handlers(void)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(fn_handlers); i++) {
+ ret = rmi_register_function_handler(fn_handlers[i]);
+ if (ret) {
+ pr_err("%s: error registering the RMI F%02x handler: %d\n",
+ __func__, fn_handlers[i]->func, ret);
+ goto err_unregister_function_handlers;
+ }
+ }
+
+ return 0;
+
+err_unregister_function_handlers:
+ __rmi_unregister_function_handlers(i - 1);
+ return ret;
+}
+
+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);
+
+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_function_handlers();
+ if (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_bus;
+ }
+
+ return 0;
+
+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_function_handlers();
+ bus_unregister(&rmi_bus_type);
+}
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com");
+MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com");
+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 000000000000..899579830536
--- /dev/null
+++ b/drivers/input/rmi4/rmi_bus.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2011-2016 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 convenience, can be used to mask IRQ bits off during ATTN
+ * interrupt handling.
+ *
+ * @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 *f