linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* RE: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
@ 2012-10-03  3:30 Alexandra Chin
  2012-10-03 18:01 ` Henrik Rydberg
  0 siblings, 1 reply; 12+ messages in thread
From: Alexandra Chin @ 2012-10-03  3:30 UTC (permalink / raw)
  To: Alexandra Chin, Dmitry Torokhov, Henrik Rydberg
  Cc: Linux Kernel, Linux Input, Linus Walleij, Naveen Kumar Gaddipati,
	Mahesh Srinivasan, Alex Chang, Scott Lin, Christopher Heiny

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 41985 bytes --]

Add Chris in mail loop.

Best Regards,
Alexandra Chin


 Synaptics Hong Kong Limited, Taiwan Branch
 5F., No. 501, Sec. 2 Tiding Blvd., Neihu District, 
 Taipei City 114, Taiwan
Office: 886.2.8752.5700  ext:652
Email:Alexandra.chin@synaptics.com.tw


-----Original Message-----
From: Alexandra Chin 
Sent: Tuesday, October 02, 2012 3:58 PM
To: Dmitry Torokhov; 'Henrik Rydberg'
Cc: Linux Kernel; Linux Input; Linus Walleij; Naveen Kumar Gaddipati; Mahesh Srinivasan; Alex Chang; Scott Lin
Subject: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices

Hi Henrik/Dmitry,

We are working on a product specific driver for Synaptics DS4 I2C touchscreen
devices. It was submitted on Sept. 16, 2012, but has not been reviewed.
(http://lkml.org/lkml/2012/9/16/24). 
We found several warnings after running script/checkpatch.pl, therefore 
an updated patch is attached.

As Chris says in https://lkml.org/lkml/2012/9/19/505, this driver will enable
us to support all our customers effectively and provide our customers with 
the best flexibility possible.
Please help review attached patch, and we really appreciate your feedback :-)



Synaptics DS4 touchscreen driver implements a generic driver supporting I2C
protocol for Synaptics Design Studio 4 (DS4) family of Touchscreen Controllers
which include the following:

- S32xX series
- S730X series
- S22xx series

The driver supports multifinger pointing functionality and power management.
The driver is based on the original work submitted by
Linus Walleij <linus.walleij@stericsson.com> and
Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>.

This patch is against the v3.1-rc9 tag of Dmitry Torokhov's kernel tree,
commit bd68dfe0071b50bc69416a92ee22b63d1cc33a3b.

Changes in this patch:
 - modified:   drivers/input/touchscreen/Kconfig
 - modified:   drivers/input/touchscreen/Makefile
 - new file:   drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
 - new file:   drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
 - new file:   include/linux/input/synaptics_dsx.h

This patch is functionally tested on omap beagleboard-xm.

We will continue to maintain and support this driver officially, including
making updates, as well as supporting future Touch Controller revisions
from Synaptics.

Any comments are much appreciated.

Alexandra Chin

Signed-off-by: Alexandra Chin <alexandra.chin@tw.synaptics.com>
---
 drivers/input/touchscreen/Kconfig                  |   12 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c | 1083 ++++++++++++++++++++
 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h |   94 ++
 include/linux/input/synaptics_dsx.h                |   49 +
 5 files changed, 1239 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
 create mode 100644 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
 create mode 100644 include/linux/input/synaptics_dsx.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..431c72b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -900,4 +900,16 @@ config TOUCHSCREEN_TPS6507X
 	  To compile this driver as a module, choose M here: the
 	  module will be called tps6507x_ts.
 
+config TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C
+	tristate "Synaptics ds4 i2c touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have a Synaptics DS4 I2C touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called synaptics_ds4_rmi4_i2c.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 178eb12..61f5f22 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C)	+= synaptics_ds4_rmi4_i2c.o
diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
new file mode 100644
index 0000000..6e85d97
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
@@ -0,0 +1,1083 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_ds4_rmi4_i2c.h"
+
+#define DRIVER_NAME "synaptics_ds4_i2c"
+#define INPUT_PHYS_NAME "synaptics_ds4_i2c/input0"
+
+#define PAGES_TO_SERVICE	0xFF
+#define MAX_INTR_REGISTERS	4
+#define MAX_TOUCH_MAJOR		15
+#define BUF_LEN			37
+#define PAGE_LEN		2
+#define DATA_LEN		12
+#define QUERY_LEN		9
+#define DATA_BUF_LEN		10
+#define STD_QUERY_LEN		21
+
+#define MASK_16BIT		0xFFFF
+#define MASK_8BIT		0xFF
+#define MASK_7BIT		0x7F
+#define MASK_5BIT		0x1F
+#define MASK_4BIT		0x0F
+#define MASK_3BIT		0x07
+#define MASK_2BIT		0x03
+#define TOUCH_CTRL_INTR	0x8
+
+#define NO_SLEEP_ON		(1 << 3)
+#define NO_SLEEP_OFF		(0 << 3)
+#define NORMAL_OPERATION	(0 << 0)
+#define SENSOR_SLEEP		(1 << 0)
+#define RMI4_NUMBER_OF_MAX_FINGERS	(10)
+
+#ifdef CONFIG_PM
+static int synaptics_rmi4_suspend(struct device *dev);
+static int synaptics_rmi4_resume(struct device *dev);
+#endif
+
+/*
+ * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write().
+ *
+ * This function writes to the page select register to switch to the
+ * assigned page.
+ */
+static int synaptics_rmi4_set_page(struct synaptics_ds4_rmi4_data *pdata,
+					unsigned int address)
+{
+	unsigned char	txbuf[PAGE_LEN];
+	int retval = 0;
+	unsigned int page;
+	struct i2c_client *i2c = pdata->i2c_client;
+
+	page = ((address >> 8) & MASK_8BIT);
+	if (page != pdata->current_page) {
+		txbuf[0] = MASK_8BIT;
+		txbuf[1] = page;
+		retval = i2c_master_send(i2c, txbuf, PAGE_LEN);
+		if (retval != PAGE_LEN)
+			retval = -EIO;
+		else
+			pdata->current_page = page;
+	} else
+		retval = PAGE_LEN;
+
+	return retval;
+}
+
+/*
+ * Called by various functions in this driver
+ *
+ * This function reads data of an arbitrary length from the sensor, starting
+ * from an assigned register address of the sensor, via I2C with a retry
+ * mechanism.
+ */
+static int synaptics_rmi4_i2c_read(struct synaptics_ds4_rmi4_data *pdata,
+			unsigned short addr, unsigned char *data,
+			unsigned short length)
+{
+	int retval = 0;
+	unsigned char buf;
+	struct i2c_msg msg[] = {
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &buf,
+		},
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = data,
+		}
+	};
+	buf = addr & MASK_8BIT;
+
+	mutex_lock(&(pdata->io_ctrl_mutex));
+	retval = synaptics_rmi4_set_page(pdata, addr);
+	if (retval != PAGE_LEN)
+		goto exit;
+	retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg));
+	if (retval != ARRAY_SIZE(msg))
+		retval = -EIO;
+exit:
+	mutex_unlock(&(pdata->io_ctrl_mutex));
+	return retval;
+}
+
+/*
+ * Called by various functions in this driver
+ *
+ * This function writes data of an arbitrary length to the sensor, starting
+ * from an assigned register address of the sensor, via I2C with a retry
+ * mechanism.
+ */
+static int synaptics_rmi4_i2c_write(struct synaptics_ds4_rmi4_data *pdata,
+			unsigned short addr, unsigned char *data,
+			unsigned short length)
+{
+	int retval = 0;
+	unsigned char buf[length + 1];
+
+	struct i2c_msg msg[] = {
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = 0,
+			.len = length + 1,
+			.buf = buf,
+		}
+	};
+
+	mutex_lock(&(pdata->io_ctrl_mutex));
+	retval = synaptics_rmi4_set_page(pdata, addr);
+	if (retval != PAGE_LEN)
+		goto exit;
+
+	buf[0] = addr & MASK_8BIT;
+	memcpy(&buf[1], &data[0], length);
+
+	retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg));
+	if (retval != ARRAY_SIZE(msg))
+		retval = -EIO;
+exit:
+	mutex_unlock(&(pdata->io_ctrl_mutex));
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_report_device() when valid Function $11
+ * finger data has been detected.
+ *
+ * This function reads the Function $11 data registers, determines the
+ * status of each finger supported by the Function, processes any
+ * necessary coordinate manipulation, reports the finger data to
+ * the input subsystem
+ */
+static int synaptics_rmi4_abs_report(struct synaptics_ds4_rmi4_data *pdata,
+			struct synaptics_ds4_rmi4_fn *function_handler)
+{
+	int touch_count = 0;	/* number of touch points */
+	int finger;
+	int fingers_supported;
+	int finger_registers;
+	int reg;
+	int finger_shift;
+	int finger_status;
+	int retval;
+	unsigned short data_base_addr;
+	unsigned short data_offset;
+	unsigned char data_reg_blk_size;
+	unsigned char values[2];
+	unsigned char data[DATA_LEN];
+	int x[RMI4_NUMBER_OF_MAX_FINGERS];
+	int y[RMI4_NUMBER_OF_MAX_FINGERS];
+	int wx[RMI4_NUMBER_OF_MAX_FINGERS];
+	int wy[RMI4_NUMBER_OF_MAX_FINGERS];
+
+	/* get 2D sensor finger data */
+	/*
+	 * First get the finger status field - the size of the finger status
+	 * field is determined by the number of finger supporte - 2 bits per
+	 * finger, so the number of registers to read is:
+	 * registerCount = ceil(numberOfFingers/4).
+	 * Read the required number of registers and check each 2 bit field to
+	 * determine if a finger is down:
+	 *	00 = finger not present,
+	 *	01 = finger present and data accurate,
+	 *	10 = finger present but data may not be accurate,
+	 *	11 = reserved for product use.
+	 */
+	fingers_supported = function_handler->num_of_data_points;
+	finger_registers = (fingers_supported + 3) / 4;
+	data_base_addr = function_handler->fn_full_addr.data_base_addr;
+
+	retval = synaptics_rmi4_i2c_read(pdata, data_base_addr, values,
+							finger_registers);
+	if (retval < 0)
+		return retval;
+	/*
+	 * For each finger present, read the proper number of registers
+	 * to get absolute data.
+	 */
+	data_reg_blk_size = function_handler->size_of_data_register_block;
+	for (finger = 0; finger < fingers_supported; finger++) {
+		/* 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	= (values[reg] >> finger_shift) & 3;
+		/*
+		 * If finger status indicates a finger is present then
+		 * read the finger data and report it
+		 */
+		if (finger_status == 1 || finger_status == 2) {
+			/* Read the finger data */
+			data_offset = data_base_addr +
+					((finger * data_reg_blk_size) +
+					finger_registers);
+			retval = synaptics_rmi4_i2c_read(pdata,
+						data_offset, data,
+						data_reg_blk_size);
+			if (retval < 0)
+				return retval;
+			else {
+				x[touch_count] =
+					(data[0] << 4) | (data[2] & MASK_4BIT);
+				y[touch_count] =
+					(data[1] << 4) |
+					((data[2] >> 4) & MASK_4BIT);
+				wy[touch_count] = (data[3] >> 4) & MASK_4BIT;
+				wx[touch_count] = (data[3] & MASK_4BIT);
+
+				if (pdata->board->x_flip)
+					x[touch_count] =
+						pdata->sensor_max_x -
+								x[touch_count];
+				if (pdata->board->y_flip)
+					y[touch_count] =
+						pdata->sensor_max_y -
+								y[touch_count];
+			}
+			/* Number of active touch points */
+			touch_count++;
+		}
+	}
+
+	/* Report to input subsystem */
+	if (touch_count) {
+		for (finger = 0; finger < touch_count; finger++) {
+			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
+						max(wx[finger] , wy[finger]));
+			input_report_abs(pdata->input_dev, ABS_MT_POSITION_X,
+								x[finger]);
+			input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y,
+								y[finger]);
+			input_mt_sync(pdata->input_dev);
+		}
+	} else
+		input_mt_sync(pdata->input_dev);
+
+	/* sync after groups of events */
+	input_sync(pdata->input_dev);
+	return touch_count;
+}
+
+/*
+ * Called by synaptics_rmi4_sensor_report().
+ *
+ * This function calls the appropriate finger data reporting function
+ * based on the function handler it receives
+ */
+static int synaptics_rmi4_report_device(struct synaptics_ds4_rmi4_data *pdata,
+				struct synaptics_ds4_rmi4_fn *function_handler)
+{
+	int touch_count = 0;
+	struct i2c_client *client = pdata->i2c_client;
+
+	dev_dbg(&pdata->i2c_client->dev, "%s: function number 0x%X\n",
+				__func__, function_handler->fn_number);
+
+	switch (function_handler->fn_number) {
+	case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC:
+		touch_count = synaptics_rmi4_abs_report(
+						pdata, function_handler);
+		break;
+	default:
+		dev_info(&client->dev, "%s: F%02X not supported\n",
+					__func__, function_handler->fn_number);
+		break;
+	}
+	return touch_count;
+}
+
+/*
+ * Called by synaptics_rmi4_irq().
+ *
+ * This function determines the interrupt source(s) from the sensor and
+ * calls synaptics_rmi4_report_device() with the appropriate function
+ * handler for each function with valid data inputs.
+ */
+static int synaptics_rmi4_sensor_report(struct synaptics_ds4_rmi4_data *pdata)
+{
+	unsigned char	intr_status[MAX_INTR_REGISTERS];
+	int retval;
+	int touch_count = 0;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	struct synaptics_ds4_rmi4_device_info *rmi;
+
+	/*
+	 * Get the interrupt status from the function $01
+	 * control register+1 to find which source(s) were interrupting
+	 * so we can read the data from the source(s) (2D sensor, buttons..)
+	 */
+	retval = synaptics_rmi4_i2c_read(pdata,
+					pdata->fn01_data_base_addr + 1,
+					intr_status,
+					pdata->number_of_interrupt_register);
+	if (retval < 0)
+		return 0;
+
+	if (pdata->touch_stopped) {
+		dev_warn(&pdata->i2c_client->dev,
+			 "Not ready to handle interrupts yet!\n");
+		return 0;
+	}
+	/*
+	 * Check each function that has data sources and if the interrupt for
+	 * that triggered then call that RMI4 functions report() function to
+	 * gather data and report it to the input subsystem
+	 */
+	rmi = &(pdata->rmi4_mod_info);
+	list_for_each_entry(function_handler, &rmi->support_fn_list, link) {
+		if (function_handler->num_of_data_sources) {
+			if (intr_status[function_handler->index_to_intr_reg] &
+						function_handler->intr_mask)
+				touch_count = synaptics_rmi4_report_device(
+						pdata, function_handler);
+		}
+	}
+	return touch_count;
+}
+
+/*
+ * Called by the kernel when an interrupt occurs (when the sensor asserts
+ * the attention irq.
+ *
+ * This function is the ISR thread and handles the acquisition and
+ * the reporting of finger data when the presence of fingers is detected.
+ */
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
+{
+	struct synaptics_ds4_rmi4_data *pdata = data;
+	int touch_count;
+	do {
+		touch_count = synaptics_rmi4_sensor_report(pdata);
+		if (touch_count)
+			wait_event_timeout(pdata->wait, pdata->touch_stopped,
+							msecs_to_jiffies(1));
+		else
+			break;
+	} while (!pdata->touch_stopped);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Called by synaptics_rmi4_probe() and the power management functions in
+ * this driver
+ *
+ * This function handles the enabling of the attention irq
+ * including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_enable(struct synaptics_ds4_rmi4_data *pdata,
+					bool request)
+{
+	int retval = 0;
+	char intr_status;
+	const struct synaptics_dsx_platform_data *platformdata =
+				pdata->i2c_client->dev.platform_data;
+
+	mutex_lock(&pdata->irq_request_mutex);
+	if (pdata->irq_enabled)
+		goto exit;
+
+	/* Clear interrupts */
+	retval = synaptics_rmi4_i2c_read(pdata,
+					pdata->fn01_data_base_addr + 1,
+					&intr_status,
+					pdata->number_of_interrupt_register);
+	if (retval < 0)
+		return retval;
+	if (request) {
+		retval = request_threaded_irq(pdata->irq, NULL,
+					  synaptics_rmi4_irq,
+					  platformdata->irq_type,
+					  DRIVER_NAME, pdata);
+		if (retval) {
+			dev_err(&pdata->i2c_client->dev,
+				"%s:Unable to get attn irq %d, type %d\n",
+				__func__,
+				pdata->irq,
+				platformdata->irq_type);
+			goto exit;
+		}
+	} else
+		enable_irq(pdata->irq);
+	pdata->irq_enabled  = true;
+
+exit:
+	mutex_unlock(&pdata->irq_request_mutex);
+	return retval;
+}
+
+/*
+ * Called by synaptics_rmi4_probe() and the power management functions in
+ * this driver
+ *
+ * This function handles the disabling of the attention irq
+ * including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_disable(struct synaptics_ds4_rmi4_data *pdata,
+					bool release)
+{
+	int retval = 0;
+	mutex_lock(&pdata->irq_request_mutex);
+
+	if (pdata->irq_enabled) {
+		disable_irq(pdata->irq);
+		if (release)
+			free_irq(pdata->irq, pdata);
+		pdata->irq_enabled = false;
+	} else
+		dev_warn(&pdata->i2c_client->dev,
+			 "%s:irq has not been reqested\n", __func__);
+	mutex_unlock(&pdata->irq_request_mutex);
+	return retval;
+}
+
+/*
+ * Called by synaptics_rmi4_query_device().
+ *
+ * This funtion parses information from the Function 11 registers and
+ * determines the number of fingers supported, x and y data ranges,
+ * offset to the associated interrupt status register, interrupt bit mask,
+ * and gathers finger data acquisition capabilities from the query registers.
+ */
+static int synaptics_rmi4_2d_touch_detect(
+				struct synaptics_ds4_rmi4_data *pdata,
+				struct synaptics_ds4_rmi4_fn *function_handler,
+				struct synaptics_ds4_rmi4_fn_desc *fd,
+				unsigned int interruptcount)
+{
+	unsigned char	queries[QUERY_LEN];
+	unsigned char data[BUF_LEN];
+	unsigned char	abs_data_size;
+	unsigned char	abs_data_blk_size;
+	unsigned short intr_offset;
+	int i;
+	int retval;
+
+	function_handler->fn_number = fd->fn_number;
+	function_handler->num_of_data_sources = fd->intr_src_count;
+
+	/*
+	 * 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 = synaptics_rmi4_i2c_read(pdata,
+				function_handler->fn_full_addr.query_base_addr,
+				queries, sizeof(queries));
+	if (retval < 0)
+		return retval;
+
+	/* Number of fingers */
+	if ((queries[1] & MASK_3BIT) <= 4)	/* add 1 since zero based */
+		function_handler->num_of_data_points =
+					(queries[1] & MASK_3BIT) + 1;
+	else
+		if ((queries[1] & MASK_3BIT) == 5)
+			function_handler->num_of_data_points = 10;
+
+	/* Max x/y */
+	retval = synaptics_rmi4_i2c_read(pdata,
+				function_handler->fn_full_addr.ctrl_base_addr,
+				data, DATA_BUF_LEN);
+	if (retval < 0)
+		return retval;
+
+	/* Store these for use later*/
+	pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) |
+						((data[7] & MASK_4BIT) << 8);
+	pdata->sensor_max_y = ((data[8] & MASK_8BIT) << 0) |
+						((data[9] & MASK_4BIT) << 8);
+
+
+	/* Interrupt info for handling interrupts */
+	function_handler->index_to_intr_reg = (interruptcount + 7) / 8;
+	if (function_handler->index_to_intr_reg != 0)
+		function_handler->index_to_intr_reg -= 1;
+	/*
+	 * Loop through interrupts for each source in fn $11
+	 * and or in a bit to the interrupt mask for each.
+	 */
+	intr_offset = interruptcount % 8;
+	function_handler->intr_mask = 0;
+	for (i = intr_offset;
+		i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++)
+		function_handler->intr_mask |= 1 << i;
+
+	/* Size of just the absolute data for one finger */
+	abs_data_size	= queries[5] & MASK_2BIT;
+
+	/* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+	abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+	function_handler->size_of_data_register_block = abs_data_blk_size;
+
+	return retval;
+}
+
+static int synaptics_rmi4_alloc_func_handler(
+			struct synaptics_ds4_rmi4_fn **function_handler,
+			struct synaptics_ds4_rmi4_fn_desc *rmi_fd,
+			int page_number)
+{
+	*function_handler =
+		kmalloc(sizeof(struct synaptics_ds4_rmi4_fn), GFP_KERNEL);
+	if (!(*function_handler))
+		return -ENOMEM;
+
+	(*function_handler)->fn_full_addr.data_base_addr =
+		(rmi_fd->data_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.ctrl_base_addr =
+		(rmi_fd->ctrl_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.cmd_base_addr =
+		(rmi_fd->cmd_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.query_base_addr =
+		(rmi_fd->query_base_addr | (page_number << 8));
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_probe().
+ *
+ * This funtion scans the page description table, records the offsets to the
+ * register types of Function $01, sets up the function handlers for Function
+ * $11, determines the number of interrupt sources from the
+ * sensor, adds valid Functions with data inputs to the Function linked list,
+ * parses information from the query registers of Function $01, and enables
+ * the interrupt sources from the valid Functions with data inputs.
+ */
+static int synaptics_rmi4_query_device(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int i, page_number;
+	int retval;
+	int data_sources = 0;
+	unsigned char std_queries[STD_QUERY_LEN];
+	unsigned char interrupt_mask[MAX_INTR_REGISTERS];
+	unsigned char intr_count = 0;
+	unsigned char intr_index = 0;
+	unsigned short ctrl_offset;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	struct synaptics_ds4_rmi4_fn_desc rmi_fd;
+	struct synaptics_ds4_rmi4_device_info *rmi;
+	struct i2c_client *client = pdata->i2c_client;
+
+	/* Init the physical drivers RMI module info list of functions */
+	INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list);
+
+	/* Scan the Page Descriptor Table */
+	for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
+		for (i = SYNAPTICS_DS4_RMI4_PDT_START;
+			i > SYNAPTICS_DS4_RMI4_PDT_END;
+			i -= SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE) {
+			function_handler = NULL;
+			i |= (page_number << 8);
+
+			retval = synaptics_rmi4_i2c_read(pdata, i,
+						(unsigned char *)&rmi_fd,
+						sizeof(rmi_fd));
+			if (retval < 0)
+				return retval;
+
+			if (rmi_fd.fn_number == 0)	/* end of the PDT */
+				break;
+			dev_dbg(&pdata->i2c_client->dev,
+					"%s: function F%02X is present\n",
+					__func__, rmi_fd.fn_number);
+			switch (rmi_fd.fn_number & MASK_8BIT) {
+			case SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC:
+				pdata->fn01_query_base_addr =
+						rmi_fd.query_base_addr;
+				pdata->fn01_ctrl_base_addr =
+						rmi_fd.ctrl_base_addr;
+				pdata->fn01_data_base_addr =
+						rmi_fd.data_base_addr;
+				break;
+			case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC:
+				if (rmi_fd.intr_src_count) {
+					retval =
+					synaptics_rmi4_alloc_func_handler(
+							&function_handler,
+							&rmi_fd,
+							page_number);
+					if (retval < 0)
+						return retval;
+					retval =
+					synaptics_rmi4_2d_touch_detect(
+							pdata,
+							function_handler,
+							&rmi_fd,
+							intr_count);
+					if (retval < 0)
+						return retval;
+				}
+				break;
+			}
+			/* interrupt count for next iteration */
+			intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+			/*
+			 * add functions to the list that have data associated
+			 */
+			if (function_handler && rmi_fd.intr_src_count) {
+				/* link this function info to RMI module */
+				mutex_lock(&(pdata->fn_list_mutex));
+				dev_dbg(&pdata->i2c_client->dev,
+				    "%s: add fiunction handler 0x%X to list\n",
+				    __func__, function_handler->fn_number);
+				list_add_tail(&function_handler->link,
+					&pdata->rmi4_mod_info.support_fn_list);
+				mutex_unlock(&(pdata->fn_list_mutex));
+			}
+		}
+	}
+	/*
+	 * calculate the interrupt register count - used in the
+	 * ISR to read the correct number of interrupt registers
+	 */
+	pdata->number_of_interrupt_register = (intr_count + 7) / 8;
+	dev_dbg(&client->dev, "%s: interrupt register count :%d\n",
+					__func__,
+					pdata->number_of_interrupt_register);
+
+	/* Load up the standard queries and get the RMI4 module info */
+	retval = synaptics_rmi4_i2c_read(pdata, pdata->fn01_query_base_addr,
+					std_queries, sizeof(std_queries));
+	if (retval < 0)
+		return retval;
+
+	/*
+	 * get manufacturer id, product_props, product info,
+	 * date code, tester id, serial num and product id (name)
+	 */
+	pdata->rmi4_mod_info.manufacturer_id = std_queries[0];
+	pdata->rmi4_mod_info.product_props = std_queries[1];
+	pdata->rmi4_mod_info.product_info[0] = std_queries[2];
+	pdata->rmi4_mod_info.product_info[1] = std_queries[3];
+	/* year - 2001-2032 */
+	pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT;
+	/* month - 1-12 */
+	pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT;
+	/* day - 1-31 */
+	pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT;
+	pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) |
+						(std_queries[8] & MASK_7BIT);
+	pdata->rmi4_mod_info.serial_number =
+				((std_queries[9] & MASK_7BIT) << 8) |
+				(std_queries[10] & MASK_7BIT);
+	memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10);
+
+	/* Check if this is a Synaptics device - report if not. */
+	if (pdata->rmi4_mod_info.manufacturer_id != 1)
+			dev_err(&client->dev, "%s: non-Synaptics mfg id:%d\n",
+					__func__,
+					pdata->rmi4_mod_info.manufacturer_id);
+
+	memset(interrupt_mask, 0x00, sizeof(interrupt_mask));
+	list_for_each_entry(function_handler,
+			&pdata->rmi4_mod_info.support_fn_list, link)
+			data_sources += function_handler->num_of_data_sources;
+	if (data_sources) {
+		rmi = &(pdata->rmi4_mod_info);
+		list_for_each_entry(function_handler,
+					&rmi->support_fn_list, link) {
+			if (function_handler->num_of_data_sources)
+				intr_index =
+					function_handler->index_to_intr_reg;
+				interrupt_mask[intr_index] |=
+				function_handler->intr_mask;
+		}
+	}
+
+	for (i = 0; i < MAX_INTR_REGISTERS; i++) {
+		if (interrupt_mask[i] != 0x00) {
+			dev_dbg(&client->dev,
+					"%s: interrupt %d enable mask :0x%X\n",
+					__func__, i, interrupt_mask[i]);
+			ctrl_offset = pdata->fn01_ctrl_base_addr + 1 + i;
+			retval = synaptics_rmi4_i2c_write(pdata,
+						ctrl_offset,
+						&(interrupt_mask[i]),
+						sizeof(interrupt_mask[i]));
+			if (retval < 0)
+				return retval;
+		}
+	}
+	return 0;
+}
+
+static int __devinit synaptics_rmi4_probe(
+		struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+	int retval;
+	struct synaptics_ds4_rmi4_data *pdata;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	const struct synaptics_dsx_platform_data *platformdata =
+						client->dev.platform_data;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "i2c smbus byte data not supported\n");
+		return -EIO;
+	}
+
+	if (!platformdata) {
+		dev_err(&client->dev, "%s: no platform data\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Allocate and initialize the instance data for this client */
+	pdata = kzalloc(sizeof(struct synaptics_ds4_rmi4_data) * 2,
+							GFP_KERNEL);
+	if (!pdata) {
+		dev_err(&client->dev, "%s: no memory allocated\n", __func__);
+		return -ENOMEM;
+	}
+
+	pdata->input_dev = input_allocate_device();
+	if (pdata->input_dev == NULL) {
+		dev_err(&client->dev, "%s:input device alloc failed\n",
+						__func__);
+		retval = -ENOMEM;
+		goto err_input;
+	}
+
+	if (platformdata->regulator_en) {
+		pdata->regulator = regulator_get(&client->dev, "vdd");
+		if (IS_ERR(pdata->regulator)) {
+			dev_err(&client->dev, "%s:get regulator failed\n",
+								__func__);
+			retval = PTR_ERR(pdata->regulator);
+			goto err_regulator;
+		}
+		regulator_enable(pdata->regulator);
+	}
+	init_waitqueue_head(&pdata->wait);
+	pdata->i2c_client		= client;
+	pdata->current_page	= MASK_16BIT;
+	pdata->board		= platformdata;
+	pdata->sensor_sleep	= false;
+	pdata->irq_enabled	= false;
+	pdata->touch_stopped	= false;
+
+	/* Init the mutexes for maintain the lists */
+	mutex_init(&(pdata->fn_list_mutex));
+	mutex_init(&(pdata->io_ctrl_mutex));
+
+	/*
+	 * Register physical driver - this will call the detect function that
+	 * will then scan the device and determine the supported
+	 * rmi4 functions.
+	 */
+	retval = synaptics_rmi4_query_device(pdata);
+	if (retval) {
+		dev_err(&client->dev, "%s: rmi4 query device failed\n",
+							__func__);
+		goto err_query_dev;
+	}
+
+	/* Store the instance data in the i2c_client */
+	i2c_set_clientdata(client, pdata);
+
+	/* Initialize the input device parameters */
+	pdata->input_dev->name = DRIVER_NAME;
+	pdata->input_dev->phys = INPUT_PHYS_NAME;
+	pdata->input_dev->id.bustype = BUS_I2C;
+	pdata->input_dev->dev.parent = &client->dev;
+	input_set_drvdata(pdata->input_dev, pdata);
+
+	/* Initialize the function handlers for rmi4 */
+	set_bit(EV_SYN, pdata->input_dev->evbit);
+	set_bit(EV_KEY, pdata->input_dev->evbit);
+	set_bit(EV_ABS, pdata->input_dev->evbit);
+#ifdef INPUT_PROP_DIRECT
+	set_bit(INPUT_PROP_DIRECT, pdata->input_dev->propbit);
+#endif
+
+	input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_X, 0,
+						pdata->sensor_max_x, 0, 0);
+	input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_Y, 0,
+						pdata->sensor_max_y, 0, 0);
+	input_set_abs_params(pdata->input_dev, ABS_MT_TOUCH_MAJOR, 0,
+						MAX_TOUCH_MAJOR, 0, 0);
+
+	retval = input_register_device(pdata->input_dev);
+	if (retval) {
+		dev_err(&client->dev, "%s:input register failed\n", __func__);
+		goto err_query_dev;
+	}
+
+	/* Gpio configuration */
+	if (platformdata->gpio_config) {
+		retval = platformdata->gpio_config(platformdata->gpio, true);
+		if (retval < 0) {
+			dev_err(&client->dev,
+				"Failed to configure GPIOs, code: %d.\n",
+				retval);
+			return retval;
+		}
+		dev_info(&client->dev, "Done with GPIO configuration.\n");
+	}
+	pdata->irq = gpio_to_irq(platformdata->gpio);
+	mutex_init(&(pdata->irq_request_mutex));
+	pdata->touch_stopped = false;
+	retval = synaptics_rmi4_irq_enable(pdata, true);
+	if (retval) {
+		dev_err(&client->dev,
+			"%s:Unable to get attn irq %d, type %d\n, name: %s",
+			__func__, pdata->irq, platformdata->irq_type,
+			DRIVER_NAME);
+		goto err_request_irq;
+	}
+
+	return retval;
+
+err_request_irq:
+	input_unregister_device(pdata->input_dev);
+err_query_dev:
+	if (platformdata->regulator_en) {
+		regulator_disable(pdata->regulator);
+		regulator_put(pdata->regulator);
+	}
+
+	if (!list_empty(&pdata->rmi4_mod_info.support_fn_list)) {
+		list_for_each_entry(function_handler,
+			&pdata->rmi4_mod_info.support_fn_list, link) {
+			if (function_handler) {
+				kfree(function_handler->data);
+				kfree(function_handler);
+			}
+		}
+	}
+
+err_regulator:
+	input_free_device(pdata->input_dev);
+	pdata->input_dev = NULL;
+err_input:
+	kfree(pdata);
+
+	return retval;
+}
+
+static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
+{
+	struct synaptics_ds4_rmi4_data *pdata = i2c_get_clientdata(client);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	pdata->touch_stopped = true;
+	synaptics_rmi4_irq_disable(pdata, true);
+
+	input_unregister_device(pdata->input_dev);
+	if (platformdata->regulator_en) {
+		regulator_disable(pdata->regulator);
+		regulator_put(pdata->regulator);
+	}
+	kfree(pdata);
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_suspend().
+ *
+ * This function stops finger data acquisition and puts the sensor to sleep.
+ */
+static void synaptics_rmi4_sensor_sleep(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int retval;
+	unsigned char device_ctrl;
+
+	if (pdata->sensor_sleep == true)
+		return;
+
+	pdata->touch_stopped = true;
+	wake_up(&pdata->wait);
+
+	retval = synaptics_rmi4_i2c_read(pdata,
+				pdata->fn01_ctrl_base_addr,
+				&device_ctrl,
+				1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to enter sleep mode. Code: %d.\n", retval);
+		pdata->sensor_sleep = false;
+		return;
+	}
+
+	device_ctrl = (device_ctrl & ~MASK_3BIT);
+	device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP);
+
+	retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr,
+						&device_ctrl, 1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to enter sleep mode. Code: %d.\n", retval);
+		pdata->sensor_sleep = false;
+		return;
+	} else
+		pdata->sensor_sleep = true;
+
+	return;
+}
+
+/*
+ * Called by synaptics_rmi4_resume().
+ *
+ * This function wakes the sensor from sleep.
+ */
+static void synaptics_rmi4_sensor_wake(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int retval;
+	unsigned char device_ctrl;
+
+	if (pdata->sensor_sleep == false)
+		return;
+	retval = synaptics_rmi4_i2c_read(pdata,
+				pdata->fn01_ctrl_base_addr,
+				&device_ctrl,
+				1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to wake from sleep mode. Code: %d.\n",
+			retval);
+		pdata->sensor_sleep = true;
+		return;
+	}
+
+	device_ctrl = (device_ctrl & ~MASK_3BIT);
+	device_ctrl = (device_ctrl | NO_SLEEP_OFF | NORMAL_OPERATION);
+
+	retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr,
+						&device_ctrl, 1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to wake from sleep mode. Code: %d.\n",
+			retval);
+		pdata->sensor_sleep = true;
+		return;
+	} else
+		pdata->sensor_sleep = false;
+
+	return;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Called by the kernel during the suspend phase when the system
+ * enters suspend.
+ *
+ * This function stops finger data acquisition and puts the sensor to
+ * sleep, disables the interrupt, and turns off the power to the sensor.
+ */
+static int synaptics_rmi4_suspend(struct device *dev)
+{
+	struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	synaptics_rmi4_irq_disable(pdata, false);
+	synaptics_rmi4_sensor_sleep(pdata);
+
+	if (platformdata->regulator_en)
+		regulator_disable(pdata->regulator);
+	return 0;
+}
+
+/*
+ * Called by the kernel during the resume phase when the system
+ * wakes up from suspend.
+ *
+ * This function turns on the power to the sensor, wakes the sensor
+ * from sleep, enables the interrupt, and starts finger data
+ * acquisition.
+ */
+static int synaptics_rmi4_resume(struct device *dev)
+{
+	struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	if (platformdata->regulator_en)
+		regulator_enable(pdata->regulator);
+
+	synaptics_rmi4_sensor_wake(pdata);
+	synaptics_rmi4_irq_enable(pdata, false);
+	pdata->touch_stopped = false;
+
+	return 0;
+}
+
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+	.suspend = synaptics_rmi4_suspend,
+	.resume  = synaptics_rmi4_resume,
+};
+#endif
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+	.driver = {
+		.name	=	DRIVER_NAME,
+		.owner	=	THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	=	&synaptics_rmi4_dev_pm_ops,
+#endif
+	},
+	.probe		=	synaptics_rmi4_probe,
+	.remove		=	__devexit_p(synaptics_rmi4_remove),
+	.id_table	=	synaptics_rmi4_id_table,
+};
+
+static int __init synaptics_rmi4_init(void)
+{
+	return i2c_add_driver(&synaptics_rmi4_driver);
+}
+
+static void __exit synaptics_rmi4_exit(void)
+{
+	i2c_del_driver(&synaptics_rmi4_driver);
+}
+
+module_init(synaptics_rmi4_init);
+module_exit(synaptics_rmi4_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("synaptics ds4 i2c touch driver");
diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
new file mode 100644
index 0000000..58329d9
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
@@ -0,0 +1,94 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYNAPTICS_DS4_RMI4_H_
+#define _SYNAPTICS_DS4_RMI4_H_
+
+#define SYNAPTICS_DS4_RMI4_PDT_START				(0x00E9)
+#define SYNAPTICS_DS4_RMI4_PDT_END				(0x000A)
+#define SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE		(0x0006)
+#define SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC	(0x01)
+#define SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC		(0x11)
+
+/* funtion descriptor information */
+struct synaptics_ds4_rmi4_fn_desc {
+	unsigned char	query_base_addr;
+	unsigned char	cmd_base_addr;
+	unsigned char	ctrl_base_addr;
+	unsigned char	data_base_addr;
+	unsigned char	intr_src_count;
+	unsigned char	fn_number;
+};
+
+/*  funtion information */
+struct synaptics_ds4_rmi4_fn {
+	unsigned char fn_number;
+	unsigned char num_of_data_sources;
+	unsigned char num_of_data_points;
+	unsigned char size_of_data_register_block;
+	unsigned char index_to_intr_reg;
+	unsigned char intr_mask;
+	struct synaptics_ds4_rmi4_fn_desc fn_full_addr;
+	struct list_head link;
+	void *data;
+};
+
+/* device information */
+struct synaptics_ds4_rmi4_device_info {
+	unsigned char manufacturer_id;
+	unsigned char product_props;
+	unsigned char product_info[2];
+	unsigned char date_code[3];
+	unsigned short tester_id;
+	unsigned short serial_number;
+	unsigned char product_id_string[11];
+	struct list_head support_fn_list;
+};
+
+/* ds4 rmi4 touch screen data */
+struct synaptics_ds4_rmi4_data {
+	const struct synaptics_dsx_platform_data *board;
+	struct input_dev *input_dev;
+	struct i2c_client *i2c_client;
+	struct mutex fn_list_mutex;
+	struct mutex io_ctrl_mutex;
+	struct mutex irq_request_mutex;
+	struct synaptics_ds4_rmi4_device_info rmi4_mod_info;
+	struct regulator *regulator;
+	unsigned int number_of_interrupt_register;
+	unsigned short current_page;
+	unsigned short fn01_ctrl_base_addr;
+	unsigned short fn01_query_base_addr;
+	unsigned short fn01_data_base_addr;
+	unsigned short sensor_max_x;
+	unsigned short sensor_max_y;
+	wait_queue_head_t	 wait;
+	bool touch_stopped;
+	bool irq_enabled;
+	bool sensor_sleep;
+	int irq;
+};
+
+#endif
diff --git a/include/linux/input/synaptics_dsx.h b/include/linux/input/synaptics_dsx.h
new file mode 100644
index 0000000..4f0d0d6
--- /dev/null
+++ b/include/linux/input/synaptics_dsx.h
@@ -0,0 +1,49 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYNAPTICS_DSX_H_
+#define _SYNAPTICS_DSX_H_
+
+/*
+ * struct synaptics_dsx_platform_data - contains the dsx platform data
+ * @x_flip: x flip flag
+ * @y_flip: y flip flag
+ * @regulator_en: regulator enable flag
+ * @irq_type: irq type
+ * @gpio: gpio pin assignment
+ * @gpio_config: callback for gpio set up
+ *
+ * This structure gives platform data for dsx.
+ */
+struct synaptics_dsx_platform_data {
+	bool x_flip;
+	bool y_flip;
+	bool regulator_en;
+	int irq_type;
+	unsigned gpio;
+	int (*gpio_config)(unsigned interrupt_gpio, bool configure);
+};
+
+#endif
-- 
1.7.5.4

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
  2012-10-03  3:30 [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices Alexandra Chin
@ 2012-10-03 18:01 ` Henrik Rydberg
  2012-10-04  5:52   ` Alexandra Chin
  2012-10-31  9:17   ` [PATCH] staging: ste_rmi4: Convert to Type-B support Alexandra Chin
  0 siblings, 2 replies; 12+ messages in thread
