diff mbox

[7/9] input/touchscreen: Synaptics RMI4 Touchscreen Driver

Message ID 1309497556-7344-8-git-send-email-cheiny@synaptics.com (mailing list archive)
State New, archived
Headers show

Commit Message

Christopher Heiny July 1, 2011, 5:19 a.m. UTC
Driver for Synaptics touchscreens using RMI4 protocol.

Please see the email 0/9 for a description of this patch.

Signed-off-by: Christopher Heiny <cheiny@synaptics.com>
Signed-off-by: William Manson <wmanson@synaptics.com>
Signed-off-by: Allie Xiong <axiong@synaptics.com>
Signed-off-by: Peichen Chang <peichen.chang@synaptics.com>

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

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

---

--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/input/touchscreen/rmi_f11.h b/drivers/input/touchscreen/rmi_f11.h
new file mode 100644
index 0000000..7750ed4
--- /dev/null
+++ b/drivers/input/touchscreen/rmi_f11.h
@@ -0,0 +1,80 @@ 
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 header.
+ * Copyright (c) 2007 - 2010, Synaptics Incorporated
+ *
+ * For every RMI4 function that has a data source - like 2D sensors,
+ * buttons, LEDs, GPIOs, etc. - the user will create a new rmi_function_xx.c
+ * file and add these functions to perform the config(), init(), report()
+ * and detect() functionality. The function pointers are then srored under
+ * the RMI function info and these functions will automatically be called by
+ * the global config(), init(), report() and detect() functions that will
+ * loop through all data sources and call the data sources functions using
+ * these functions pointed to by the function ptrs.
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#if !defined(_RMI_F11_H)
+#define _RMI_F11_H
+
+/* This is the data read from the F11 query registers.
+ */
+struct rmi_F11_device_query {
+	bool has_query_9;
+	unsigned char number_of_sensors;
+};
+
+struct rmi_F11_sensor_query {
+	bool configurable;
+	bool has_sensitivity_adjust;
+	bool has_gestures;
+	bool has_absolute;
+	bool has_relative;
+	unsigned char number_of_fingers;
+	unsigned char number_of_X_electrodes;
+	unsigned char number_of_Y_electrodes;
+	unsigned char maximum_electrodes;
+	bool has_anchored_finger;
+	unsigned char abs_data_size;
+};
+
+struct rmi_F11_control {
+	bool relative_ballistics;
+	bool relative_position_filter;
+	bool absolute_position_filter;
+	unsigned char reporting_mode;
+	bool manually_tracked_finger;
+	bool manually_tracked_finger_enable;
+	unsigned char motion_sensitivity;
+	unsigned char palm_detect_threshold;
+	unsigned char delta_X_pos_threshold;
+	unsigned char delta_Y_pos_threshold;
+	unsigned char velocity;
+	unsigned char acceleration;
+	unsigned short sensor_max_X_pos;
+	unsigned short sensor_max_Y_pos;
+};
+
+void FN_11_inthandler(struct rmi_function_info *rmifninfo,
+		      unsigned int asserted_IRQs);
+int FN_11_config(struct rmi_function_info *rmifninfo);
+int FN_11_init(struct rmi_function_device *function_device);
+int FN_11_detect(struct rmi_function_info *rmifninfo);
+/* No attention function for Fn $11 */
+#endif
diff --git a/drivers/input/touchscreen/rmi_f11.c b/drivers/input/touchscreen/rmi_f11.c
new file mode 100644
index 0000000..0e28cdc
--- /dev/null
+++ b/drivers/input/touchscreen/rmi_f11.c
@@ -0,0 +1,962 @@ 
+/**
+ *
+ * Synaptics Register Mapped Interface (RMI4) Function $11 support for 2D.
+ * Copyright (c) 2007 - 2011, Synaptics Incorporated
+ *
+ */
+/*
+ * This file is licensed under the GPL2 license.
+ *
+ *#############################################################################
+ * GPL
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ *#############################################################################
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "rmi.h"
+#include "rmi_drvr.h"
+#include "rmi_bus.h"
+#include "rmi_sensor.h"
+#include "rmi_function.h"
+#include "rmi_f11.h"
+#include "rmi_platformdata.h"
+
+/* By default, we'll support two fingers if we can't figure out how many we
+ * really need to handle.
+ */
+#define DEFAULT_NR_OF_FINGERS 2
+
+struct f11_instance_data {
+	struct rmi_F11_device_query *device_info;
+	struct rmi_F11_sensor_query *sensor_info;
+	struct rmi_F11_control *control_registers;
+	unsigned char finger_data_buffer_size;
+	unsigned char abs_data_offset;
+	unsigned char abs_data_size;
+	unsigned char rel_data_offset;
+	unsigned char gesture_data_offset;
+	unsigned char *finger_data_buffer;
+	/* Last X & Y seen, needed at finger lift.  Was down indicates
+	 * at least one finger was here. TODO: Eventually we'll need to
+	 * track this info on a per finger basis. */
+	bool wasdown;
+	unsigned int old_X;
+	unsigned int old_Y;
+	/* Transformations to be applied to coordinates before reporting. */
+	bool flip_X;
+	bool flip_Y;
+	int offset_X;
+	int offset_Y;
+	int clip_X_low;
+	int clip_X_high;
+	int clip_Y_low;
+	int clip_Y_high;
+	bool swap_axes;
+	bool rel_report_enabled;
+};
+
+enum f11_finger_state {
+	F11_NO_FINGER = 0,
+	F11_PRESENT = 1,
+	F11_INACCURATE = 2,
+	F11_RESERVED = 3
+};
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+				   struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf);
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+				     struct device_attribute *attr, char *buf);
+
+static struct device_attribute attrs[] = {
+	__ATTR(flip, 0664,
+	       rmi_fn_11_flip_show, rmi_fn_11_flip_store),	/* RW attr */
+	__ATTR(clip, 0664,
+	       rmi_fn_11_clip_show, rmi_fn_11_clip_store),	/* RW attr */
+	__ATTR(offset, 0664,
+	       rmi_fn_11_offset_show, rmi_fn_11_offset_store),	/* RW attr */
+	__ATTR(swap, 0664,
+	       rmi_fn_11_swap_show, rmi_fn_11_swap_store),	/* RW attr */
+	__ATTR(relreport, 0664,
+	       rmi_fn_11_relreport_show, rmi_fn_11_relreport_store),	/* RW */
+	__ATTR(maxPos, 0444,
+	       rmi_fn_11_maxPos_show, rmi_store_error)	/* R0 attr */
+};
+
+static void FN_11_relreport(struct rmi_function_info *rmifninfo);
+
+
+/* Reading and parsing the F11 query registers is a big hairy wad.  There's a
+ * lot of stuff that is dependent on the presence or absence of other stuff,
+ * and there's really no tidy way to deal with it.
+ *
+ * TODO: Use more computed and #def'ed offset values.
+ */
+static int read_query_registers(struct rmi_function_info *rmifninfo)
+{
+	int retval = 0;
+	struct f11_instance_data *instance_data = rmifninfo->fndata;
+	unsigned char query_buffer[12];	/* TODO: Compute size correctly. */
+	unsigned char abs_data_size;
+	int has_pinch, has_flick, has_tap;
+	int has_tap_and_hold, has_double_tap;
+	int has_early_tap, has_press;
+	int has_palm_detect, has_rotate;
+	int has_relative;
+	unsigned char f11_egr_0, f11_egr_1;
+	unsigned int all_data_block_size;
+
+	/* need to get number of fingers supported, data size, etc. -
+	 * to be used when getting data since the number of registers to
+	 * read depends on the number of fingers supported and data size. */
+	retval = rmi_read_multiple(rmifninfo->sensor,
+			  rmifninfo->function_descriptor.query_base_addr,
+			  query_buffer, sizeof(query_buffer));
+	if (retval) {
+		pr_err("%s:Could not read F11 query registers 0x%04x\n",
+			__func__,
+			rmifninfo->function_descriptor.query_base_addr);
+		return retval;
+	}
+
+	/* Extract device data. */
+	instance_data->device_info->has_query_9 = (query_buffer[0] & 0x04) != 0;
+	instance_data->device_info->number_of_sensors =
+	(query_buffer[0] & 0x07) + 1;
+	pr_debug("%s: F11 device - %d sensors.  Query 9? %d.", __func__,
+		 instance_data->device_info->number_of_sensors,
+		 instance_data->device_info->has_query_9);
+
+	/* Extract sensor data. */
+	/* 2D data sources have only 3 bits for the number of fingers
+	 * supported - so the encoding is a bit wierd. */
+	instance_data->sensor_info->number_of_fingers = DEFAULT_NR_OF_FINGERS;
+	if ((query_buffer[1] & 0x7) <= 4)
+		/* add 1 since zero based */
+		instance_data->sensor_info->number_of_fingers =
+		(query_buffer[1] & 0x7) + 1;
+	else {
+		/* a value of 5 is up to 10 fingers - 6 and 7 are reserved
+		 (shouldn't get these in a normal 2D source). */
+		if ((query_buffer[1] & 0x7) == 5)
+			instance_data->sensor_info->number_of_fingers = 10;
+	}
+	instance_data->sensor_info->configurable =
+			(query_buffer[1] & 0x80) != 0;
+	instance_data->sensor_info->has_sensitivity_adjust =
+			(query_buffer[1] & 0x40) != 0;
+	instance_data->sensor_info->has_gestures =
+			(query_buffer[1] & 0x20) != 0;
+	instance_data->sensor_info->has_absolute =
+			(query_buffer[1] & 0x10) != 0;
+	instance_data->sensor_info->has_relative =
+			(query_buffer[1] & 0x08) != 0;
+	instance_data->sensor_info->abs_data_size = query_buffer[5] & 0x03;
+	pr_debug("%s: Number of fingers: %d.", __func__,
+		 instance_data->sensor_info->number_of_fingers);
+
+	/* Figure out just how much data we'll need to read. */
+	instance_data->finger_data_buffer_size =
+			(instance_data->sensor_info->number_of_fingers + 3) / 4;
+	/* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+	abs_data_size = 5;
+	if (instance_data->sensor_info->abs_data_size != 0)
+		pr_warning("%s: Unrecognized abs data size %d ignored.",
+			__func__, instance_data->sensor_info->abs_data_size);
+	if (instance_data->sensor_info->has_absolute) {
+		instance_data->abs_data_size = abs_data_size;
+		instance_data->abs_data_offset =
+			instance_data->finger_data_buffer_size;
+		instance_data->finger_data_buffer_size +=
+			instance_data->sensor_info->number_of_fingers *
+			abs_data_size;
+	}
+	if (instance_data->sensor_info->has_relative) {
+		instance_data->rel_data_offset =
+			((instance_data->sensor_info->number_of_fingers + 3)
+				/ 4) +
+			/* absolute data, per finger times number of fingers */
+			(abs_data_size *
+			instance_data->sensor_info->number_of_fingers);
+		instance_data->finger_data_buffer_size +=
+			instance_data->sensor_info->number_of_fingers * 2;
+	}
+	if (instance_data->sensor_info->has_gestures) {
+		instance_data->gesture_data_offset =
+			instance_data->finger_data_buffer_size;
+		pr_warning("%s: WARNING Need to correctly compute gesture "
+			"data location.", __func__);
+	}
+
+	/* need to determine the size of data to read - this depends on
+	 * conditions such as whether Relative data is reported and if Gesture
+	 * data is reported. */
+	f11_egr_0 = query_buffer[7];
+	f11_egr_1 = query_buffer[8];
+
+	/* Get info about what EGR data is supported, whether it has
+	 * Relative data suppo*rted, etc. */
+	has_pinch = f11_egr_0 & 0x40;
+	has_flick = f11_egr_0 & 0x10;
+	has_tap = f11_egr_0 & 0x01;
+	has_tap_and_hold = f11_egr_0 & 0x02;
+	has_double_tap = f11_egr_0 & 0x04;
+	has_early_tap = f11_egr_0 & 0x08;
+	has_press = f11_egr_0 & 0x20;
+	has_palm_detect = f11_egr_1 & 0x01;
+	has_rotate = f11_egr_1 & 0x02;
+	has_relative = query_buffer[1] & 0x08;
+
+	/* Size of all data including finger status, absolute data for each
+	 * finger, relative data and EGR data */
+	all_data_block_size =
+		/* finger status, four fingers per register */
+		((instance_data->sensor_info->number_of_fingers + 3) / 4) +
+		/* absolute data, per finger times number of fingers */
+		(abs_data_size *
+		instance_data->sensor_info->number_of_fingers) +
+		/* two relative registers (if relative is being reported) */
+		2 * has_relative +
+		/* F11_2D_Data8 is only present if the egr_0
+		 * register is non-zero. */
+		!!(f11_egr_0) +
+		/* F11_2D_Data9 is only present if either egr_0 or
+		 * egr_1 registers are non-zero. */
+		(f11_egr_0 || f11_egr_1) +
+		/* F11_2D_Data10 is only present if EGR_PINCH or EGR_FLICK of
+		 * egr_0 reports as 1. */
+		!!(has_pinch | has_flick) +
+		/* F11_2D_Data11 and F11_2D_Data12 are only present if
+		 * EGR_FLICK of egr_0 reports as 1. */
+		2 * !!(has_flick);
+
+	return 0;
+}
+
+/*
+ * This reads in a sample and reports the function $11 source data to the
+ * input subsystem. It is used for both polling and interrupt driven
+ * operation. This is called a lot so don't put in any informational
+ * printks since they will slow things way down!
+ */
+void FN_11_inthandler(struct rmi_function_info *rmifninfo,
+		      unsigned int asserted_IRQs)
+{
+	/* number of touch points - fingers down in this case */
+	int finger_down_count;
+	int finger;
+	struct rmi_function_device *function_device;
+	struct f11_instance_data *instance_data = rmifninfo->fndata;
+	int retval;
+
+	finger_down_count = 0;
+	function_device = rmifninfo->function_device;
+
+	/* get 2D sensor finger data */
+	retval =
+	    rmi_read_multiple(rmifninfo->sensor,
+			      rmifninfo->function_descriptor.data_base_addr,
+			      instance_data->finger_data_buffer,
+			      instance_data->finger_data_buffer_size);
+	if (retval) {
+		pr_err("%s: Failed to read finger data registers, code=%d.\n",
+		       __func__, retval);
+		return;
+	}
+
+	/* First we need to count the fingers and generate some events
+	 * related to that. */
+	for (finger = 0; finger < instance_data->sensor_info->number_of_fingers;
+	     finger++) {
+		int reg = finger / 4;	/* Which data byte has finger status */
+		int finger_shift = (finger % 4) * 2;	/* Where in the byte? */
+		int finger_status =
+		    (instance_data->
+		     finger_data_buffer[reg] >> finger_shift) & 0x03;
+
+		if (finger_status == F11_PRESENT
+		    || finger_status == F11_INACCURATE) {
+			finger_down_count++;
+			instance_data->wasdown = true;
+		}
+	}
+	input_report_key(function_device->input, BTN_TOUCH, finger_down_count);
+	for (finger = 0;
+	     finger < (instance_data->sensor_info->number_of_fingers - 1);
+	     finger++)
+		input_report_key(function_device->input, BTN_2 + finger,
+				 finger_down_count >= (finger + 2));
+
+	for (finger = 0; finger < instance_data->sensor_info->number_of_fingers;
+	     finger++) {
+		int reg;
+		int finger_shift;
+		int finger_status;
+		int X = 0, Y = 0, Z = 0, Wy = 0, Wx = 0;
+
+		/* determine which data byte the finger status is in */
+		reg = finger / 4;
+		/* bit shift to get finger's status */
+		finger_shift = (finger % 4) * 2;
+		finger_status =
+		    (instance_data->finger_data_buffer[reg] >> finger_shift)
+		    & 0x03;
+
+		/* if finger status indicates a finger is present then
+		   extract the finger data and report it */
+		if (finger_status == F11_PRESENT
+		    || finger_status == F11_INACCURATE) {
+
+			if (instance_data->sensor_info->has_absolute) {
+				int maxX =
+				    instance_data->control_registers->
+				    sensor_max_X_pos;
+				int maxY =
+				    instance_data->control_registers->
+				    sensor_max_Y_pos;
+				reg =
+				    instance_data->abs_data_offset +
+				    (finger * instance_data->abs_data_size);
+				X = (instance_data->
+				     finger_data_buffer[reg] << 4) & 0x0ff0;
+				X |= (instance_data->
+				      finger_data_buffer[reg + 2] & 0x0f);
+				Y = (instance_data->
+				     finger_data_buffer[reg + 1] << 4) & 0x0ff0;
+				Y |= ((instance_data->
+				       finger_data_buffer[reg +
+							2] & 0xf0) >> 4) & 0x0f;
+				/* First thing to do is swap axes if needed.
+				 */
+				if (instance_data->swap_axes) {
+					int temp = X;
+					X = Y;
+					Y = temp;
+					maxX =
+					    instance_data->control_registers->
+					    sensor_max_Y_pos;
+					maxY =
+					    instance_data->control_registers->
+					    sensor_max_X_pos;
+				}
+				if (instance_data->flip_X)
+					X = max(maxX - X, 0);
+				X = X - instance_data->offset_X;
+				X = min(max(X, instance_data->clip_X_low),
+					instance_data->clip_X_high);
+				if (instance_data->flip_Y)
+					Y = max(maxY - Y, 0);
+				Y = Y - instance_data->offset_Y;
+				Y = min(max(Y, instance_data->clip_Y_low),
+					instance_data->clip_Y_high);
+
+				/* upper 4 bits of W are Wy,
+				   lower 4 of W are Wx */
+				Wy = (instance_data->
+				      finger_data_buffer[reg + 3] >> 4) & 0x0f;
+				Wx = instance_data->finger_data_buffer[reg +
+								     3] & 0x0f;
+				if (instance_data->swap_axes) {
+					int temp = Wx;
+					Wx = Wy;
+					Wy = temp;
+				}
+
+				Z = instance_data->finger_data_buffer[reg + 4];
+
+				/* if this is the first finger report normal
+				 * ABS_X, ABS_Y, PRESSURE, TOOL_WIDTH events for
+				 * non-MT apps. Apps that support Multi-touch
+				 * will ignore these events and use the MT
+				 * events. Apps that don't support Multi-touch
+				 * will still function.
+				 */
+				if (finger_down_count == 1) {
+					instance_data->old_X = X;
+					instance_data->old_Y = Y;
+					input_report_abs(function_device->input,
+							 ABS_X, X);
+					input_report_abs(function_device->input,
+							 ABS_Y, Y);
+					input_report_abs(function_device->input,
+							 ABS_PRESSURE, Z);
+					input_report_abs(function_device->input,
+							 ABS_TOOL_WIDTH,
+							 max(Wx, Wy));
+				} else {
+					/* TODO generate non MT events for
+					 * multifinger situation. */
+				}
+#ifdef CONFIG_SYNA_MULTI_TOUCH
+				/* Report Multi-Touch events for each finger */
+				/* major axis of touch area ellipse */
+				input_report_abs(function_device->input,
+						 ABS_MT_TOUCH_MAJOR, Z);
+				/* minor axis of touch area ellipse */
+				input_report_abs(function_device->input,
+						 ABS_MT_WIDTH_MAJOR, max(Wx,
+									 Wy));
+				/* Currently only 2 supported - 1 or 0 */
+				input_report_abs(function_device->input,
+						 ABS_MT_ORIENTATION,
+						 (Wx > Wy ? 1 : 0));
+				input_report_abs(function_device->input,
+						 ABS_MT_POSITION_X, X);
+				input_report_abs(function_device->input,
+						 ABS_MT_POSITION_Y, Y);
+
+				/* TODO: Tracking ID needs to be reported but
+				 * not used yet. Could be formed by keeping
+				 * an id per position and assiging a new id
+				 * when finger_status changes for that position.
+				 */
+				input_report_abs(function_device->input,
+						 ABS_MT_TRACKING_ID,
+						 finger + 1);
+
+				/* MT sync between fingers */
+				input_mt_sync(function_device->input);
+#endif
+			}
+		}
+	}
+
+	/* if we had a finger down before and now we don't have
+	 * any send a button up. */
+	if ((finger_down_count == 0) && instance_data->wasdown) {
+		instance_data->wasdown = false;
+
+#ifdef CONFIG_SYNA_MULTI_TOUCH
+		input_report_abs(function_device->input, ABS_MT_TOUCH_MAJOR, 0);
+		input_report_abs(function_device->input, ABS_MT_WIDTH_MAJOR, 0);
+		input_report_abs(function_device->input, ABS_MT_POSITION_X,
+				 instance_data->old_X);
+		input_report_abs(function_device->input, ABS_MT_POSITION_Y,
+				 instance_data->old_Y);
+		input_report_abs(function_device->input, ABS_MT_TRACKING_ID, 1);
+		input_mt_sync(function_device->input);
+#endif
+
+		input_report_abs(function_device->input, ABS_X,
+				 instance_data->old_X);
+		input_report_abs(function_device->input, ABS_Y,
+				 instance_data->old_Y);
+		instance_data->old_X = instance_data->old_Y = 0;
+		pr_debug("%s: Finger up.", __func__);
+	}
+
+	FN_11_relreport(rmifninfo);
+	input_sync(function_device->input); /* sync after groups of events */
+
+}
+EXPORT_SYMBOL(FN_11_inthandler);
+
+#define F11_MIN_RELATIVE -128
+#define F11_MAX_RELATIVE 127
+
+/* This function reads in relative data for first finger and
+ * sends it to input system */
+static void FN_11_relreport(struct rmi_function_info *rmifninfo)
+{
+	struct f11_instance_data *instance_data = rmifninfo->fndata;
+	struct rmi_function_device *function_device;
+	signed char X, Y;
+	unsigned short fn11DataBaseAddr;
+
+	if (instance_data->sensor_info->has_relative &&
+			instance_data->rel_report_enabled) {
+		int reg = instance_data->rel_data_offset;
+
+		function_device = rmifninfo->function_device;
+
+		fn11DataBaseAddr =
+		    rmifninfo->function_descriptor.data_base_addr;
+		/* Read and report Rel data for primary finger
+		 * one register for X and one for Y */
+		X = instance_data->finger_data_buffer[reg];
+		Y = instance_data->finger_data_buffer[reg + 1];
+		if (instance_data->swap_axes) {
+			signed char temp = X;
+			X = Y;
+			Y = temp;
+		}
+		if (instance_data->flip_X)
+			X = -X;
+		if (instance_data->flip_Y)
+			Y = -Y;
+		X = (signed char)min(F11_MAX_RELATIVE,
+				     max(F11_MIN_RELATIVE, (int)X));
+		Y = (signed char)min(F11_MAX_RELATIVE,
+				     max(F11_MIN_RELATIVE, (int)Y));
+
+		input_report_rel(function_device->input, REL_X, X);
+		input_report_rel(function_device->input, REL_Y, Y);
+	}
+}
+
+/* This is a stub for now, and will be expanded as this implementation
+ * evolves.
+ */
+int FN_11_config(struct rmi_function_info *rmifninfo)
+{
+	int retval = 0;
+
+	pr_debug("%s: RMI4 function $11 config\n", __func__);
+
+	return retval;
+}
+EXPORT_SYMBOL(FN_11_config);
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_device *function_device)
+{
+	struct f11_instance_data *instance_data = function_device->rfi->fndata;
+	/* Use the max X and max Y read from the device, or the clip values,
+	 * whichever is stricter.
+	 */
+	int xMin = instance_data->clip_X_low;
+	int xMax =
+	    min((int)instance_data->control_registers->sensor_max_X_pos,
+		instance_data->clip_X_high);
+	int yMin = instance_data->clip_Y_low;
+	int yMax =
+	    min((int)instance_data->control_registers->sensor_max_Y_pos,
+		instance_data->clip_Y_high);
+	if (instance_data->swap_axes) {
+		int temp = xMin;
+		xMin = yMin;
+		yMin = temp;
+		temp = xMax;
+		xMax = yMax;
+		yMax = temp;
+	}
+	pr_debug("%s: Set ranges X=[%d..%d] Y=[%d..%d].", __func__, xMin, xMax,
+		 yMin, yMax);
+	input_set_abs_params(function_device->input, ABS_X, xMin, xMax, 0, 0);
+	input_set_abs_params(function_device->input, ABS_Y, yMin, yMax, 0, 0);
+	input_set_abs_params(function_device->input, ABS_PRESSURE, 0, 255, 0,
+			     0);
+	input_set_abs_params(function_device->input, ABS_TOOL_WIDTH, 0, 15, 0,
+			     0);
+
+#ifdef CONFIG_SYNA_MULTI_TOUCH
+	input_set_abs_params(function_device->input, ABS_MT_TOUCH_MAJOR, 0, 15,
+			     0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_TOUCH_MINOR, 0, 15,
+			     0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_ORIENTATION, 0, 1,
+			     0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_TRACKING_ID, 1, 10,
+			     0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_POSITION_X, xMin,
+			     xMax, 0, 0);
+	input_set_abs_params(function_device->input, ABS_MT_POSITION_Y, yMin,
+			     yMax, 0, 0);
+#endif
+}
+
+/* Initialize any function $11 specific params and settings - input
+ * settings, device settings, etc.
+ */
+int FN_11_init(struct rmi_function_device *function_device)
+{
+	struct f11_instance_data *instance_data = function_device->rfi->fndata;
+	int retval = 0;
+	int attr_count = 0;
+	struct rmi_f11_functiondata *functiondata =
+		rmi_sensor_get_functiondata(function_device->sensor,
+			RMI_F11_INDEX);
+	pr_debug("%s: RMI4 F11 init", __func__);
+
+	/* TODO: Initialize these through some normal kernel mechanism.
+	 */
+	instance_data->flip_X = false;
+	instance_data->flip_Y = false;
+	instance_data->swap_axes = false;
+	instance_data->rel_report_enabled = true;
+	instance_data->offset_X = instance_data->offset_Y = 0;
+	instance_data->clip_X_low = instance_data->clip_Y_low = 0;
+	/* TODO: 65536 should actually be the largest valid RMI4
+	 * position coordinate */
+	instance_data->clip_X_high = instance_data->clip_Y_high = 65536;
+
+	/* Load any overrides that were specified via platform data.
+	 */
+	if (functiondata) {
+		pr_debug("%s: found F11 per function platformdata.", __func__);
+		instance_data->flip_X = functiondata->flip_X;
+		instance_data->flip_Y = functiondata->flip_Y;
+		instance_data->swap_axes = functiondata->swap_axes;
+		if (functiondata->offset) {
+			instance_data->offset_X = functiondata->offset->x;
+			instance_data->offset_Y = functiondata->offset->y;
+		}
+		if (functiondata->clip_X) {
+			if (functiondata->clip_X->min >=
+			    functiondata->clip_X->max) {
+				pr_warning
+				    ("%s: Clip X min (%d) >= X clip max (%d) "
+				     "- ignored.",
+				     __func__, functiondata->clip_X->min,
+				     functiondata->clip_X->max);
+			} else {
+				instance_data->clip_X_low =
+				    functiondata->clip_X->min;
+				instance_data->clip_X_high =
+				    functiondata->clip_X->max;
+			}
+		}
+		if (functiondata->clip_Y) {
+			if (functiondata->clip_Y->min >=
+			    functiondata->clip_Y->max) {
+				pr_warning
+				    ("%s: Clip Y min (%d) >= Y clip max (%d) "
+				     "- ignored.",
+				     __func__, functiondata->clip_Y->min,
+				     functiondata->clip_Y->max);
+			} else {
+				instance_data->clip_Y_low =
+				    functiondata->clip_Y->min;
+				instance_data->clip_Y_high =
+				    functiondata->clip_Y->max;
+			}
+		}
+	}
+
+	/* need to init the input abs params for the 2D */
+	set_bit(EV_ABS, function_device->input->evbit);
+	set_bit(EV_SYN, function_device->input->evbit);
+	set_bit(EV_KEY, function_device->input->evbit);
+
+	f11_set_abs_params(function_device);
+
+	pr_debug("%s: Creating sysfs files.", __func__);
+	/* Set up sysfs device attributes. */
+	for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+		if (sysfs_create_file
+		    (&function_device->dev.kobj, &attrs[attr_count].attr) < 0) {
+			pr_err
+			    ("%s: Failed to create sysfs file for %s.",
+			     __func__, attrs[attr_count].attr.name);
+			retval = -ENODEV;
+			goto error_exit;
+		}
+	}
+
+	return 0;
+
+error_exit:
+	for (attr_count--; attr_count >= 0; attr_count--)
+		sysfs_remove_file(&function_device->dev.kobj,
+				  &attrs[attr_count].attr);
+	/* If you alloc anything, free it here. */
+	return retval;
+}
+EXPORT_SYMBOL(FN_11_init);
+
+int FN_11_detect(struct rmi_function_info *rmifninfo)
+{
+	unsigned char control_buffer[12]; /* TODO: Compute size correctly. */
+	int retval = 0;
+	struct f11_instance_data *instance_data;
+
+	pr_debug("%s: RMI4 F11 detect\n", __func__);
+
+	if (rmifninfo->fndata) {
+		/* detect routine should only ever be called once
+		 * per rmifninfo. */
+		pr_err("%s: WTF?!? F11 instance data is already present!",
+		       __func__);
+		return -EINVAL;
+	}
+	instance_data = kzalloc(sizeof(struct f11_instance_data), GFP_KERNEL);
+	if (!instance_data) {
+		pr_err("%s: Error allocating F11 instance data.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	rmifninfo->fndata = instance_data;
+
+	instance_data->device_info =
+	    kzalloc(sizeof(struct rmi_F11_device_query), GFP_KERNEL);
+	if (!instance_data->device_info) {
+		pr_err("%s: Error allocating F11 device query.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	instance_data->sensor_info =
+	    kzalloc(sizeof(struct rmi_F11_sensor_query), GFP_KERNEL);
+	if (!instance_data->sensor_info) {
+		pr_err("%s: Error allocating F11 sensor query.\n", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	retval = read_query_registers(rmifninfo);
+	if (retval) {
+		pr_err("%s: Failed to read sensor query registers.", __func__);
+		goto error_exit;
+	}
+
+	instance_data->finger_data_buffer =
+	    kcalloc(instance_data->finger_data_buffer_size,
+		    sizeof(unsigned char), GFP_KERNEL);
+	if (!instance_data->finger_data_buffer) {
+		pr_err("%s: Failed to allocate finger data buffer.", __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+
+	/* Grab a copy of the control registers. */
+	instance_data->control_registers =
+	    kzalloc(sizeof(struct rmi_F11_control), GFP_KERNEL);
+	if (!instance_data->control_registers) {
+		pr_err("%s: Error allocating F11 control registers.\n",
+		       __func__);
+		retval = -ENOMEM;
+		goto error_exit;
+	}
+	retval = rmi_read_multiple(rmifninfo->sensor,
+			      rmifninfo->function_descriptor.control_base_addr,
+			      control_buffer, sizeof(control_buffer));
+	if (retval) {
+		pr_err("%s: Failed to read F11 control registers.", __func__);
+		goto error_exit;
+	}
+	instance_data->control_registers->sensor_max_X_pos =
+	    (((int)control_buffer[7] & 0x0F) << 8) + control_buffer[6];
+	instance_data->control_registers->sensor_max_Y_pos =
+	    (((int)control_buffer[9] & 0x0F) << 8) + control_buffer[8];
+	pr_debug("%s: Max X %d Max Y %d", __func__,
+		 instance_data->control_registers->sensor_max_X_pos,
+		 instance_data->control_registers->sensor_max_Y_pos);
+	return 0;
+
+error_exit:
+	if (instance_data) {
+		kfree(instance_data->sensor_info);
+		kfree(instance_data->device_info);
+		kfree(instance_data->control_registers);
+		kfree(instance_data->finger_data_buffer);
+	}
+	kfree(instance_data);
+	rmifninfo->fndata = NULL;
+	return retval;
+}
+EXPORT_SYMBOL(FN_11_detect);
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+
+	return snprintf(buf, PAGE_SIZE, "%u %u\n",
+		instance_data->control_registers->sensor_max_X_pos,
+		instance_data->control_registers->sensor_max_Y_pos);
+}
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+
+	return snprintf(buf, PAGE_SIZE, "%u %u\n", instance_data->flip_X,
+			instance_data->flip_Y);
+}
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+	unsigned int new_X, new_Y;
+
+	if (sscanf(buf, "%u %u", &new_X, &new_Y) != 2)
+		return -EINVAL;
+	if (new_X < 0 || new_X > 1 || new_Y < 0 || new_Y > 1)
+		return -EINVAL;
+	instance_data->flip_X = new_X;
+	instance_data->flip_Y = new_Y;
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->swap_axes);
+}
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+	unsigned int newSwap;
+
+	if (sscanf(buf, "%u", &newSwap) != 1)
+		return -EINVAL;
+	if (newSwap < 0 || newSwap > 1)
+		return -EINVAL;
+	instance_data->swap_axes = newSwap;
+
+	f11_set_abs_params(fn);
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+
+	return snprintf(buf, PAGE_SIZE,
+			"%u\n", instance_data->rel_report_enabled);
+}
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf,
+					 size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+	unsigned int new_value;
+
+	if (sscanf(buf, "%u", &new_value) != 1)
+		return -EINVAL;
+	if (new_value < 0 || new_value > 1)
+		return -EINVAL;
+	instance_data->rel_report_enabled = new_value;
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+
+	return snprintf(buf, PAGE_SIZE, "%d %d\n", instance_data->offset_X,
+		instance_data->offset_Y);
+}
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+	int new_X, new_Y;
+
+	if (sscanf(buf, "%d %d", &new_X, &new_Y) != 2)
+		return -EINVAL;
+	instance_data->offset_X = new_X;
+	instance_data->offset_Y = new_Y;
+
+	return count;
+}
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+
+	return snprintf(buf, PAGE_SIZE, "%u %u %u %u\n",
+		       instance_data->clip_X_low, instance_data->clip_X_high,
+		       instance_data->clip_Y_low, instance_data->clip_Y_high);
+}
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf,
+				size_t count)
+{
+	struct rmi_function_device *fn = dev_get_drvdata(dev);
+	struct f11_instance_data *instance_data = fn->rfi->fndata;
+	unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+
+	if (sscanf(buf, "%u %u %u %u",
+			&new_X_low, &new_X_high, &new_Y_low, &new_Y_high) != 4)
+		return -EINVAL;
+	if (new_X_low < 0 || new_X_low >= new_X_high || new_Y_low < 0
+			|| new_Y_low >= new_Y_high)
+		return -EINVAL;
+	instance_data->clip_X_low = new_X_low;
+	instance_data->clip_X_high = new_X_high;
+	instance_data->clip_Y_low = new_Y_low;
+	instance_data->clip_Y_high = new_Y_high;
+
+	f11_set_abs_params(fn);
+
+	return count;
+}