From: Henrik Rydberg @ 2012-10-03 18:01 UTC (permalink / raw)
  To: Alexandra Chin
  Cc: Dmitry Torokhov, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

Hi Alexandra,

> Hi Henrik/Dmitry,
> 
> We are working on a product specific driver for Synaptics DS4 I2C touchscreen
> devices. It was submitted on Sept. 16, 2012, but has not been reviewed.
> (http://lkml.org/lkml/2012/9/16/24). 
> We found several warnings after running script/checkpatch.pl, therefore 
> an updated patch is attached.
> 
> As Chris says in https://lkml.org/lkml/2012/9/19/505, this driver will enable
> us to support all our customers effectively and provide our customers with 
> the best flexibility possible.
> Please help review attached patch, and we really appreciate your feedback :-)

It seems this driver is already present in staging. Comments and
formatting have been improved in the staging version, but that aside,
the two versions look very similar.  Why don't you submit fixes to
that driver instead?

On a general note, both versions of the driver use MT-A. Please
convert to MT-B, using the in-kernel tracking if necessary.

Thanks.
Henrik

^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
  2012-10-03 18:01 ` Henrik Rydberg
@ 2012-10-04  5:52   ` Alexandra Chin
  2012-10-04  6:51     ` Dmitry Torokhov
  2012-10-31  9:17   ` [PATCH] staging: ste_rmi4: Convert to Type-B support Alexandra Chin
  1 sibling, 1 reply; 12+ messages in thread
From: Alexandra Chin @ 2012-10-04  5:52 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Dmitry Torokhov, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

From: Henrik Rydberg
Sent: Thursday, October 04, 2012 2:01 AM
> It seems this driver is already present in staging. Comments and
> formatting have been improved in the staging version, but that aside,
> the two versions look very similar.  Why don't you submit fixes to
> that driver instead?

> On a general note, both versions of the driver use MT-A. Please
> convert to MT-B, using the in-kernel tracking if necessary.


Hi Henrik,

Appreciate your suggestion!
We are going to update a patch of using MT-B, because Synaptics devices are 
capable of tracking identifiable contacts (type B).
Thanks for pointing out this.

You are right, there is already a synaptics_i2c_rmi4 driver in staging 
state (drivers/staging/ste_rmi4). 
Actually synaptics_ds4_i2c driver is developed based on synaptics_i2c_rmi4 
driver. The point is that we would want to make a clear definition that 
synaptics_ds4_i2c only targets to DS4 family, so that we can keep maintaining 
driver focusing on DS4 product line (not all Synaptics touchscreen devices are 
DS4 compatible).

We are open to discuss if it is appropriate to retained DS4 driver, and please 
let us know if you have any concerns.

Best Regards,
Alexandra Chin

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
  2012-10-04  5:52   ` Alexandra Chin
@ 2012-10-04  6:51     ` Dmitry Torokhov
  2012-10-05  9:44       ` Alexandra Chin
  0 siblings, 1 reply; 12+ messages in thread
From: Dmitry Torokhov @ 2012-10-04  6:51 UTC (permalink / raw)
  To: Alexandra Chin
  Cc: Henrik Rydberg, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

Hi Alexandra,

On Thu, Oct 04, 2012 at 05:52:45AM +0000, Alexandra Chin wrote:
> From: Henrik Rydberg
> Sent: Thursday, October 04, 2012 2:01 AM
> > It seems this driver is already present in staging. Comments and
> > formatting have been improved in the staging version, but that aside,
> > the two versions look very similar.  Why don't you submit fixes to
> > that driver instead?
> 
> > On a general note, both versions of the driver use MT-A. Please
> > convert to MT-B, using the in-kernel tracking if necessary.
> 
> 
> Hi Henrik,
> 
> Appreciate your suggestion!
> We are going to update a patch of using MT-B, because Synaptics devices are 
> capable of tracking identifiable contacts (type B).
> Thanks for pointing out this.
> 
> You are right, there is already a synaptics_i2c_rmi4 driver in staging 
> state (drivers/staging/ste_rmi4). 
> Actually synaptics_ds4_i2c driver is developed based on synaptics_i2c_rmi4 
> driver. The point is that we would want to make a clear definition that 
> synaptics_ds4_i2c only targets to DS4 family, so that we can keep maintaining 
> driver focusing on DS4 product line (not all Synaptics touchscreen devices are 
> DS4 compatible).

Given that the other group at Synaptics works on general version of RMI4
driver what is the benefit of having separate driver for DS4 line? I can
understand keeping one copy of older driver in staging so that some
customers coudl still use hardware while universal implementation is
being developed, but I do not see why we would want to have 3 separate
drivers.

Thanks.

-- 
Dmitry

^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
  2012-10-04  6:51     ` Dmitry Torokhov
@ 2012-10-05  9:44       ` Alexandra Chin
  0 siblings, 0 replies; 12+ messages in thread
From: Alexandra Chin @ 2012-10-05  9:44 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Henrik Rydberg, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

Hi Dmitry/Henrik,


From: Dmitry Torokhov
Sent: Thursday, October 04, 2012 2:52 PM
> Given that the other group at Synaptics works on general version of RMI4
> driver what is the benefit of having separate driver for DS4 line? I can
> understand keeping one copy of older driver in staging so that some
> customers coudl still use hardware while universal implementation is
> being developed, but I do not see why we would want to have 3 separate
> drivers.


Thank you for your suggestions and comments!

We agree with your opinion. We will submit the driver as a patch on top 
of STE's driver since it is in the staging.

And we would like to state our purpose clearly why we are doing this 
including the following:
- We have customers that require a Touch Controller platform specific
 driver, that is protocol specific, following a single source file structure.
- There is also a requirement that the platform specific driver has to be 
 adopted and included into the Linux kernel.

As you are aware, Chris' team is working on general version of RMI4. 
However customers still require a platform specific driver for Touch 
Controller platform. We really hope both drivers (general RMI4 driver 
and STE's driver) being adopted into the kernel. This will allow us to 
provide the best flexibility possible to customers.

Your opinion will be greatly appreciated.

Alexandra Chin

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] staging: ste_rmi4: Convert to Type-B support
  2012-10-03 18:01 ` Henrik Rydberg
  2012-10-04  5:52   ` Alexandra Chin
@ 2012-10-31  9:17   ` Alexandra Chin
  2012-10-31 18:43     ` Henrik Rydberg
  1 sibling, 1 reply; 12+ messages in thread
From: Alexandra Chin @ 2012-10-31  9:17 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Dmitry Torokhov, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

>From 4d0dbd9ee464027e7c40658abb9dd019be520e4d Mon Sep 17 00:00:00 2001
From: Alexandra Chin <alexandra.chin@tw.synaptics.com>
Date: Wed, 31 Oct 2012 16:21:12 +0800
Subject: [PATCH] staging: ste_rmi4: Convert to Type-B support

This patch:
- Convert to MT-B because Synaptics touch devices are capable of
  tracking identifiable fingers
- Modify maximum supported fingers up to 10
- Set INPUT_PROP_POINTER for direct input devices

Any comments and suggestions are welcomed.

Alexandra Chin

Signed-off-by: Alexandra Chin <alexandra.chin@tw.synaptics.com>
---
 drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c |   82 ++++++++++++------------
 1 files changed, 41 insertions(+), 41 deletions(-)

diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
index 11728a0..e69ca5ee1 100644
--- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
+++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/regulator/consumer.h>
 #include <linux/module.h>
+#include <linux/input/mt.h>
 #include "synaptics_i2c_rmi4.h"
 
 /* TODO: for multiple device support will need a per-device mutex */
@@ -67,7 +68,7 @@
 #define PDT_START_SCAN_LOCATION (0x00E9)
 #define PDT_END_SCAN_LOCATION	(0x000A)
 #define PDT_ENTRY_SIZE		(0x0006)
-#define RMI4_NUMBER_OF_MAX_FINGERS		(8)
+#define RMI4_NUMBER_OF_MAX_FINGERS		(10)
 #define SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM	(0x11)
 #define SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM	(0x01)
 
@@ -164,6 +165,8 @@ struct synaptics_rmi4_device_info {
  * @regulator: pointer to the regulator structure
  * @wait: wait queue structure variable
  * @touch_stopped: flag to stop the thread function
+ * @finger_state: previous state of fingers
+ * @fingers_supported: maximum supported fingers
  *
  * This structure gives the device data information.
  */
@@ -184,6 +187,8 @@ struct synaptics_rmi4_data {
 	struct regulator	*regulator;
 	wait_queue_head_t	wait;
 	bool			touch_stopped;
+	unsigned char finger_state[RMI4_NUMBER_OF_MAX_FINGERS];
+	unsigned char fingers_supported;
 };
 
 /**
@@ -303,7 +308,6 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
 	/* number of touch points - fingers down in this case */
 	int	touch_count = 0;
 	int	finger;
-	int	fingers_supported;
 	int	finger_registers;
 	int	reg;
 	int	finger_shift;
@@ -314,10 +318,8 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
 	unsigned char	data_reg_blk_size;
 	unsigned char	values[2];
 	unsigned char	data[DATA_LEN];
-	int	x[RMI4_NUMBER_OF_MAX_FINGERS];
-	int	y[RMI4_NUMBER_OF_MAX_FINGERS];
-	int	wx[RMI4_NUMBER_OF_MAX_FINGERS];
-	int	wy[RMI4_NUMBER_OF_MAX_FINGERS];
+	int x, y;
+	int wx, wy;
 	struct	i2c_client *client = pdata->i2c_client;
 
 	/* get 2D sensor finger data */
@@ -333,8 +335,7 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
 	 *	10 = finger present but data may not be accurate,
 	 *	11 = reserved for product use.
 	 */
-	fingers_supported	= rfi->num_of_data_points;
-	finger_registers	= (fingers_supported + 3)/4;
+	finger_registers	= (pdata->fingers_supported + 3)/4;
 	data_base_addr		= rfi->fn_desc.data_base_addr;
 	retval = synaptics_rmi4_i2c_block_read(pdata, data_base_addr, values,
 							finger_registers);
@@ -348,17 +349,26 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
 	 * to get absolute data.
 	 */
 	data_reg_blk_size = rfi->size_of_data_register_block;
-	for (finger = 0; finger < fingers_supported; finger++) {
+	for (finger = 0; finger < pdata->fingers_supported; finger++) {
 		/* 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	= (values[reg] >> finger_shift) & 3;
+		finger_status	= (values[reg] >> finger_shift) & MASK_2BIT;
 		/*
 		 * if finger status indicates a finger is present then
 		 * read the finger data and report it
 		 */
-		if (finger_status == 1 || finger_status == 2) {
+		if (!finger_status && !pdata->finger_state[finger])
+			continue;
+
+		input_mt_slot(pdata->input_dev, finger);
+		input_mt_report_slot_state(pdata->input_dev,
+						MT_TOOL_FINGER, finger_status);
+
+		if (!finger_status && pdata->finger_state[finger]) {
+			/* release event */
+		} else {
 			/* Read the finger data */
 			data_offset = data_base_addr +
 					((finger * data_reg_blk_size) +
@@ -371,44 +381,30 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
 								__func__);
 				return 0;
 			} else {
-				x[touch_count]	=
-					(data[0] << 4) | (data[2] & MASK_4BIT);
-				y[touch_count]	=
-					(data[1] << 4) |
-					((data[2] >> 4) & MASK_4BIT);
-				wy[touch_count]	=
-						(data[3] >> 4) & MASK_4BIT;
-				wx[touch_count]	=
-						(data[3] & MASK_4BIT);
+				x = (data[0] << 4) | (data[2] & MASK_4BIT);
+				y = (data[1] << 4)
+						| ((data[2] >> 4) & MASK_4BIT);
+				wy = (data[3] >> 4) & MASK_4BIT;
+				wx = (data[3] & MASK_4BIT);
 
 				if (pdata->board->x_flip)
-					x[touch_count] =
-						pdata->sensor_max_x -
-								x[touch_count];
+					x = pdata->sensor_max_x - x;
 				if (pdata->board->y_flip)
-					y[touch_count] =
-						pdata->sensor_max_y -
-								y[touch_count];
+					y = pdata->sensor_max_y - y;
 			}
+			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
+								max(wx, wy));
+			input_report_abs(pdata->input_dev,
+							ABS_MT_POSITION_X, x);
+			input_report_abs(pdata->input_dev,
+							ABS_MT_POSITION_Y, y);
+
 			/* number of active touch points */
 			touch_count++;
 		}
+		pdata->finger_state[finger] = finger_status;
 	}
 
-	/* report to input subsystem */
-	if (touch_count) {
-		for (finger = 0; finger < touch_count; finger++) {
-			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
-						max(wx[finger] , wy[finger]));
-			input_report_abs(pdata->input_dev, ABS_MT_POSITION_X,
-								x[finger]);
-			input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y,
-								y[finger]);
-			input_mt_sync(pdata->input_dev);
-		}
-	} else
-		input_mt_sync(pdata->input_dev);
-
 	/* sync after groups of events */
 	input_sync(pdata->input_dev);
 	/* return the number of touch points */
@@ -575,6 +571,7 @@ static int synpatics_rmi4_touchpad_detect(struct synaptics_rmi4_data *pdata,
 		if ((queries[1] & MASK_3BIT) == 5)
 			rfi->num_of_data_points = 10;
 	}
+	pdata->fingers_supported = rfi->num_of_data_points;
 	/* Need to get interrupt info for handling interrupts */
 	rfi->index_to_intr_reg = (interruptcount + 7)/8;
 	if (rfi->index_to_intr_reg != 0)
@@ -981,13 +978,16 @@ static int __devinit synaptics_rmi4_probe
 	set_bit(EV_SYN, rmi4_data->input_dev->evbit);
 	set_bit(EV_KEY, rmi4_data->input_dev->evbit);
 	set_bit(EV_ABS, rmi4_data->input_dev->evbit);
-
+#ifdef INPUT_PROP_DIRECT
+	set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
+#endif
 	input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, 0,
 					rmi4_data->sensor_max_x, 0, 0);
 	input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0,
 					rmi4_data->sensor_max_y, 0, 0);
 	input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0,
 						MAX_TOUCH_MAJOR, 0, 0);
+	input_mt_init_slots(rmi4_data->input_dev, rmi4_data->fingers_supported);
 
 	/* Clear interrupts */
 	synaptics_rmi4_i2c_block_read(rmi4_data,
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] staging: ste_rmi4: Convert to Type-B support
  2012-10-31  9:17   ` [PATCH] staging: ste_rmi4: Convert to Type-B support Alexandra Chin
@ 2012-10-31 18:43     ` Henrik Rydberg
  2012-11-01  3:14       ` Alexandra Chin
  0 siblings, 1 reply; 12+ messages in thread
From: Henrik Rydberg @ 2012-10-31 18:43 UTC (permalink / raw)
  To: Alexandra Chin
  Cc: Dmitry Torokhov, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

Hi Alexandra,

> From: Alexandra Chin <alexandra.chin@tw.synaptics.com>
> Date: Wed, 31 Oct 2012 16:21:12 +0800
> Subject: [PATCH] staging: ste_rmi4: Convert to Type-B support
> 
> This patch:
> - Convert to MT-B because Synaptics touch devices are capable of
>   tracking identifiable fingers
> - Modify maximum supported fingers up to 10
> - Set INPUT_PROP_POINTER for direct input devices

Thanks for the patch, looks good overall. Please find some comments
below. Also, the patch looks word-damaged, possibly mail client
problems.

> diff --git a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
> index 11728a0..e69ca5ee1 100644
> --- a/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
> +++ b/drivers/staging/ste_rmi4/synaptics_i2c_rmi4.c
> @@ -31,6 +31,7 @@
>  #include <linux/interrupt.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/module.h>
> +#include <linux/input/mt.h>
>  #include "synaptics_i2c_rmi4.h"
>  
>  /* TODO: for multiple device support will need a per-device mutex */
> @@ -67,7 +68,7 @@
>  #define PDT_START_SCAN_LOCATION (0x00E9)
>  #define PDT_END_SCAN_LOCATION	(0x000A)
>  #define PDT_ENTRY_SIZE		(0x0006)
> -#define RMI4_NUMBER_OF_MAX_FINGERS		(8)
> +#define RMI4_NUMBER_OF_MAX_FINGERS		(10)

It seems this define can be dropped altogether.

>  #define SYNAPTICS_RMI4_TOUCHPAD_FUNC_NUM	(0x11)
>  #define SYNAPTICS_RMI4_DEVICE_CONTROL_FUNC_NUM	(0x01)
>  
> @@ -164,6 +165,8 @@ struct synaptics_rmi4_device_info {
>   * @regulator: pointer to the regulator structure
>   * @wait: wait queue structure variable
>   * @touch_stopped: flag to stop the thread function
> + * @finger_state: previous state of fingers
> + * @fingers_supported: maximum supported fingers
>   *
>   * This structure gives the device data information.
>   */
> @@ -184,6 +187,8 @@ struct synaptics_rmi4_data {
>  	struct regulator	*regulator;
>  	wait_queue_head_t	wait;
>  	bool			touch_stopped;
> +	unsigned char finger_state[RMI4_NUMBER_OF_MAX_FINGERS];

The finger state is not needed with MT-B.

> +	unsigned char fingers_supported;
>  };
>  
>  /**
> @@ -303,7 +308,6 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
>  	/* number of touch points - fingers down in this case */
>  	int	touch_count = 0;
>  	int	finger;
> -	int	fingers_supported;
>  	int	finger_registers;
>  	int	reg;
>  	int	finger_shift;
> @@ -314,10 +318,8 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
>  	unsigned char	data_reg_blk_size;
>  	unsigned char	values[2];
>  	unsigned char	data[DATA_LEN];
> -	int	x[RMI4_NUMBER_OF_MAX_FINGERS];
> -	int	y[RMI4_NUMBER_OF_MAX_FINGERS];
> -	int	wx[RMI4_NUMBER_OF_MAX_FINGERS];
> -	int	wy[RMI4_NUMBER_OF_MAX_FINGERS];
> +	int x, y;
> +	int wx, wy;
>  	struct	i2c_client *client = pdata->i2c_client;
>  
>  	/* get 2D sensor finger data */
> @@ -333,8 +335,7 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
>  	 *	10 = finger present but data may not be accurate,
>  	 *	11 = reserved for product use.
>  	 */
> -	fingers_supported	= rfi->num_of_data_points;
> -	finger_registers	= (fingers_supported + 3)/4;
> +	finger_registers	= (pdata->fingers_supported + 3)/4;
>  	data_base_addr		= rfi->fn_desc.data_base_addr;
>  	retval = synaptics_rmi4_i2c_block_read(pdata, data_base_addr, values,
>  							finger_registers);
> @@ -348,17 +349,26 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
>  	 * to get absolute data.
>  	 */
>  	data_reg_blk_size = rfi->size_of_data_register_block;
> -	for (finger = 0; finger < fingers_supported; finger++) {
> +	for (finger = 0; finger < pdata->fingers_supported; finger++) {
>  		/* 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	= (values[reg] >> finger_shift) & 3;
> +		finger_status	= (values[reg] >> finger_shift) & MASK_2BIT;
>  		/*
>  		 * if finger status indicates a finger is present then
>  		 * read the finger data and report it
>  		 */
> -		if (finger_status == 1 || finger_status == 2) {
> +		if (!finger_status && !pdata->finger_state[finger])
> +			continue;

This can be dropped - the input core will only emit changes anyways.

> +
> +		input_mt_slot(pdata->input_dev, finger);
> +		input_mt_report_slot_state(pdata->input_dev,
> +						MT_TOOL_FINGER, finger_status);
> +
> +		if (!finger_status && pdata->finger_state[finger]) {
> +			/* release event */
> +		} else {

A "if (finger_status) {" will do here.

>  			/* Read the finger data */
>  			data_offset = data_base_addr +
>  					((finger * data_reg_blk_size) +
> @@ -371,44 +381,30 @@ static int synpatics_rmi4_touchpad_report(struct synaptics_rmi4_data *pdata,
>  								__func__);
>  				return 0;
>  			} else {
> -				x[touch_count]	=
> -					(data[0] << 4) | (data[2] & MASK_4BIT);
> -				y[touch_count]	=
> -					(data[1] << 4) |
> -					((data[2] >> 4) & MASK_4BIT);
> -				wy[touch_count]	=
> -						(data[3] >> 4) & MASK_4BIT;
> -				wx[touch_count]	=
> -						(data[3] & MASK_4BIT);
> +				x = (data[0] << 4) | (data[2] & MASK_4BIT);
> +				y = (data[1] << 4)
> +						| ((data[2] >> 4) & MASK_4BIT);
> +				wy = (data[3] >> 4) & MASK_4BIT;
> +				wx = (data[3] & MASK_4BIT);
>  
>  				if (pdata->board->x_flip)
> -					x[touch_count] =
> -						pdata->sensor_max_x -
> -								x[touch_count];
> +					x = pdata->sensor_max_x - x;
>  				if (pdata->board->y_flip)
> -					y[touch_count] =
> -						pdata->sensor_max_y -
> -								y[touch_count];
> +					y = pdata->sensor_max_y - y;
>  			}
> +			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
> +								max(wx, wy));
> +			input_report_abs(pdata->input_dev,
> +							ABS_MT_POSITION_X, x);
> +			input_report_abs(pdata->input_dev,
> +							ABS_MT_POSITION_Y, y);
> +

A local variable for pdata->input_dev might clean up the wrapping here.

>  			/* number of active touch points */
>  			touch_count++;
>  		}
> +		pdata->finger_state[finger] = finger_status;

Not necessary.

>  	}
>  
> -	/* report to input subsystem */
> -	if (touch_count) {
> -		for (finger = 0; finger < touch_count; finger++) {
> -			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
> -						max(wx[finger] , wy[finger]));
> -			input_report_abs(pdata->input_dev, ABS_MT_POSITION_X,
> -								x[finger]);
> -			input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y,
> -								y[finger]);
> -			input_mt_sync(pdata->input_dev);
> -		}
> -	} else
> -		input_mt_sync(pdata->input_dev);
> -
>  	/* sync after groups of events */
>  	input_sync(pdata->input_dev);
>  	/* return the number of touch points */
> @@ -575,6 +571,7 @@ static int synpatics_rmi4_touchpad_detect(struct synaptics_rmi4_data *pdata,
>  		if ((queries[1] & MASK_3BIT) == 5)
>  			rfi->num_of_data_points = 10;
>  	}
> +	pdata->fingers_supported = rfi->num_of_data_points;
>  	/* Need to get interrupt info for handling interrupts */
>  	rfi->index_to_intr_reg = (interruptcount + 7)/8;
>  	if (rfi->index_to_intr_reg != 0)
> @@ -981,13 +978,16 @@ static int __devinit synaptics_rmi4_probe
>  	set_bit(EV_SYN, rmi4_data->input_dev->evbit);
>  	set_bit(EV_KEY, rmi4_data->input_dev->evbit);
>  	set_bit(EV_ABS, rmi4_data->input_dev->evbit);
> -
> +#ifdef INPUT_PROP_DIRECT
> +	set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
> +#endif
>  	input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_X, 0,
>  					rmi4_data->sensor_max_x, 0, 0);
>  	input_set_abs_params(rmi4_data->input_dev, ABS_MT_POSITION_Y, 0,
>  					rmi4_data->sensor_max_y, 0, 0);
>  	input_set_abs_params(rmi4_data->input_dev, ABS_MT_TOUCH_MAJOR, 0,
>  						MAX_TOUCH_MAJOR, 0, 0);
> +	input_mt_init_slots(rmi4_data->input_dev, rmi4_data->fingers_supported);

Looks like your patch is against a slightly old kernel version;
Please send patches against 3.7-rcX.

>  
>  	/* Clear interrupts */
>  	synaptics_rmi4_i2c_block_read(rmi4_data,
> -- 
> 1.7.5.4
> 

Thanks!
Henrik

^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH] staging: ste_rmi4: Convert to Type-B support
  2012-10-31 18:43     ` Henrik Rydberg
@ 2012-11-01  3:14       ` Alexandra Chin
  0 siblings, 0 replies; 12+ messages in thread
From: Alexandra Chin @ 2012-11-01  3:14 UTC (permalink / raw)
  To: Henrik Rydberg
  Cc: Dmitry Torokhov, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin,
	Christopher Heiny

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 377 bytes --]

Hi Henrik,

I see what you mean. Input subsystem handles multi-touch tracking for input driver.
I will update reporting process, and resubmit a patch against 3.7-rcX.
Greatly appreciate your comments.

Alexandra Chin
ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
@ 2012-10-02  7:50 Alexandra Chin
  0 siblings, 0 replies; 12+ messages in thread
From: Alexandra Chin @ 2012-10-02  7:50 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg
  Cc: Linux Kernel, Linux Input, Linus Walleij, Naveen Kumar Gaddipati,
	Mahesh Srinivasan, Alex Chang, Scott Lin

Hi Henrik/Dmitry,

We are working on a product specific driver for Synaptics DS4 I2C touchscreen
devices. It was submitted on Sept. 16, 2012, but has not been reviewed.
(http://lkml.org/lkml/2012/9/16/24). 
We found several warnings after running script/checkpatch.pl, therefore 
an updated patch is attached.

As Chris says in https://lkml.org/lkml/2012/9/19/505, this driver will enable
us to support all our customers effectively and provide our customers with 
the best flexibility possible.
Please help review attached patch, and we really appreciate your feedback :-)



Synaptics DS4 touchscreen driver implements a generic driver supporting I2C
protocol for Synaptics Design Studio 4 (DS4) family of Touchscreen Controllers
which include the following:

- S32xX series
- S730X series
- S22xx series

The driver supports multifinger pointing functionality and power management.
The driver is based on the original work submitted by
Linus Walleij <linus.walleij@stericsson.com> and
Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>.

This patch is against the v3.1-rc9 tag of Dmitry Torokhov's kernel tree,
commit bd68dfe0071b50bc69416a92ee22b63d1cc33a3b.

Changes in this patch:
 - modified:   drivers/input/touchscreen/Kconfig
 - modified:   drivers/input/touchscreen/Makefile
 - new file:   drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
 - new file:   drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
 - new file:   include/linux/input/synaptics_dsx.h

This patch is functionally tested on omap beagleboard-xm.

We will continue to maintain and support this driver officially, including
making updates, as well as supporting future Touch Controller revisions
from Synaptics.

Any comments are much appreciated.

Alexandra Chin

Signed-off-by: Alexandra Chin <alexandra.chin@tw.synaptics.com>
---
 drivers/input/touchscreen/Kconfig                  |   12 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c | 1083 ++++++++++++++++++++
 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h |   94 ++
 include/linux/input/synaptics_dsx.h                |   49 +
 5 files changed, 1239 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
 create mode 100644 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
 create mode 100644 include/linux/input/synaptics_dsx.h

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..431c72b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -900,4 +900,16 @@ config TOUCHSCREEN_TPS6507X
 	  To compile this driver as a module, choose M here: the
 	  module will be called tps6507x_ts.
 
+config TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C
+	tristate "Synaptics ds4 i2c touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have a Synaptics DS4 I2C touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called synaptics_ds4_rmi4_i2c.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 178eb12..61f5f22 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C)	+= synaptics_ds4_rmi4_i2c.o
diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
new file mode 100644
index 0000000..6e85d97
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
@@ -0,0 +1,1083 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_ds4_rmi4_i2c.h"
+
+#define DRIVER_NAME "synaptics_ds4_i2c"
+#define INPUT_PHYS_NAME "synaptics_ds4_i2c/input0"
+
+#define PAGES_TO_SERVICE	0xFF
+#define MAX_INTR_REGISTERS	4
+#define MAX_TOUCH_MAJOR		15
+#define BUF_LEN			37
+#define PAGE_LEN		2
+#define DATA_LEN		12
+#define QUERY_LEN		9
+#define DATA_BUF_LEN		10
+#define STD_QUERY_LEN		21
+
+#define MASK_16BIT		0xFFFF
+#define MASK_8BIT		0xFF
+#define MASK_7BIT		0x7F
+#define MASK_5BIT		0x1F
+#define MASK_4BIT		0x0F
+#define MASK_3BIT		0x07
+#define MASK_2BIT		0x03
+#define TOUCH_CTRL_INTR	0x8
+
+#define NO_SLEEP_ON		(1 << 3)
+#define NO_SLEEP_OFF		(0 << 3)
+#define NORMAL_OPERATION	(0 << 0)
+#define SENSOR_SLEEP		(1 << 0)
+#define RMI4_NUMBER_OF_MAX_FINGERS	(10)
+
+#ifdef CONFIG_PM
+static int synaptics_rmi4_suspend(struct device *dev);
+static int synaptics_rmi4_resume(struct device *dev);
+#endif
+
+/*
+ * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write().
+ *
+ * This function writes to the page select register to switch to the
+ * assigned page.
+ */
+static int synaptics_rmi4_set_page(struct synaptics_ds4_rmi4_data *pdata,
+					unsigned int address)
+{
+	unsigned char	txbuf[PAGE_LEN];
+	int retval = 0;
+	unsigned int page;
+	struct i2c_client *i2c = pdata->i2c_client;
+
+	page = ((address >> 8) & MASK_8BIT);
+	if (page != pdata->current_page) {
+		txbuf[0] = MASK_8BIT;
+		txbuf[1] = page;
+		retval = i2c_master_send(i2c, txbuf, PAGE_LEN);
+		if (retval != PAGE_LEN)
+			retval = -EIO;
+		else
+			pdata->current_page = page;
+	} else
+		retval = PAGE_LEN;
+
+	return retval;
+}
+
+/*
+ * Called by various functions in this driver
+ *
+ * This function reads data of an arbitrary length from the sensor, starting
+ * from an assigned register address of the sensor, via I2C with a retry
+ * mechanism.
+ */
+static int synaptics_rmi4_i2c_read(struct synaptics_ds4_rmi4_data *pdata,
+			unsigned short addr, unsigned char *data,
+			unsigned short length)
+{
+	int retval = 0;
+	unsigned char buf;
+	struct i2c_msg msg[] = {
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &buf,
+		},
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = data,
+		}
+	};
+	buf = addr & MASK_8BIT;
+
+	mutex_lock(&(pdata->io_ctrl_mutex));
+	retval = synaptics_rmi4_set_page(pdata, addr);
+	if (retval != PAGE_LEN)
+		goto exit;
+	retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg));
+	if (retval != ARRAY_SIZE(msg))
+		retval = -EIO;
+exit:
+	mutex_unlock(&(pdata->io_ctrl_mutex));
+	return retval;
+}
+
+/*
+ * Called by various functions in this driver
+ *
+ * This function writes data of an arbitrary length to the sensor, starting
+ * from an assigned register address of the sensor, via I2C with a retry
+ * mechanism.
+ */
+static int synaptics_rmi4_i2c_write(struct synaptics_ds4_rmi4_data *pdata,
+			unsigned short addr, unsigned char *data,
+			unsigned short length)
+{
+	int retval = 0;
+	unsigned char buf[length + 1];
+
+	struct i2c_msg msg[] = {
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = 0,
+			.len = length + 1,
+			.buf = buf,
+		}
+	};
+
+	mutex_lock(&(pdata->io_ctrl_mutex));
+	retval = synaptics_rmi4_set_page(pdata, addr);
+	if (retval != PAGE_LEN)
+		goto exit;
+
+	buf[0] = addr & MASK_8BIT;
+	memcpy(&buf[1], &data[0], length);
+
+	retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg));
+	if (retval != ARRAY_SIZE(msg))
+		retval = -EIO;
+exit:
+	mutex_unlock(&(pdata->io_ctrl_mutex));
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_report_device() when valid Function $11
+ * finger data has been detected.
+ *
+ * This function reads the Function $11 data registers, determines the
+ * status of each finger supported by the Function, processes any
+ * necessary coordinate manipulation, reports the finger data to
+ * the input subsystem
+ */
+static int synaptics_rmi4_abs_report(struct synaptics_ds4_rmi4_data *pdata,
+			struct synaptics_ds4_rmi4_fn *function_handler)
+{
+	int touch_count = 0;	/* number of touch points */
+	int finger;
+	int fingers_supported;
+	int finger_registers;
+	int reg;
+	int finger_shift;
+	int finger_status;
+	int retval;
+	unsigned short data_base_addr;
+	unsigned short data_offset;
+	unsigned char data_reg_blk_size;
+	unsigned char values[2];
+	unsigned char data[DATA_LEN];
+	int x[RMI4_NUMBER_OF_MAX_FINGERS];
+	int y[RMI4_NUMBER_OF_MAX_FINGERS];
+	int wx[RMI4_NUMBER_OF_MAX_FINGERS];
+	int wy[RMI4_NUMBER_OF_MAX_FINGERS];
+
+	/* get 2D sensor finger data */
+	/*
+	 * First get the finger status field - the size of the finger status
+	 * field is determined by the number of finger supporte - 2 bits per
+	 * finger, so the number of registers to read is:
+	 * registerCount = ceil(numberOfFingers/4).
+	 * Read the required number of registers and check each 2 bit field to
+	 * determine if a finger is down:
+	 *	00 = finger not present,
+	 *	01 = finger present and data accurate,
+	 *	10 = finger present but data may not be accurate,
+	 *	11 = reserved for product use.
+	 */
+	fingers_supported = function_handler->num_of_data_points;
+	finger_registers = (fingers_supported + 3) / 4;
+	data_base_addr = function_handler->fn_full_addr.data_base_addr;
+
+	retval = synaptics_rmi4_i2c_read(pdata, data_base_addr, values,
+							finger_registers);
+	if (retval < 0)
+		return retval;
+	/*
+	 * For each finger present, read the proper number of registers
+	 * to get absolute data.
+	 */
+	data_reg_blk_size = function_handler->size_of_data_register_block;
+	for (finger = 0; finger < fingers_supported; finger++) {
+		/* 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	= (values[reg] >> finger_shift) & 3;
+		/*
+		 * If finger status indicates a finger is present then
+		 * read the finger data and report it
+		 */
+		if (finger_status == 1 || finger_status == 2) {
+			/* Read the finger data */
+			data_offset = data_base_addr +
+					((finger * data_reg_blk_size) +
+					finger_registers);
+			retval = synaptics_rmi4_i2c_read(pdata,
+						data_offset, data,
+						data_reg_blk_size);
+			if (retval < 0)
+				return retval;
+			else {
+				x[touch_count] =
+					(data[0] << 4) | (data[2] & MASK_4BIT);
+				y[touch_count] =
+					(data[1] << 4) |
+					((data[2] >> 4) & MASK_4BIT);
+				wy[touch_count] = (data[3] >> 4) & MASK_4BIT;
+				wx[touch_count] = (data[3] & MASK_4BIT);
+
+				if (pdata->board->x_flip)
+					x[touch_count] =
+						pdata->sensor_max_x -
+								x[touch_count];
+				if (pdata->board->y_flip)
+					y[touch_count] =
+						pdata->sensor_max_y -
+								y[touch_count];
+			}
+			/* Number of active touch points */
+			touch_count++;
+		}
+	}
+
+	/* Report to input subsystem */
+	if (touch_count) {
+		for (finger = 0; finger < touch_count; finger++) {
+			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
+						max(wx[finger] , wy[finger]));
+			input_report_abs(pdata->input_dev, ABS_MT_POSITION_X,
+								x[finger]);
+			input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y,
+								y[finger]);
+			input_mt_sync(pdata->input_dev);
+		}
+	} else
+		input_mt_sync(pdata->input_dev);
+
+	/* sync after groups of events */
+	input_sync(pdata->input_dev);
+	return touch_count;
+}
+
+/*
+ * Called by synaptics_rmi4_sensor_report().
+ *
+ * This function calls the appropriate finger data reporting function
+ * based on the function handler it receives
+ */
+static int synaptics_rmi4_report_device(struct synaptics_ds4_rmi4_data *pdata,
+				struct synaptics_ds4_rmi4_fn *function_handler)
+{
+	int touch_count = 0;
+	struct i2c_client *client = pdata->i2c_client;
+
+	dev_dbg(&pdata->i2c_client->dev, "%s: function number 0x%X\n",
+				__func__, function_handler->fn_number);
+
+	switch (function_handler->fn_number) {
+	case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC:
+		touch_count = synaptics_rmi4_abs_report(
+						pdata, function_handler);
+		break;
+	default:
+		dev_info(&client->dev, "%s: F%02X not supported\n",
+					__func__, function_handler->fn_number);
+		break;
+	}
+	return touch_count;
+}
+
+/*
+ * Called by synaptics_rmi4_irq().
+ *
+ * This function determines the interrupt source(s) from the sensor and
+ * calls synaptics_rmi4_report_device() with the appropriate function
+ * handler for each function with valid data inputs.
+ */
+static int synaptics_rmi4_sensor_report(struct synaptics_ds4_rmi4_data *pdata)
+{
+	unsigned char	intr_status[MAX_INTR_REGISTERS];
+	int retval;
+	int touch_count = 0;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	struct synaptics_ds4_rmi4_device_info *rmi;
+
+	/*
+	 * Get the interrupt status from the function $01
+	 * control register+1 to find which source(s) were interrupting
+	 * so we can read the data from the source(s) (2D sensor, buttons..)
+	 */
+	retval = synaptics_rmi4_i2c_read(pdata,
+					pdata->fn01_data_base_addr + 1,
+					intr_status,
+					pdata->number_of_interrupt_register);
+	if (retval < 0)
+		return 0;
+
+	if (pdata->touch_stopped) {
+		dev_warn(&pdata->i2c_client->dev,
+			 "Not ready to handle interrupts yet!\n");
+		return 0;
+	}
+	/*
+	 * Check each function that has data sources and if the interrupt for
+	 * that triggered then call that RMI4 functions report() function to
+	 * gather data and report it to the input subsystem
+	 */
+	rmi = &(pdata->rmi4_mod_info);
+	list_for_each_entry(function_handler, &rmi->support_fn_list, link) {
+		if (function_handler->num_of_data_sources) {
+			if (intr_status[function_handler->index_to_intr_reg] &
+						function_handler->intr_mask)
+				touch_count = synaptics_rmi4_report_device(
+						pdata, function_handler);
+		}
+	}
+	return touch_count;
+}
+
+/*
+ * Called by the kernel when an interrupt occurs (when the sensor asserts
+ * the attention irq.
+ *
+ * This function is the ISR thread and handles the acquisition and
+ * the reporting of finger data when the presence of fingers is detected.
+ */
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
+{
+	struct synaptics_ds4_rmi4_data *pdata = data;
+	int touch_count;
+	do {
+		touch_count = synaptics_rmi4_sensor_report(pdata);
+		if (touch_count)
+			wait_event_timeout(pdata->wait, pdata->touch_stopped,
+							msecs_to_jiffies(1));
+		else
+			break;
+	} while (!pdata->touch_stopped);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Called by synaptics_rmi4_probe() and the power management functions in
+ * this driver
+ *
+ * This function handles the enabling of the attention irq
+ * including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_enable(struct synaptics_ds4_rmi4_data *pdata,
+					bool request)
+{
+	int retval = 0;
+	char intr_status;
+	const struct synaptics_dsx_platform_data *platformdata =
+				pdata->i2c_client->dev.platform_data;
+
+	mutex_lock(&pdata->irq_request_mutex);
+	if (pdata->irq_enabled)
+		goto exit;
+
+	/* Clear interrupts */
+	retval = synaptics_rmi4_i2c_read(pdata,
+					pdata->fn01_data_base_addr + 1,
+					&intr_status,
+					pdata->number_of_interrupt_register);
+	if (retval < 0)
+		return retval;
+	if (request) {
+		retval = request_threaded_irq(pdata->irq, NULL,
+					  synaptics_rmi4_irq,
+					  platformdata->irq_type,
+					  DRIVER_NAME, pdata);
+		if (retval) {
+			dev_err(&pdata->i2c_client->dev,
+				"%s:Unable to get attn irq %d, type %d\n",
+				__func__,
+				pdata->irq,
+				platformdata->irq_type);
+			goto exit;
+		}
+	} else
+		enable_irq(pdata->irq);
+	pdata->irq_enabled  = true;
+
+exit:
+	mutex_unlock(&pdata->irq_request_mutex);
+	return retval;
+}
+
+/*
+ * Called by synaptics_rmi4_probe() and the power management functions in
+ * this driver
+ *
+ * This function handles the disabling of the attention irq
+ * including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_disable(struct synaptics_ds4_rmi4_data *pdata,
+					bool release)
+{
+	int retval = 0;
+	mutex_lock(&pdata->irq_request_mutex);
+
+	if (pdata->irq_enabled) {
+		disable_irq(pdata->irq);
+		if (release)
+			free_irq(pdata->irq, pdata);
+		pdata->irq_enabled = false;
+	} else
+		dev_warn(&pdata->i2c_client->dev,
+			 "%s:irq has not been reqested\n", __func__);
+	mutex_unlock(&pdata->irq_request_mutex);
+	return retval;
+}
+
+/*
+ * Called by synaptics_rmi4_query_device().
+ *
+ * This funtion parses information from the Function 11 registers and
+ * determines the number of fingers supported, x and y data ranges,
+ * offset to the associated interrupt status register, interrupt bit mask,
+ * and gathers finger data acquisition capabilities from the query registers.
+ */
+static int synaptics_rmi4_2d_touch_detect(
+				struct synaptics_ds4_rmi4_data *pdata,
+				struct synaptics_ds4_rmi4_fn *function_handler,
+				struct synaptics_ds4_rmi4_fn_desc *fd,
+				unsigned int interruptcount)
+{
+	unsigned char	queries[QUERY_LEN];
+	unsigned char data[BUF_LEN];
+	unsigned char	abs_data_size;
+	unsigned char	abs_data_blk_size;
+	unsigned short intr_offset;
+	int i;
+	int retval;
+
+	function_handler->fn_number = fd->fn_number;
+	function_handler->num_of_data_sources = fd->intr_src_count;
+
+	/*
+	 * 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 = synaptics_rmi4_i2c_read(pdata,
+				function_handler->fn_full_addr.query_base_addr,
+				queries, sizeof(queries));
+	if (retval < 0)
+		return retval;
+
+	/* Number of fingers */
+	if ((queries[1] & MASK_3BIT) <= 4)	/* add 1 since zero based */
+		function_handler->num_of_data_points =
+					(queries[1] & MASK_3BIT) + 1;
+	else
+		if ((queries[1] & MASK_3BIT) == 5)
+			function_handler->num_of_data_points = 10;
+
+	/* Max x/y */
+	retval = synaptics_rmi4_i2c_read(pdata,
+				function_handler->fn_full_addr.ctrl_base_addr,
+				data, DATA_BUF_LEN);
+	if (retval < 0)
+		return retval;
+
+	/* Store these for use later*/
+	pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) |
+						((data[7] & MASK_4BIT) << 8);
+	pdata->sensor_max_y = ((data[8] & MASK_8BIT) << 0) |
+						((data[9] & MASK_4BIT) << 8);
+
+
+	/* Interrupt info for handling interrupts */
+	function_handler->index_to_intr_reg = (interruptcount + 7) / 8;
+	if (function_handler->index_to_intr_reg != 0)
+		function_handler->index_to_intr_reg -= 1;
+	/*
+	 * Loop through interrupts for each source in fn $11
+	 * and or in a bit to the interrupt mask for each.
+	 */
+	intr_offset = interruptcount % 8;
+	function_handler->intr_mask = 0;
+	for (i = intr_offset;
+		i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++)
+		function_handler->intr_mask |= 1 << i;
+
+	/* Size of just the absolute data for one finger */
+	abs_data_size	= queries[5] & MASK_2BIT;
+
+	/* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+	abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+	function_handler->size_of_data_register_block = abs_data_blk_size;
+
+	return retval;
+}
+
+static int synaptics_rmi4_alloc_func_handler(
+			struct synaptics_ds4_rmi4_fn **function_handler,
+			struct synaptics_ds4_rmi4_fn_desc *rmi_fd,
+			int page_number)
+{
+	*function_handler =
+		kmalloc(sizeof(struct synaptics_ds4_rmi4_fn), GFP_KERNEL);
+	if (!(*function_handler))
+		return -ENOMEM;
+
+	(*function_handler)->fn_full_addr.data_base_addr =
+		(rmi_fd->data_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.ctrl_base_addr =
+		(rmi_fd->ctrl_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.cmd_base_addr =
+		(rmi_fd->cmd_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.query_base_addr =
+		(rmi_fd->query_base_addr | (page_number << 8));
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_probe().
+ *
+ * This funtion scans the page description table, records the offsets to the
+ * register types of Function $01, sets up the function handlers for Function
+ * $11, determines the number of interrupt sources from the
+ * sensor, adds valid Functions with data inputs to the Function linked list,
+ * parses information from the query registers of Function $01, and enables
+ * the interrupt sources from the valid Functions with data inputs.
+ */
+static int synaptics_rmi4_query_device(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int i, page_number;
+	int retval;
+	int data_sources = 0;
+	unsigned char std_queries[STD_QUERY_LEN];
+	unsigned char interrupt_mask[MAX_INTR_REGISTERS];
+	unsigned char intr_count = 0;
+	unsigned char intr_index = 0;
+	unsigned short ctrl_offset;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	struct synaptics_ds4_rmi4_fn_desc rmi_fd;
+	struct synaptics_ds4_rmi4_device_info *rmi;
+	struct i2c_client *client = pdata->i2c_client;
+
+	/* Init the physical drivers RMI module info list of functions */
+	INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list);
+
+	/* Scan the Page Descriptor Table */
+	for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
+		for (i = SYNAPTICS_DS4_RMI4_PDT_START;
+			i > SYNAPTICS_DS4_RMI4_PDT_END;
+			i -= SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE) {
+			function_handler = NULL;
+			i |= (page_number << 8);
+
+			retval = synaptics_rmi4_i2c_read(pdata, i,
+						(unsigned char *)&rmi_fd,
+						sizeof(rmi_fd));
+			if (retval < 0)
+				return retval;
+
+			if (rmi_fd.fn_number == 0)	/* end of the PDT */
+				break;
+			dev_dbg(&pdata->i2c_client->dev,
+					"%s: function F%02X is present\n",
+					__func__, rmi_fd.fn_number);
+			switch (rmi_fd.fn_number & MASK_8BIT) {
+			case SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC:
+				pdata->fn01_query_base_addr =
+						rmi_fd.query_base_addr;
+				pdata->fn01_ctrl_base_addr =
+						rmi_fd.ctrl_base_addr;
+				pdata->fn01_data_base_addr =
+						rmi_fd.data_base_addr;
+				break;
+			case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC:
+				if (rmi_fd.intr_src_count) {
+					retval =
+					synaptics_rmi4_alloc_func_handler(
+							&function_handler,
+							&rmi_fd,
+							page_number);
+					if (retval < 0)
+						return retval;
+					retval =
+					synaptics_rmi4_2d_touch_detect(
+							pdata,
+							function_handler,
+							&rmi_fd,
+							intr_count);
+					if (retval < 0)
+						return retval;
+				}
+				break;
+			}
+			/* interrupt count for next iteration */
+			intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+			/*
+			 * add functions to the list that have data associated
+			 */
+			if (function_handler && rmi_fd.intr_src_count) {
+				/* link this function info to RMI module */
+				mutex_lock(&(pdata->fn_list_mutex));
+				dev_dbg(&pdata->i2c_client->dev,
+				    "%s: add fiunction handler 0x%X to list\n",
+				    __func__, function_handler->fn_number);
+				list_add_tail(&function_handler->link,
+					&pdata->rmi4_mod_info.support_fn_list);
+				mutex_unlock(&(pdata->fn_list_mutex));
+			}
+		}
+	}
+	/*
+	 * calculate the interrupt register count - used in the
+	 * ISR to read the correct number of interrupt registers
+	 */
+	pdata->number_of_interrupt_register = (intr_count + 7) / 8;
+	dev_dbg(&client->dev, "%s: interrupt register count :%d\n",
+					__func__,
+					pdata->number_of_interrupt_register);
+
+	/* Load up the standard queries and get the RMI4 module info */
+	retval = synaptics_rmi4_i2c_read(pdata, pdata->fn01_query_base_addr,
+					std_queries, sizeof(std_queries));
+	if (retval < 0)
+		return retval;
+
+	/*
+	 * get manufacturer id, product_props, product info,
+	 * date code, tester id, serial num and product id (name)
+	 */
+	pdata->rmi4_mod_info.manufacturer_id = std_queries[0];
+	pdata->rmi4_mod_info.product_props = std_queries[1];
+	pdata->rmi4_mod_info.product_info[0] = std_queries[2];
+	pdata->rmi4_mod_info.product_info[1] = std_queries[3];
+	/* year - 2001-2032 */
+	pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT;
+	/* month - 1-12 */
+	pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT;
+	/* day - 1-31 */
+	pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT;
+	pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) |
+						(std_queries[8] & MASK_7BIT);
+	pdata->rmi4_mod_info.serial_number =
+				((std_queries[9] & MASK_7BIT) << 8) |
+				(std_queries[10] & MASK_7BIT);
+	memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10);
+
+	/* Check if this is a Synaptics device - report if not. */
+	if (pdata->rmi4_mod_info.manufacturer_id != 1)
+			dev_err(&client->dev, "%s: non-Synaptics mfg id:%d\n",
+					__func__,
+					pdata->rmi4_mod_info.manufacturer_id);
+
+	memset(interrupt_mask, 0x00, sizeof(interrupt_mask));
+	list_for_each_entry(function_handler,
+			&pdata->rmi4_mod_info.support_fn_list, link)
+			data_sources += function_handler->num_of_data_sources;
+	if (data_sources) {
+		rmi = &(pdata->rmi4_mod_info);
+		list_for_each_entry(function_handler,
+					&rmi->support_fn_list, link) {
+			if (function_handler->num_of_data_sources)
+				intr_index =
+					function_handler->index_to_intr_reg;
+				interrupt_mask[intr_index] |=
+				function_handler->intr_mask;
+		}
+	}
+
+	for (i = 0; i < MAX_INTR_REGISTERS; i++) {
+		if (interrupt_mask[i] != 0x00) {
+			dev_dbg(&client->dev,
+					"%s: interrupt %d enable mask :0x%X\n",
+					__func__, i, interrupt_mask[i]);
+			ctrl_offset = pdata->fn01_ctrl_base_addr + 1 + i;
+			retval = synaptics_rmi4_i2c_write(pdata,
+						ctrl_offset,
+						&(interrupt_mask[i]),
+						sizeof(interrupt_mask[i]));
+			if (retval < 0)
+				return retval;
+		}
+	}
+	return 0;
+}
+
+static int __devinit synaptics_rmi4_probe(
+		struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+	int retval;
+	struct synaptics_ds4_rmi4_data *pdata;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	const struct synaptics_dsx_platform_data *platformdata =
+						client->dev.platform_data;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "i2c smbus byte data not supported\n");
+		return -EIO;
+	}
+
+	if (!platformdata) {
+		dev_err(&client->dev, "%s: no platform data\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Allocate and initialize the instance data for this client */
+	pdata = kzalloc(sizeof(struct synaptics_ds4_rmi4_data) * 2,
+							GFP_KERNEL);
+	if (!pdata) {
+		dev_err(&client->dev, "%s: no memory allocated\n", __func__);
+		return -ENOMEM;
+	}
+
+	pdata->input_dev = input_allocate_device();
+	if (pdata->input_dev == NULL) {
+		dev_err(&client->dev, "%s:input device alloc failed\n",
+						__func__);
+		retval = -ENOMEM;
+		goto err_input;
+	}
+
+	if (platformdata->regulator_en) {
+		pdata->regulator = regulator_get(&client->dev, "vdd");
+		if (IS_ERR(pdata->regulator)) {
+			dev_err(&client->dev, "%s:get regulator failed\n",
+								__func__);
+			retval = PTR_ERR(pdata->regulator);
+			goto err_regulator;
+		}
+		regulator_enable(pdata->regulator);
+	}
+	init_waitqueue_head(&pdata->wait);
+	pdata->i2c_client		= client;
+	pdata->current_page	= MASK_16BIT;
+	pdata->board		= platformdata;
+	pdata->sensor_sleep	= false;
+	pdata->irq_enabled	= false;
+	pdata->touch_stopped	= false;
+
+	/* Init the mutexes for maintain the lists */
+	mutex_init(&(pdata->fn_list_mutex));
+	mutex_init(&(pdata->io_ctrl_mutex));
+
+	/*
+	 * Register physical driver - this will call the detect function that
+	 * will then scan the device and determine the supported
+	 * rmi4 functions.
+	 */
+	retval = synaptics_rmi4_query_device(pdata);
+	if (retval) {
+		dev_err(&client->dev, "%s: rmi4 query device failed\n",
+							__func__);
+		goto err_query_dev;
+	}
+
+	/* Store the instance data in the i2c_client */
+	i2c_set_clientdata(client, pdata);
+
+	/* Initialize the input device parameters */
+	pdata->input_dev->name = DRIVER_NAME;
+	pdata->input_dev->phys = INPUT_PHYS_NAME;
+	pdata->input_dev->id.bustype = BUS_I2C;
+	pdata->input_dev->dev.parent = &client->dev;
+	input_set_drvdata(pdata->input_dev, pdata);
+
+	/* Initialize the function handlers for rmi4 */
+	set_bit(EV_SYN, pdata->input_dev->evbit);
+	set_bit(EV_KEY, pdata->input_dev->evbit);
+	set_bit(EV_ABS, pdata->input_dev->evbit);
+#ifdef INPUT_PROP_DIRECT
+	set_bit(INPUT_PROP_DIRECT, pdata->input_dev->propbit);
+#endif
+
+	input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_X, 0,
+						pdata->sensor_max_x, 0, 0);
+	input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_Y, 0,
+						pdata->sensor_max_y, 0, 0);
+	input_set_abs_params(pdata->input_dev, ABS_MT_TOUCH_MAJOR, 0,
+						MAX_TOUCH_MAJOR, 0, 0);
+
+	retval = input_register_device(pdata->input_dev);
+	if (retval) {
+		dev_err(&client->dev, "%s:input register failed\n", __func__);
+		goto err_query_dev;
+	}
+
+	/* Gpio configuration */
+	if (platformdata->gpio_config) {
+		retval = platformdata->gpio_config(platformdata->gpio, true);
+		if (retval < 0) {
+			dev_err(&client->dev,
+				"Failed to configure GPIOs, code: %d.\n",
+				retval);
+			return retval;
+		}
+		dev_info(&client->dev, "Done with GPIO configuration.\n");
+	}
+	pdata->irq = gpio_to_irq(platformdata->gpio);
+	mutex_init(&(pdata->irq_request_mutex));
+	pdata->touch_stopped = false;
+	retval = synaptics_rmi4_irq_enable(pdata, true);
+	if (retval) {
+		dev_err(&client->dev,
+			"%s:Unable to get attn irq %d, type %d\n, name: %s",
+			__func__, pdata->irq, platformdata->irq_type,
+			DRIVER_NAME);
+		goto err_request_irq;
+	}
+
+	return retval;
+
+err_request_irq:
+	input_unregister_device(pdata->input_dev);
+err_query_dev:
+	if (platformdata->regulator_en) {
+		regulator_disable(pdata->regulator);
+		regulator_put(pdata->regulator);
+	}
+
+	if (!list_empty(&pdata->rmi4_mod_info.support_fn_list)) {
+		list_for_each_entry(function_handler,
+			&pdata->rmi4_mod_info.support_fn_list, link) {
+			if (function_handler) {
+				kfree(function_handler->data);
+				kfree(function_handler);
+			}
+		}
+	}
+
+err_regulator:
+	input_free_device(pdata->input_dev);
+	pdata->input_dev = NULL;
+err_input:
+	kfree(pdata);
+
+	return retval;
+}
+
+static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
+{
+	struct synaptics_ds4_rmi4_data *pdata = i2c_get_clientdata(client);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	pdata->touch_stopped = true;
+	synaptics_rmi4_irq_disable(pdata, true);
+
+	input_unregister_device(pdata->input_dev);
+	if (platformdata->regulator_en) {
+		regulator_disable(pdata->regulator);
+		regulator_put(pdata->regulator);
+	}
+	kfree(pdata);
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_suspend().
+ *
+ * This function stops finger data acquisition and puts the sensor to sleep.
+ */
+static void synaptics_rmi4_sensor_sleep(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int retval;
+	unsigned char device_ctrl;
+
+	if (pdata->sensor_sleep == true)
+		return;
+
+	pdata->touch_stopped = true;
+	wake_up(&pdata->wait);
+
+	retval = synaptics_rmi4_i2c_read(pdata,
+				pdata->fn01_ctrl_base_addr,
+				&device_ctrl,
+				1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to enter sleep mode. Code: %d.\n", retval);
+		pdata->sensor_sleep = false;
+		return;
+	}
+
+	device_ctrl = (device_ctrl & ~MASK_3BIT);
+	device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP);
+
+	retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr,
+						&device_ctrl, 1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to enter sleep mode. Code: %d.\n", retval);
+		pdata->sensor_sleep = false;
+		return;
+	} else
+		pdata->sensor_sleep = true;
+
+	return;
+}
+
+/*
+ * Called by synaptics_rmi4_resume().
+ *
+ * This function wakes the sensor from sleep.
+ */
+static void synaptics_rmi4_sensor_wake(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int retval;
+	unsigned char device_ctrl;
+
+	if (pdata->sensor_sleep == false)
+		return;
+	retval = synaptics_rmi4_i2c_read(pdata,
+				pdata->fn01_ctrl_base_addr,
+				&device_ctrl,
+				1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to wake from sleep mode. Code: %d.\n",
+			retval);
+		pdata->sensor_sleep = true;
+		return;
+	}
+
+	device_ctrl = (device_ctrl & ~MASK_3BIT);
+	device_ctrl = (device_ctrl | NO_SLEEP_OFF | NORMAL_OPERATION);
+
+	retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr,
+						&device_ctrl, 1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to wake from sleep mode. Code: %d.\n",
+			retval);
+		pdata->sensor_sleep = true;
+		return;
+	} else
+		pdata->sensor_sleep = false;
+
+	return;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Called by the kernel during the suspend phase when the system
+ * enters suspend.
+ *
+ * This function stops finger data acquisition and puts the sensor to
+ * sleep, disables the interrupt, and turns off the power to the sensor.
+ */
+static int synaptics_rmi4_suspend(struct device *dev)
+{
+	struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	synaptics_rmi4_irq_disable(pdata, false);
+	synaptics_rmi4_sensor_sleep(pdata);
+
+	if (platformdata->regulator_en)
+		regulator_disable(pdata->regulator);
+	return 0;
+}
+
+/*
+ * Called by the kernel during the resume phase when the system
+ * wakes up from suspend.
+ *
+ * This function turns on the power to the sensor, wakes the sensor
+ * from sleep, enables the interrupt, and starts finger data
+ * acquisition.
+ */
+static int synaptics_rmi4_resume(struct device *dev)
+{
+	struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	if (platformdata->regulator_en)
+		regulator_enable(pdata->regulator);
+
+	synaptics_rmi4_sensor_wake(pdata);
+	synaptics_rmi4_irq_enable(pdata, false);
+	pdata->touch_stopped = false;
+
+	return 0;
+}
+
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+	.suspend = synaptics_rmi4_suspend,
+	.resume  = synaptics_rmi4_resume,
+};
+#endif
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+	.driver = {
+		.name	=	DRIVER_NAME,
+		.owner	=	THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	=	&synaptics_rmi4_dev_pm_ops,
+#endif
+	},
+	.probe		=	synaptics_rmi4_probe,
+	.remove		=	__devexit_p(synaptics_rmi4_remove),
+	.id_table	=	synaptics_rmi4_id_table,
+};
+
+static int __init synaptics_rmi4_init(void)
+{
+	return i2c_add_driver(&synaptics_rmi4_driver);
+}
+
+static void __exit synaptics_rmi4_exit(void)
+{
+	i2c_del_driver(&synaptics_rmi4_driver);
+}
+
+module_init(synaptics_rmi4_init);
+module_exit(synaptics_rmi4_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("synaptics ds4 i2c touch driver");
diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
new file mode 100644
index 0000000..58329d9
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
@@ -0,0 +1,94 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYNAPTICS_DS4_RMI4_H_
+#define _SYNAPTICS_DS4_RMI4_H_
+
+#define SYNAPTICS_DS4_RMI4_PDT_START				(0x00E9)
+#define SYNAPTICS_DS4_RMI4_PDT_END				(0x000A)
+#define SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE		(0x0006)
+#define SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC	(0x01)
+#define SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC		(0x11)
+
+/* funtion descriptor information */
+struct synaptics_ds4_rmi4_fn_desc {
+	unsigned char	query_base_addr;
+	unsigned char	cmd_base_addr;
+	unsigned char	ctrl_base_addr;
+	unsigned char	data_base_addr;
+	unsigned char	intr_src_count;
+	unsigned char	fn_number;
+};
+
+/*  funtion information */
+struct synaptics_ds4_rmi4_fn {
+	unsigned char fn_number;
+	unsigned char num_of_data_sources;
+	unsigned char num_of_data_points;
+	unsigned char size_of_data_register_block;
+	unsigned char index_to_intr_reg;
+	unsigned char intr_mask;
+	struct synaptics_ds4_rmi4_fn_desc fn_full_addr;
+	struct list_head link;
+	void *data;
+};
+
+/* device information */
+struct synaptics_ds4_rmi4_device_info {
+	unsigned char manufacturer_id;
+	unsigned char product_props;
+	unsigned char product_info[2];
+	unsigned char date_code[3];
+	unsigned short tester_id;
+	unsigned short serial_number;
+	unsigned char product_id_string[11];
+	struct list_head support_fn_list;
+};
+
+/* ds4 rmi4 touch screen data */
+struct synaptics_ds4_rmi4_data {
+	const struct synaptics_dsx_platform_data *board;
+	struct input_dev *input_dev;
+	struct i2c_client *i2c_client;
+	struct mutex fn_list_mutex;
+	struct mutex io_ctrl_mutex;
+	struct mutex irq_request_mutex;
+	struct synaptics_ds4_rmi4_device_info rmi4_mod_info;
+	struct regulator *regulator;
+	unsigned int number_of_interrupt_register;
+	unsigned short current_page;
+	unsigned short fn01_ctrl_base_addr;
+	unsigned short fn01_query_base_addr;
+	unsigned short fn01_data_base_addr;
+	unsigned short sensor_max_x;
+	unsigned short sensor_max_y;
+	wait_queue_head_t	 wait;
+	bool touch_stopped;
+	bool irq_enabled;
+	bool sensor_sleep;
+	int irq;
+};
+
+#endif
diff --git a/include/linux/input/synaptics_dsx.h b/include/linux/input/synaptics_dsx.h
new file mode 100644
index 0000000..4f0d0d6
--- /dev/null
+++ b/include/linux/input/synaptics_dsx.h
@@ -0,0 +1,49 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYNAPTICS_DSX_H_
+#define _SYNAPTICS_DSX_H_
+
+/*
+ * struct synaptics_dsx_platform_data - contains the dsx platform data
+ * @x_flip: x flip flag
+ * @y_flip: y flip flag
+ * @regulator_en: regulator enable flag
+ * @irq_type: irq type
+ * @gpio: gpio pin assignment
+ * @gpio_config: callback for gpio set up
+ *
+ * This structure gives platform data for dsx.
+ */
+struct synaptics_dsx_platform_data {
+	bool x_flip;
+	bool y_flip;
+	bool regulator_en;
+	int irq_type;
+	unsigned gpio;
+	int (*gpio_config)(unsigned interrupt_gpio, bool configure);
+};
+
+#endif
-- 
1.7.5.4


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
  2012-09-16 22:33 ` Linus Walleij
@ 2012-09-19 21:57   ` Christopher Heiny
  0 siblings, 0 replies; 12+ messages in thread
From: Christopher Heiny @ 2012-09-19 21:57 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Alexandra Chin, Henrik Rydberg, Dmitry Torokhov, Linux Kernel,
	Linux Input, Linus Walleij, Naveen Kumar Gaddipati,
	Mahesh Srinivasan, Alex Chang, Scott Lin

On 09/16/2012 03:33 PM, Linus Walleij wrote:
> On Sun, Sep 16, 2012 at 11:56 AM, Alexandra Chin
> <alexandra.chin@tw.synaptics.com> wrote:
>
>> Synaptics DS4 touchscreen driver implements a generic driver supporting I2C
>> protocol for Synaptics Design Studio 4 (DS4) family of Touchscreen Controllers
>> which include the following:
>>
>> - S32xX series
>> - S730X series
>> - S22xx series
>>
>> The driver supports multifinger pointing functionality and power management.
>> The driver is based on the original work submitted by
>> Linus Walleij <linus.walleij@stericsson.com> and
>> Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>.
>
> Naveen's driver is sitting in drivers/staging/ste_rmi4 right now because
> Christopher Heiny is working on a RMI4 bus driver, and wanted to
> merge that.
>
> I am happy with this being merged for now if the plan is
> to eventually replace it with Christopher's (et al) driver,
> if it's not going to disturb the other driver in the end.
>
> How are Synaptics planning to handle the transition?
> (I assume you are coordinated... not always the case in
> big companies, I know from experience :-)
>
> Remember to keep Henrik Rydberg in the loop on touch
> controllers.

Hi Linus,

The RMI4 bus driver will continue to be developed by my team, with the 
intent that it will support all RMI4 functions as well as all Synaptics 
RMI4 products, and the goal of eventual inclusion in the kernel. 
However, as you've noted elsewhere, this is taking a while. :-)

In the meantime, there is a perceived customer pull for a limited 
functionality product/platform specific "thin driver".  Alexandra's team 
has been working on such a driver, resulting in her patch submission 
from a couple of days ago.  They really would like to see both our 
drivers being adopted into the kernel. This will enable them to support 
all our customers effectively and provide our customers with the best 
flexibility possible. Going forward, Synaptics will continue to 
maintain, support and upgrade these drivers for future touch controller 
platforms as well.

						Chris

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
  2012-09-16  9:56 Alexandra Chin
@ 2012-09-16 22:33 ` Linus Walleij
  2012-09-19 21:57   ` Christopher Heiny
  0 siblings, 1 reply; 12+ messages in thread
From: Linus Walleij @ 2012-09-16 22:33 UTC (permalink / raw)
  To: Alexandra Chin, Christopher Heiny, Henrik Rydberg
  Cc: Dmitry Torokhov, Linux Kernel, Linux Input, Linus Walleij,
	Naveen Kumar Gaddipati, Mahesh Srinivasan, Alex Chang, Scott Lin

On Sun, Sep 16, 2012 at 11:56 AM, Alexandra Chin
<alexandra.chin@tw.synaptics.com> wrote:

> Synaptics DS4 touchscreen driver implements a generic driver supporting I2C
> protocol for Synaptics Design Studio 4 (DS4) family of Touchscreen Controllers
> which include the following:
>
> - S32xX series
> - S730X series
> - S22xx series
>
> The driver supports multifinger pointing functionality and power management.
> The driver is based on the original work submitted by
> Linus Walleij <linus.walleij@stericsson.com> and
> Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>.

Naveen's driver is sitting in drivers/staging/ste_rmi4 right now because
Christopher Heiny is working on a RMI4 bus driver, and wanted to
merge that.

I am happy with this being merged for now if the plan is
to eventually replace it with Christopher's (et al) driver,
if it's not going to disturb the other driver in the end.

How are Synaptics planning to handle the transition?
(I assume you are coordinated... not always the case in
big companies, I know from experience :-)

Remember to keep Henrik Rydberg in the loop on touch
controllers.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices
@ 2012-09-16  9:56 Alexandra Chin
  2012-09-16 22:33 ` Linus Walleij
  0 siblings, 1 reply; 12+ messages in thread
From: Alexandra Chin @ 2012-09-16  9:56 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Linux Kernel, Linux Input, Linus Walleij, Naveen Kumar Gaddipati,
	Mahesh Srinivasan, Alex Chang, Scott Lin

Synaptics DS4 touchscreen driver implements a generic driver supporting I2C
protocol for Synaptics Design Studio 4 (DS4) family of Touchscreen Controllers
which include the following:

- S32xX series
- S730X series
- S22xx series

The driver supports multifinger pointing functionality and power management.
The driver is based on the original work submitted by
Linus Walleij <linus.walleij@stericsson.com> and
Naveen Kumar Gaddipati <naveen.gaddipati@stericsson.com>.

This patch is against the v3.1-rc9 tag of Dmitry Torokhov's kernel tree,
commit bd68dfe0071b50bc69416a92ee22b63d1cc33a3b.

Changes in this patch:
 - modified:   drivers/input/touchscreen/Kconfig
 - modified:   drivers/input/touchscreen/Makefile
 - new file:   drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
 - new file:   drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
 - new file:   include/linux/input/synaptics_dsx.h

This patch is functionally tested on omap beagleboard-xm.

We will continue to maintain and support this driver officially, including 
making updates, as well as supporting future Touch Controller revisions 
from Synaptics.

Any comments are much appreciated.

Alexandra Chin

Signed-off-by: Alexandra Chin <alexandra.chin@tw.synaptics.com>
---
 drivers/input/touchscreen/Kconfig                  |   12 +
 drivers/input/touchscreen/Makefile                 |    1 +
 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c | 1085 ++++++++++++++++++++
 drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h |   94 ++
 include/linux/input/synaptics_dsx.h                |   49 +
 5 files changed, 1241 insertions(+), 0 deletions(-)

diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 1ba232c..431c72b 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -900,4 +900,16 @@ config TOUCHSCREEN_TPS6507X
 	  To compile this driver as a module, choose M here: the
 	  module will be called tps6507x_ts.
 
+config TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C
+	tristate "Synaptics ds4 i2c touchscreen"
+	depends on I2C
+	help
+	  Say Y here if you have a Synaptics DS4 I2C touchscreen
+	  connected to your system.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called synaptics_ds4_rmi4_i2c.
+
 endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 178eb12..61f5f22 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -73,3 +73,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE)	+= mainstone-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE)	+= zylonite-wm97xx.o
 obj-$(CONFIG_TOUCHSCREEN_W90X900)	+= w90p910_ts.o
 obj-$(CONFIG_TOUCHSCREEN_TPS6507X)	+= tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DS4_RMI4_I2C)	+= synaptics_ds4_rmi4_i2c.o
diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
new file mode 100644
index 0000000..c3bf46f
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.c
@@ -0,0 +1,1085 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_ds4_rmi4_i2c.h"
+
+#define DRIVER_NAME "synaptics_ds4_i2c"
+#define INPUT_PHYS_NAME "synaptics_ds4_i2c/input0"
+
+#define PAGES_TO_SERVICE 	0xFF
+#define MAX_INTR_REGISTERS 	4
+#define MAX_TOUCH_MAJOR	15
+#define BUF_LEN		37
+#define PAGE_LEN		2
+#define DATA_LEN		12
+#define QUERY_LEN		9
+#define DATA_BUF_LEN		10
+#define STD_QUERY_LEN	21
+
+#define MASK_16BIT		0xFFFF
+#define MASK_8BIT		0xFF
+#define MASK_7BIT		0x7F
+#define MASK_5BIT		0x1F
+#define MASK_4BIT		0x0F
+#define MASK_3BIT		0x07
+#define MASK_2BIT		0x03
+#define TOUCH_CTRL_INTR	0x8
+
+#define NO_SLEEP_ON		(1 << 3)
+#define NO_SLEEP_OFF		(0 << 3)
+#define NORMAL_OPERATION	(0 << 0)
+#define SENSOR_SLEEP		(1 << 0)
+#define RMI4_NUMBER_OF_MAX_FINGERS	(10)
+
+#ifdef CONFIG_PM
+static int synaptics_rmi4_suspend(struct device *dev);
+static int synaptics_rmi4_resume(struct device *dev);
+#endif
+
+/*
+ * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write().
+ *
+ * This function writes to the page select register to switch to the
+ * assigned page.
+ */
+static int synaptics_rmi4_set_page(struct synaptics_ds4_rmi4_data *pdata,
+					unsigned int address)
+{
+	unsigned char	txbuf[PAGE_LEN];
+	int retval = 0;
+	unsigned int page;
+	struct i2c_client *i2c = pdata->i2c_client;
+
+	page = ((address >> 8) & MASK_8BIT);
+	if (page != pdata->current_page) {
+		txbuf[0] = MASK_8BIT;
+		txbuf[1] = page;
+		retval = i2c_master_send(i2c, txbuf, PAGE_LEN);
+		if (retval != PAGE_LEN)
+			retval = -EIO;
+		else
+			pdata->current_page = page;
+	} else
+		retval = PAGE_LEN;
+
+	return retval;
+}
+
+/*
+ * Called by various functions in this driver
+ *
+ * This function reads data of an arbitrary length from the sensor, starting
+ * from an assigned register address of the sensor, via I2C with a retry
+ * mechanism.
+ */
+static int synaptics_rmi4_i2c_read(struct synaptics_ds4_rmi4_data *pdata,
+			unsigned short addr, unsigned char *data,
+			unsigned short length)
+{
+	int retval = 0;
+	unsigned char buf;
+	struct i2c_msg msg[] = {
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = 0,
+			.len = 1,
+			.buf = &buf,
+		},
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = I2C_M_RD,
+			.len = length,
+			.buf = data,
+		}
+	};
+	buf = addr & MASK_8BIT;
+
+	mutex_lock(&(pdata->io_ctrl_mutex));
+	retval = synaptics_rmi4_set_page(pdata, addr);
+	if (retval != PAGE_LEN)
+		goto exit;
+	retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg));
+	if (retval != ARRAY_SIZE(msg))
+		retval = -EIO;
+exit:
+	mutex_unlock(&(pdata->io_ctrl_mutex));
+	return retval;
+}
+
+/*
+ * Called by various functions in this driver
+ *
+ * This function writes data of an arbitrary length to the sensor, starting
+ * from an assigned register address of the sensor, via I2C with a retry
+ * mechanism.
+ */
+static int synaptics_rmi4_i2c_write(struct synaptics_ds4_rmi4_data *pdata,
+			unsigned short addr, unsigned char *data,
+			unsigned short length)
+{
+	int retval = 0;
+	unsigned char buf[length + 1];
+
+	struct i2c_msg msg[] = {
+		{
+			.addr = pdata->i2c_client->addr,
+			.flags = 0,
+			.len = length + 1,
+			.buf = buf,
+		}
+	};
+
+	mutex_lock(&(pdata->io_ctrl_mutex));
+	retval = synaptics_rmi4_set_page(pdata, addr);
+	if (retval != PAGE_LEN)
+		goto exit;
+
+	buf[0] = addr & MASK_8BIT;
+	memcpy(&buf[1], &data[0], length);
+
+	retval = i2c_transfer(pdata->i2c_client->adapter, msg, ARRAY_SIZE(msg));
+	if (retval != ARRAY_SIZE(msg))
+		retval = -EIO;
+exit:
+	mutex_unlock(&(pdata->io_ctrl_mutex));
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_report_device() when valid Function $11
+ * finger data has been detected.
+ *
+ * This function reads the Function $11 data registers, determines the
+ * status of each finger supported by the Function, processes any
+ * necessary coordinate manipulation, reports the finger data to
+ * the input subsystem
+ */
+static int synaptics_rmi4_abs_report(struct synaptics_ds4_rmi4_data *pdata,
+			struct synaptics_ds4_rmi4_fn *function_handler)
+{
+	int touch_count = 0; 	/* number of touch points */
+	int finger;
+	int fingers_supported;
+	int finger_registers;
+	int reg;
+	int finger_shift;
+	int finger_status;
+	int retval;
+	unsigned short data_base_addr;
+	unsigned short data_offset;
+	unsigned char data_reg_blk_size;
+	unsigned char values[2];
+	unsigned char data[DATA_LEN];
+	int x[RMI4_NUMBER_OF_MAX_FINGERS];
+	int y[RMI4_NUMBER_OF_MAX_FINGERS];
+	int wx[RMI4_NUMBER_OF_MAX_FINGERS];
+	int wy[RMI4_NUMBER_OF_MAX_FINGERS];
+
+	/* get 2D sensor finger data */
+	/*
+	 * First get the finger status field - the size of the finger status
+	 * field is determined by the number of finger supporte - 2 bits per
+	 * finger, so the number of registers to read is:
+	 * registerCount = ceil(numberOfFingers/4).
+	 * Read the required number of registers and check each 2 bit field to
+	 * determine if a finger is down:
+	 *	00 = finger not present,
+	 *	01 = finger present and data accurate,
+	 *	10 = finger present but data may not be accurate,
+	 *	11 = reserved for product use.
+	 */
+	fingers_supported = function_handler->num_of_data_points;
+	finger_registers = (fingers_supported + 3) / 4;
+	data_base_addr = function_handler->fn_full_addr.data_base_addr;
+
+	retval = synaptics_rmi4_i2c_read(pdata, data_base_addr, values,
+							finger_registers);
+	if (retval < 0)
+		return retval;
+	/*
+	 * For each finger present, read the proper number of registers
+	 * to get absolute data.
+	 */
+	data_reg_blk_size = function_handler->size_of_data_register_block;
+	for (finger = 0; finger < fingers_supported; finger++) {
+		/* 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	= (values[reg] >> finger_shift) & 3;
+		/*
+		 * If finger status indicates a finger is present then
+		 * read the finger data and report it
+		 */
+		if (finger_status == 1 || finger_status == 2) {
+			/* Read the finger data */
+			data_offset = data_base_addr +
+					((finger * data_reg_blk_size) +
+					finger_registers);
+			retval = synaptics_rmi4_i2c_read(pdata,
+						data_offset, data,
+						data_reg_blk_size);
+			if (retval < 0)
+				return retval;
+			else {
+				x[touch_count] =
+					(data[0] << 4) | (data[2] & MASK_4BIT);
+				y[touch_count] =
+					(data[1] << 4) |
+					((data[2] >> 4) & MASK_4BIT);
+				wy[touch_count] = (data[3] >> 4) & MASK_4BIT;
+				wx[touch_count] = (data[3] & MASK_4BIT);
+
+				if (pdata->board->x_flip)
+					x[touch_count] =
+						pdata->sensor_max_x -
+								x[touch_count];
+				if (pdata->board->y_flip)
+					y[touch_count] =
+						pdata->sensor_max_y -
+								y[touch_count];
+			}
+			/* Number of active touch points */
+			touch_count++;
+		}
+	}
+
+	/* Report to input subsystem */
+	if (touch_count) {
+		for (finger = 0; finger < touch_count; finger++) {
+			input_report_abs(pdata->input_dev, ABS_MT_TOUCH_MAJOR,
+						max(wx[finger] , wy[finger]));
+			input_report_abs(pdata->input_dev, ABS_MT_POSITION_X,
+								x[finger]);
+			input_report_abs(pdata->input_dev, ABS_MT_POSITION_Y,
+								y[finger]);
+			input_mt_sync(pdata->input_dev);
+		}
+	} else
+		input_mt_sync(pdata->input_dev);
+
+	/* sync after groups of events */
+	input_sync(pdata->input_dev);
+	return touch_count;
+}
+
+/*
+ * Called by synaptics_rmi4_sensor_report().
+ *
+ * This function calls the appropriate finger data reporting function
+ * based on the function handler it receives
+ */
+static int synaptics_rmi4_report_device(struct synaptics_ds4_rmi4_data *pdata,
+				struct synaptics_ds4_rmi4_fn *function_handler)
+{
+	int touch_count = 0;
+	struct i2c_client *client = pdata->i2c_client;
+
+	dev_dbg(&pdata->i2c_client->dev, "%s: function number 0x%X\n",
+				__func__, function_handler->fn_number);
+
+	switch (function_handler->fn_number) {
+	case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC:
+		touch_count = synaptics_rmi4_abs_report(
+						pdata, function_handler);
+		break;
+	default:
+		dev_info(&client->dev, "%s: F%02X not supported\n",
+					__func__, function_handler->fn_number);
+		break;
+	}
+	return touch_count;
+}
+
+/*
+ * Called by synaptics_rmi4_irq().
+ *
+ * This function determines the interrupt source(s) from the sensor and
+ * calls synaptics_rmi4_report_device() with the appropriate function
+ * handler for each function with valid data inputs.
+ */
+static int synaptics_rmi4_sensor_report(struct synaptics_ds4_rmi4_data *pdata)
+{
+	unsigned char	intr_status[MAX_INTR_REGISTERS];
+	int retval;
+	int touch_count = 0;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	struct synaptics_ds4_rmi4_device_info *rmi;
+
+	/*
+	 * Get the interrupt status from the function $01
+	 * control register+1 to find which source(s) were interrupting
+	 * so we can read the data from the source(s) (2D sensor, buttons..)
+	 */
+	retval = synaptics_rmi4_i2c_read(pdata,
+					pdata->fn01_data_base_addr + 1,
+					intr_status,
+					pdata->number_of_interrupt_register);
+	if (retval < 0)
+		return 0;
+
+	if (pdata->touch_stopped) {
+		dev_warn(&pdata->i2c_client->dev,
+			 "Not ready to handle interrupts yet!\n");
+		return 0;
+	}
+	/*
+	 * Check each function that has data sources and if the interrupt for
+	 * that triggered then call that RMI4 functions report() function to
+	 * gather data and report it to the input subsystem
+	 */
+	rmi = &(pdata->rmi4_mod_info);
+	list_for_each_entry(function_handler, &rmi->support_fn_list, link) {
+		if (function_handler->num_of_data_sources) {
+			if (intr_status[function_handler->index_to_intr_reg] &
+						function_handler->intr_mask)
+				touch_count = synaptics_rmi4_report_device(
+						pdata, function_handler);
+		}
+	}
+	return touch_count;
+}
+
+/*
+ * Called by the kernel when an interrupt occurs (when the sensor asserts
+ * the attention irq.
+ *
+ * This function is the ISR thread and handles the acquisition and
+ * the reporting of finger data when the presence of fingers is detected.
+ */
+static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
+{
+	struct synaptics_ds4_rmi4_data *pdata = data;
+	int touch_count;
+	do {
+		touch_count = synaptics_rmi4_sensor_report(pdata);
+		if (touch_count)
+			wait_event_timeout(pdata->wait, pdata->touch_stopped,
+							msecs_to_jiffies(1));
+		else
+			break;
+	} while (!pdata->touch_stopped);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Called by synaptics_rmi4_probe() and the power management functions in
+ * this driver
+ *
+ * This function handles the enabling of the attention irq
+ * including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_enable(struct synaptics_ds4_rmi4_data *pdata,
+					bool request)
+{
+	int retval = 0;
+	char intr_status;
+	const struct synaptics_dsx_platform_data *platformdata =
+				pdata->i2c_client->dev.platform_data;
+
+	mutex_lock(&pdata->irq_request_mutex);
+	if (pdata->irq_enabled)
+		goto exit;
+
+	/* Clear interrupts */
+	retval = synaptics_rmi4_i2c_read(pdata,
+					pdata->fn01_data_base_addr + 1,
+					&intr_status,
+					pdata->number_of_interrupt_register);
+	if (retval < 0)
+		return retval;
+	if (request) {
+		retval = request_threaded_irq(pdata->irq, NULL,
+					  synaptics_rmi4_irq,
+					  platformdata->irq_type,
+					  DRIVER_NAME, pdata);
+		if (retval) {
+			dev_err(&pdata->i2c_client->dev,
+				"%s:Unable to get attn irq %d, type %d\n",
+				__func__,
+				pdata->irq,
+				platformdata->irq_type);
+			goto exit;
+		}
+	} else
+		enable_irq(pdata->irq);
+	pdata->irq_enabled  = true;
+
+exit:
+	mutex_unlock(&pdata->irq_request_mutex);
+	return retval;
+}
+
+/*
+ * Called by synaptics_rmi4_probe() and the power management functions in
+ * this driver
+ *
+ * This function handles the disabling of the attention irq
+ * including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_disable(struct synaptics_ds4_rmi4_data *pdata,
+					bool release)
+{
+	int retval = 0;
+	mutex_lock(&pdata->irq_request_mutex);
+
+	if (pdata->irq_enabled) {
+		disable_irq(pdata->irq);
+		if (release)
+			free_irq(pdata->irq, pdata);
+		pdata->irq_enabled = false;
+	} else
+		dev_warn(&pdata->i2c_client->dev,
+			 "%s:irq has not been reqested\n", __func__);
+	mutex_unlock(&pdata->irq_request_mutex);
+	return retval;
+}
+
+/*
+ * Called by synaptics_rmi4_query_device().
+ *
+ * This funtion parses information from the Function 11 registers and
+ * determines the number of fingers supported, x and y data ranges,
+ * offset to the associated interrupt status register, interrupt bit mask,
+ * and gathers finger data acquisition capabilities from the query registers.
+ */
+static int synaptics_rmi4_2d_touch_detect(
+				struct synaptics_ds4_rmi4_data *pdata,
+				struct synaptics_ds4_rmi4_fn *function_handler,
+				struct synaptics_ds4_rmi4_fn_desc *fd,
+				unsigned int interruptcount)
+{
+	unsigned char	queries[QUERY_LEN];
+	unsigned char data[BUF_LEN];
+	unsigned char	abs_data_size;
+	unsigned char	abs_data_blk_size;
+	unsigned short intr_offset;
+	int i;
+	int retval;
+
+	function_handler->fn_number = fd->fn_number;
+	function_handler->num_of_data_sources = fd->intr_src_count;
+
+	/*
+	 * 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 = synaptics_rmi4_i2c_read(pdata,
+				function_handler->fn_full_addr.query_base_addr,
+				queries, sizeof(queries));
+	if (retval < 0)
+		return retval;
+
+	/* Number of fingers */
+	if ((queries[1] & MASK_3BIT) <= 4) 	/* add 1 since zero based */
+		function_handler->num_of_data_points =
+					(queries[1] & MASK_3BIT) + 1;
+	else
+		if ((queries[1] & MASK_3BIT) == 5)
+			function_handler->num_of_data_points = 10;
+
+	/* Max x/y */
+	retval = synaptics_rmi4_i2c_read(pdata,
+				function_handler->fn_full_addr.ctrl_base_addr,
+				data, DATA_BUF_LEN);
+	if (retval < 0)
+		return retval;
+
+	/* Store these for use later*/
+	pdata->sensor_max_x = ((data[6] & MASK_8BIT) << 0) |
+						((data[7] & MASK_4BIT) << 8);
+	pdata->sensor_max_y = ((data[8] & MASK_8BIT) << 0) |
+						((data[9] & MASK_4BIT) << 8);
+
+
+	/* Interrupt info for handling interrupts */
+	function_handler->index_to_intr_reg = (interruptcount + 7) / 8;
+	if (function_handler->index_to_intr_reg != 0)
+		function_handler->index_to_intr_reg -= 1;
+	/*
+	 * Loop through interrupts for each source in fn $11
+	 * and or in a bit to the interrupt mask for each.
+	 */
+	intr_offset = interruptcount % 8;
+	function_handler->intr_mask = 0;
+	for (i = intr_offset;
+		i < ((fd->intr_src_count & MASK_3BIT) + intr_offset); i++)
+		function_handler->intr_mask |= 1 << i;
+
+	/* Size of just the absolute data for one finger */
+	abs_data_size	= queries[5] & MASK_2BIT;
+
+	/* One each for X and Y, one for LSB for X & Y, one for W, one for Z */
+	abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+	function_handler->size_of_data_register_block = abs_data_blk_size;
+
+	return retval;
+}
+
+static int synaptics_rmi4_alloc_func_handler(
+			struct synaptics_ds4_rmi4_fn **function_handler,
+			struct synaptics_ds4_rmi4_fn_desc *rmi_fd,
+			int page_number)
+{
+	*function_handler =
+		kmalloc(sizeof(struct synaptics_ds4_rmi4_fn), GFP_KERNEL);
+	if (!(*function_handler)) {
+		return -ENOMEM;
+	}
+	(*function_handler)->fn_full_addr.data_base_addr =
+		(rmi_fd->data_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.ctrl_base_addr =
+		(rmi_fd->ctrl_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.cmd_base_addr =
+		(rmi_fd->cmd_base_addr | (page_number << 8));
+	(*function_handler)->fn_full_addr.query_base_addr =
+		(rmi_fd->query_base_addr | (page_number << 8));
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_probe().
+ *
+ * This funtion scans the page description table, records the offsets to the
+ * register types of Function $01, sets up the function handlers for Function
+ * $11, determines the number of interrupt sources from the
+ * sensor, adds valid Functions with data inputs to the Function linked list,
+ * parses information from the query registers of Function $01, and enables
+ * the interrupt sources from the valid Functions with data inputs.
+ */
+static int synaptics_rmi4_query_device(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int i, page_number;
+	int retval;
+	int data_sources = 0;
+	unsigned char std_queries[STD_QUERY_LEN];
+	unsigned char interrupt_mask[MAX_INTR_REGISTERS];
+	unsigned char intr_count = 0;
+	unsigned char intr_index = 0;
+	unsigned short ctrl_offset;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	struct synaptics_ds4_rmi4_fn_desc rmi_fd;
+	struct synaptics_ds4_rmi4_device_info *rmi;
+	struct i2c_client *client = pdata->i2c_client;
+
+	/* Init the physical drivers RMI module info list of functions */
+	INIT_LIST_HEAD(&pdata->rmi4_mod_info.support_fn_list);
+
+	/* Scan the Page Descriptor Table */
+	for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
+		for (i = SYNAPTICS_DS4_RMI4_PDT_START;
+			i > SYNAPTICS_DS4_RMI4_PDT_END;
+			i -= SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE) {
+			function_handler = NULL;
+			i |= (page_number << 8);
+
+			retval = synaptics_rmi4_i2c_read(pdata, i,
+						(unsigned char *)&rmi_fd,
+						sizeof(rmi_fd));
+			if (retval < 0)
+				return retval;
+
+			if (rmi_fd.fn_number == 0)	/* end of the PDT */
+				break;
+			dev_dbg(&pdata->i2c_client->dev,
+					"%s: function F%02X is present\n",
+					__func__, rmi_fd.fn_number);
+			switch (rmi_fd.fn_number & MASK_8BIT) {
+			case SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC:
+				pdata->fn01_query_base_addr =
+						rmi_fd.query_base_addr;
+				pdata->fn01_ctrl_base_addr =
+						rmi_fd.ctrl_base_addr;
+				pdata->fn01_data_base_addr =
+						rmi_fd.data_base_addr;
+				break;
+			case SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC:
+				if (rmi_fd.intr_src_count) {
+					retval =
+					synaptics_rmi4_alloc_func_handler(
+							&function_handler,
+							&rmi_fd,
+							page_number);
+					if (retval < 0)
+						return retval;
+					retval =
+					synaptics_rmi4_2d_touch_detect(
+							pdata,
+							function_handler,
+							&rmi_fd,
+							intr_count);
+					if (retval < 0)
+						return retval;
+				}
+				break;
+			}
+			/* interrupt count for next iteration */
+			intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+			/*
+			 * add functions to the list that have data associated
+			 */
+			if (function_handler && rmi_fd.intr_src_count) {
+				/* link this function info to RMI module */
+				mutex_lock(&(pdata->fn_list_mutex));
+				dev_dbg(&pdata->i2c_client->dev,
+				    "%s: add fiunction handler 0x%X to list\n",
+				    __func__, function_handler->fn_number);
+				list_add_tail(&function_handler->link,
+					&pdata->rmi4_mod_info.support_fn_list);
+				mutex_unlock(&(pdata->fn_list_mutex));
+			}
+		}
+	}
+	/*
+	 * calculate the interrupt register count - used in the
+	 * ISR to read the correct number of interrupt registers
+	 */
+	pdata->number_of_interrupt_register = (intr_count + 7) / 8;
+	dev_dbg(&client->dev, "%s: interrupt register count :%d\n",
+					__func__,
+					pdata->number_of_interrupt_register);
+
+	/* Load up the standard queries and get the RMI4 module info */
+	retval = synaptics_rmi4_i2c_read(pdata, pdata->fn01_query_base_addr,
+					std_queries, sizeof(std_queries));
+	if (retval < 0)
+		return retval;
+
+	/*
+	 * get manufacturer id, product_props, product info,
+	 * date code, tester id, serial num and product id (name)
+	 */
+	pdata->rmi4_mod_info.manufacturer_id = std_queries[0];
+	pdata->rmi4_mod_info.product_props = std_queries[1];
+	pdata->rmi4_mod_info.product_info[0] = std_queries[2];
+	pdata->rmi4_mod_info.product_info[1] = std_queries[3];
+	/* year - 2001-2032 */
+	pdata->rmi4_mod_info.date_code[0] = std_queries[4] & MASK_5BIT;
+	/* month - 1-12 */
+	pdata->rmi4_mod_info.date_code[1] = std_queries[5] & MASK_4BIT;
+	/* day - 1-31 */
+	pdata->rmi4_mod_info.date_code[2] = std_queries[6] & MASK_5BIT;
+	pdata->rmi4_mod_info.tester_id = ((std_queries[7] & MASK_7BIT) << 8) |
+						(std_queries[8] & MASK_7BIT);
+	pdata->rmi4_mod_info.serial_number =
+				((std_queries[9] & MASK_7BIT) << 8) |
+				(std_queries[10] & MASK_7BIT);
+	memcpy(pdata->rmi4_mod_info.product_id_string, &std_queries[11], 10);
+
+	/* Check if this is a Synaptics device - report if not. */
+	if (pdata->rmi4_mod_info.manufacturer_id != 1)
+			dev_err(&client->dev, "%s: non-Synaptics mfg id:%d\n",
+					__func__,
+					pdata->rmi4_mod_info.manufacturer_id);
+
+	memset(interrupt_mask, 0x00, sizeof(interrupt_mask));
+	list_for_each_entry(function_handler,
+			&pdata->rmi4_mod_info.support_fn_list, link)
+			data_sources += function_handler->num_of_data_sources;
+	if (data_sources) {
+		rmi = &(pdata->rmi4_mod_info);
+		list_for_each_entry(function_handler,
+					&rmi->support_fn_list, link) {
+			if (function_handler->num_of_data_sources)
+				intr_index =
+					function_handler->index_to_intr_reg;
+				interrupt_mask[intr_index] |=
+				function_handler->intr_mask;
+		}
+	}
+
+	for (i = 0; i < MAX_INTR_REGISTERS; i++) {
+		if (interrupt_mask[i] != 0x00) {
+			dev_dbg(&client->dev,
+					"%s: interrupt %d enable mask :0x%X\n",
+					__func__, i, interrupt_mask[i]);
+			ctrl_offset = pdata->fn01_ctrl_base_addr + 1 + i;
+			retval = synaptics_rmi4_i2c_write(pdata,
+							ctrl_offset,
+							&(interrupt_mask[i]),
+							sizeof(interrupt_mask[i]));
+			if (retval < 0)
+				return retval;
+		}
+	}
+	return 0;
+}
+
+static int __devinit synaptics_rmi4_probe(
+		struct i2c_client *client, const struct i2c_device_id *dev_id)
+{
+	int retval;
+	struct synaptics_ds4_rmi4_data *pdata;
+	struct synaptics_ds4_rmi4_fn *function_handler;
+	const struct synaptics_dsx_platform_data *platformdata =
+						client->dev.platform_data;
+
+	if (!i2c_check_functionality(client->adapter,
+					I2C_FUNC_SMBUS_BYTE_DATA)) {
+		dev_err(&client->dev, "i2c smbus byte data not supported\n");
+		return -EIO;
+	}
+
+	if (!platformdata) {
+		dev_err(&client->dev, "%s: no platform data\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Allocate and initialize the instance data for this client */
+	pdata = kzalloc(sizeof(struct synaptics_ds4_rmi4_data) * 2,
+							GFP_KERNEL);
+	if (!pdata) {
+		dev_err(&client->dev, "%s: no memory allocated\n", __func__);
+		return -ENOMEM;
+	}
+
+	pdata->input_dev = input_allocate_device();
+	if (pdata->input_dev == NULL) {
+		dev_err(&client->dev, "%s:input device alloc failed\n",
+						__func__);
+		retval = -ENOMEM;
+		goto err_input;
+	}
+
+	if (platformdata->regulator_en) {
+		pdata->regulator = regulator_get(&client->dev, "vdd");
+		if (IS_ERR(pdata->regulator)) {
+			dev_err(&client->dev, "%s:get regulator failed\n",
+								__func__);
+			retval = PTR_ERR(pdata->regulator);
+			goto err_regulator;
+		}
+		regulator_enable(pdata->regulator);
+	}
+	init_waitqueue_head(&pdata->wait);
+	pdata->i2c_client		= client;
+	pdata->current_page	= MASK_16BIT;
+	pdata->board		= platformdata;
+	pdata->sensor_sleep	= false;
+	pdata->irq_enabled 	= false;
+	pdata->touch_stopped	= false;
+
+	/* Init the mutexes for maintain the lists */
+	mutex_init(&(pdata->fn_list_mutex));
+	mutex_init(&(pdata->io_ctrl_mutex));
+
+	/*
+	 * Register physical driver - this will call the detect function that
+	 * will then scan the device and determine the supported
+	 * rmi4 functions.
+	 */
+	retval = synaptics_rmi4_query_device(pdata);
+	if (retval) {
+		dev_err(&client->dev, "%s: rmi4 query device failed\n",
+							__func__);
+		goto err_query_dev;
+	}
+
+	/* Store the instance data in the i2c_client */
+	i2c_set_clientdata(client, pdata);
+
+	/* Initialize the input device parameters */
+	pdata->input_dev->name = DRIVER_NAME;
+	pdata->input_dev->phys = INPUT_PHYS_NAME;
+	pdata->input_dev->id.bustype = BUS_I2C;
+	pdata->input_dev->dev.parent = &client->dev;
+	input_set_drvdata(pdata->input_dev, pdata);
+
+	/* Initialize the function handlers for rmi4 */
+	set_bit(EV_SYN, pdata->input_dev->evbit);
+	set_bit(EV_KEY, pdata->input_dev->evbit);
+	set_bit(EV_ABS, pdata->input_dev->evbit);
+#ifdef INPUT_PROP_DIRECT
+	set_bit(INPUT_PROP_DIRECT, pdata->input_dev->propbit);
+#endif
+
+	input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_X, 0,
+						pdata->sensor_max_x, 0, 0);
+	input_set_abs_params(pdata->input_dev, ABS_MT_POSITION_Y, 0,
+						pdata->sensor_max_y, 0, 0);
+	input_set_abs_params(pdata->input_dev, ABS_MT_TOUCH_MAJOR, 0,
+						MAX_TOUCH_MAJOR, 0, 0);
+
+	retval = input_register_device(pdata->input_dev);
+	if (retval) {
+		dev_err(&client->dev, "%s:input register failed\n", __func__);
+		goto err_query_dev;
+	}
+
+	/* Gpio configuration */
+	if (platformdata->gpio_config) {
+		retval = platformdata->gpio_config(platformdata->gpio, true);
+		if (retval < 0) {
+			dev_err(&client->dev,
+				"Failed to configure GPIOs, code: %d.\n",
+				retval);
+			return retval;
+		}
+		dev_info(&client->dev, "Done with GPIO configuration.\n");
+	}
+	pdata->irq = gpio_to_irq(platformdata->gpio);
+	mutex_init(&(pdata->irq_request_mutex));
+	pdata->touch_stopped = false;
+	retval = synaptics_rmi4_irq_enable(pdata, true);
+	if (retval) {
+		dev_err(&client->dev,
+			"%s:Unable to get attn irq %d, type %d\n, name: %s",
+			__func__, pdata->irq, platformdata->irq_type,
+			DRIVER_NAME);
+		goto err_request_irq;
+	}
+
+	return retval;
+
+err_request_irq:
+	input_unregister_device(pdata->input_dev);
+err_query_dev:
+	if (platformdata->regulator_en) {
+		regulator_disable(pdata->regulator);
+		regulator_put(pdata->regulator);
+	}
+
+	if (!list_empty(&pdata->rmi4_mod_info.support_fn_list)) {
+		list_for_each_entry(function_handler,
+			&pdata->rmi4_mod_info.support_fn_list, link) {
+			if (function_handler) {
+				if (function_handler->data)
+					kfree(function_handler->data);
+				kfree(function_handler);
+			}
+		}
+	}
+
+err_regulator:
+	input_free_device(pdata->input_dev);
+	pdata->input_dev = NULL;
+err_input:
+	kfree(pdata);
+
+	return retval;
+}
+
+static int __devexit synaptics_rmi4_remove(struct i2c_client *client)
+{
+	struct synaptics_ds4_rmi4_data *pdata = i2c_get_clientdata(client);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	pdata->touch_stopped = true;
+	synaptics_rmi4_irq_disable(pdata, true);
+
+	input_unregister_device(pdata->input_dev);
+	if (platformdata->regulator_en) {
+		regulator_disable(pdata->regulator);
+		regulator_put(pdata->regulator);
+	}
+	kfree(pdata);
+	return 0;
+}
+
+/*
+ * Called by synaptics_rmi4_early_suspend() and synaptics_rmi4_suspend().
+ *
+ * This function stops finger data acquisition and puts the sensor to sleep.
+ */
+static void synaptics_rmi4_sensor_sleep(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int retval;
+	unsigned char device_ctrl;
+
+	if (pdata->sensor_sleep == true)
+		return;
+
+	pdata->touch_stopped = true;
+	wake_up(&pdata->wait);
+
+	retval = synaptics_rmi4_i2c_read(pdata,
+				pdata->fn01_ctrl_base_addr,
+				&device_ctrl,
+				1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to enter sleep mode. Code: %d.\n", retval);
+		pdata->sensor_sleep = false;
+		return;
+	}
+
+	device_ctrl = (device_ctrl & ~MASK_3BIT);
+	device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP);
+
+	retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr,
+						&device_ctrl, 1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to enter sleep mode. Code: %d.\n", retval);
+		pdata->sensor_sleep = false;
+		return;
+	} else
+		pdata->sensor_sleep = true;
+
+	return;
+}
+
+/*
+ * Called by synaptics_rmi4_resume() and synaptics_rmi4_late_resume().
+ *
+ * This function wakes the sensor from sleep.
+ */
+static void synaptics_rmi4_sensor_wake(struct synaptics_ds4_rmi4_data *pdata)
+{
+	int retval;
+	unsigned char device_ctrl;
+
+	if (pdata->sensor_sleep == false)
+		return;
+	retval = synaptics_rmi4_i2c_read(pdata,
+				pdata->fn01_ctrl_base_addr,
+				&device_ctrl,
+				1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to wake from sleep mode. Code: %d.\n",
+			retval);
+		pdata->sensor_sleep = true;
+		return;
+	}
+
+	device_ctrl = (device_ctrl & ~MASK_3BIT);
+	device_ctrl = (device_ctrl | NO_SLEEP_OFF | NORMAL_OPERATION);
+
+	retval = synaptics_rmi4_i2c_write(pdata, pdata->fn01_ctrl_base_addr,
+						&device_ctrl, 1);
+
+	if (retval < 0) {
+		dev_err(&(pdata->input_dev->dev),
+			"Failed to wake from sleep mode. Code: %d.\n",
+			retval);
+		pdata->sensor_sleep = true;
+		return;
+	} else
+		pdata->sensor_sleep = false;
+
+	return;
+}
+
+#ifdef CONFIG_PM
+/*
+ * Called by the kernel during the suspend phase when the system
+ * enters suspend.
+ *
+ * This function stops finger data acquisition and puts the sensor to
+ * sleep (if not already done so during the early suspend phase),
+ * disables the interrupt, and turns off the power to the sensor.
+ */
+static int synaptics_rmi4_suspend(struct device *dev)
+{
+	struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	synaptics_rmi4_irq_disable(pdata, false);
+	synaptics_rmi4_sensor_sleep(pdata);
+
+	if (platformdata->regulator_en)
+		regulator_disable(pdata->regulator);
+	return 0;
+}
+
+/*
+ * Called by the kernel during the resume phase when the system
+ * wakes up from suspend.
+ *
+ * This function turns on the power to the sensor, wakes the sensor
+ * from sleep, enables the interrupt, and starts finger data
+ * acquisition.
+ */
+static int synaptics_rmi4_resume(struct device *dev)
+{
+	struct synaptics_ds4_rmi4_data *pdata = dev_get_drvdata(dev);
+	const struct synaptics_dsx_platform_data *platformdata = pdata->board;
+
+	if (platformdata->regulator_en)
+		regulator_enable(pdata->regulator);
+
+	synaptics_rmi4_sensor_wake(pdata);
+	synaptics_rmi4_irq_enable(pdata, false);
+	pdata->touch_stopped = false;
+
+	return 0;
+}
+
+static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = {
+	.suspend = synaptics_rmi4_suspend,
+	.resume  = synaptics_rmi4_resume,
+};
+#endif
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+	{ DRIVER_NAME, 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+	.driver = {
+		.name	=	DRIVER_NAME,
+		.owner	=	THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm	=	&synaptics_rmi4_dev_pm_ops,
+#endif
+	},
+	.probe		=	synaptics_rmi4_probe,
+	.remove		=	__devexit_p(synaptics_rmi4_remove),
+	.id_table	=	synaptics_rmi4_id_table,
+};
+
+static int __init synaptics_rmi4_init(void)
+{
+	return i2c_add_driver(&synaptics_rmi4_driver);
+}
+
+static void __exit synaptics_rmi4_exit(void)
+{
+	i2c_del_driver(&synaptics_rmi4_driver);
+}
+
+module_init(synaptics_rmi4_init);
+module_exit(synaptics_rmi4_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("synaptics ds4 i2c touch driver");
\ No newline at end of file
diff --git a/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
new file mode 100644
index 0000000..58329d9
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_ds4_rmi4_i2c.h
@@ -0,0 +1,94 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYNAPTICS_DS4_RMI4_H_
+#define _SYNAPTICS_DS4_RMI4_H_
+
+#define SYNAPTICS_DS4_RMI4_PDT_START				(0x00E9)
+#define SYNAPTICS_DS4_RMI4_PDT_END				(0x000A)
+#define SYNAPTICS_DS4_RMI4_PDT_ENTRY_SIZE		(0x0006)
+#define SYNAPTICS_DS4_RMI4_DEVICE_CONTROL_FUNC	(0x01)
+#define SYNAPTICS_DS4_RMI4_2D_SENSOR_FUNC		(0x11)
+
+/* funtion descriptor information */
+struct synaptics_ds4_rmi4_fn_desc {
+	unsigned char	query_base_addr;
+	unsigned char	cmd_base_addr;
+	unsigned char	ctrl_base_addr;
+	unsigned char	data_base_addr;
+	unsigned char	intr_src_count;
+	unsigned char	fn_number;
+};
+
+/*  funtion information */
+struct synaptics_ds4_rmi4_fn {
+	unsigned char fn_number;
+	unsigned char num_of_data_sources;
+	unsigned char num_of_data_points;
+	unsigned char size_of_data_register_block;
+	unsigned char index_to_intr_reg;
+	unsigned char intr_mask;
+	struct synaptics_ds4_rmi4_fn_desc fn_full_addr;
+	struct list_head link;
+	void *data;
+};
+
+/* device information */
+struct synaptics_ds4_rmi4_device_info {
+	unsigned char manufacturer_id;
+	unsigned char product_props;
+	unsigned char product_info[2];
+	unsigned char date_code[3];
+	unsigned short tester_id;
+	unsigned short serial_number;
+	unsigned char product_id_string[11];
+	struct list_head support_fn_list;
+};
+
+/* ds4 rmi4 touch screen data */
+struct synaptics_ds4_rmi4_data {
+	const struct synaptics_dsx_platform_data *board;
+	struct input_dev *input_dev;
+	struct i2c_client *i2c_client;
+	struct mutex fn_list_mutex;
+	struct mutex io_ctrl_mutex;
+	struct mutex irq_request_mutex;
+	struct synaptics_ds4_rmi4_device_info rmi4_mod_info;
+	struct regulator *regulator;
+	unsigned int number_of_interrupt_register;
+	unsigned short current_page;
+	unsigned short fn01_ctrl_base_addr;
+	unsigned short fn01_query_base_addr;
+	unsigned short fn01_data_base_addr;
+	unsigned short sensor_max_x;
+	unsigned short sensor_max_y;
+	wait_queue_head_t	 wait;
+	bool touch_stopped;
+	bool irq_enabled;
+	bool sensor_sleep;
+	int irq;
+};
+
+#endif
diff --git a/include/linux/input/synaptics_dsx.h b/include/linux/input/synaptics_dsx.h
new file mode 100644
index 0000000..4f0d0d6
--- /dev/null
+++ b/include/linux/input/synaptics_dsx.h
@@ -0,0 +1,49 @@
+/*
+ * Synaptics DS4 touchscreen driver
+ *
+ * Copyright (C) 2012 Synaptics Incorporated
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright (C) 2010 Js HA <js.ha@stericsson.com>
+ * Copyright (C) 2010 Naveen Kumar G <naveen.gaddipati@stericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _SYNAPTICS_DSX_H_
+#define _SYNAPTICS_DSX_H_
+
+/*
+ * struct synaptics_dsx_platform_data - contains the dsx platform data
+ * @x_flip: x flip flag
+ * @y_flip: y flip flag
+ * @regulator_en: regulator enable flag
+ * @irq_type: irq type
+ * @gpio: gpio pin assignment
+ * @gpio_config: callback for gpio set up
+ *
+ * This structure gives platform data for dsx.
+ */
+struct synaptics_dsx_platform_data {
+	bool x_flip;
+	bool y_flip;
+	bool regulator_en;
+	int irq_type;
+	unsigned gpio;
+	int (*gpio_config)(unsigned interrupt_gpio, bool configure);
+};
+
+#endif

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2012-11-01  3:14 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-03  3:30 [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices Alexandra Chin
2012-10-03 18:01 ` Henrik Rydberg
2012-10-04  5:52   ` Alexandra Chin
2012-10-04  6:51     ` Dmitry Torokhov
2012-10-05  9:44       ` Alexandra Chin
2012-10-31  9:17   ` [PATCH] staging: ste_rmi4: Convert to Type-B support Alexandra Chin
2012-10-31 18:43     ` Henrik Rydberg
2012-11-01  3:14       ` Alexandra Chin
  -- strict thread matches above, loose matches on Subject: below --
2012-10-02  7:50 [PATCH] Input: Add new driver into Input Subsystem for Synaptics DS4 touchscreen I2C devices Alexandra Chin
2012-09-16  9:56 Alexandra Chin
2012-09-16 22:33 ` Linus Walleij
2012-09-19 21:57   ` Christopher Heiny

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).