summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexandra Chin <alexandra.chin@tw.synaptics.com>2013-12-13 16:44:36 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 20:57:50 -0700
commit7ef5f9cf4e6464f7128dccd58481977a91d69400 (patch)
tree8f4b4b58011203a42a56790f9c675c77b45cf148
parent12cf0ae81919bbcba57aa4b0e3f421b9939a5494 (diff)
input: synaptics_dsx: Add support for synaptics_dsx touch
Change-Id: I1df8a7465fdb5c18cf69e0908347fc78ce7dd07c Git-commit: 67b7a9d78725d5de2a1899095eb0026e15343a62 Git-repo: git://github.com/synaptics-touch/synaptics-dsx-v2.1 Signed-off-by: Alexandra Chin <alexandra.chin@tw.synaptics.com> Signed-off-by: Amy Maloche <amaloche@codeaurora.org>
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/Kconfig70
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/Makefile12
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c3063
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h336
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c1850
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c271
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c671
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c781
-rwxr-xr-xdrivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c335
-rwxr-xr-xinclude/linux/input/synaptics_dsx_v2.h80
10 files changed, 7469 insertions, 0 deletions
diff --git a/drivers/input/touchscreen/synaptics_dsx/Kconfig b/drivers/input/touchscreen/synaptics_dsx/Kconfig
new file mode 100644
index 000000000000..18591719f162
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/Kconfig
@@ -0,0 +1,70 @@
+#
+# Synaptics DSX touchscreen driver configuration
+#
+menuconfig TOUCHSCREEN_SYNAPTICS_DSX
+ bool "Synaptics DSX touchscreen"
+ default y
+ help
+ Say Y here if you have a Synaptics DSX touchscreen connected
+ to your system.
+
+ If unsure, say N.
+
+if TOUCHSCREEN_SYNAPTICS_DSX
+
+choice
+ default TOUCHSCREEN_SYNAPTICS_DSX_I2C
+ prompt "Synaptics DSX touchscreen bus interface"
+config TOUCHSCREEN_SYNAPTICS_DSX_I2C
+ bool "I2C"
+ depends on I2C
+config TOUCHSCREEN_SYNAPTICS_DSX_SPI
+ bool "SPI"
+ depends on SPI_MASTER
+endchoice
+
+config TOUCHSCREEN_SYNAPTICS_DSX_CORE
+ tristate "Synaptics DSX core driver module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_I2C || TOUCHSCREEN_SYNAPTICS_DSX_SPI
+ help
+ Say Y here to enable basic touch reporting functionalities.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_dsx_core.
+
+config TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV
+ tristate "Synaptics DSX touchscreen RMI device module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+ help
+ Say Y here to enable support for direct RMI register access.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_dsx_rmi_dev.
+
+config TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE
+ tristate "Synaptics DSX touchscreen firmware update module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+ help
+ Say Y here to enable support for carrying out firmware update.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_dsx_fw_update.
+
+config TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY
+ tristate "Synaptics DSX touchscreen proximity module"
+ depends on TOUCHSCREEN_SYNAPTICS_DSX_CORE
+ help
+ Say Y here to enable support for proximity functionalities.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_dsx_proximity.
+
+endif
diff --git a/drivers/input/touchscreen/synaptics_dsx/Makefile b/drivers/input/touchscreen/synaptics_dsx/Makefile
new file mode 100644
index 000000000000..7e7f218c07f9
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the Synaptics DSX touchscreen driver.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C) += synaptics_dsx_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_SPI) += synaptics_dsx_spi.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE) += synaptics_dsx_core.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_RMI_DEV) += synaptics_dsx_rmi_dev.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE) += synaptics_dsx_fw_update.o
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_PROXIMITY) += synaptics_dsx_proximity.o
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
new file mode 100755
index 000000000000..dbdaa3e6f612
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.c
@@ -0,0 +1,3063 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_dsx_core.h"
+#ifdef KERNEL_ABOVE_2_6_38
+#include <linux/input/mt.h>
+#endif
+
+#define INPUT_PHYS_NAME "synaptics_dsx/input0"
+
+#ifdef KERNEL_ABOVE_2_6_38
+#define TYPE_B_PROTOCOL
+#endif
+
+#define NO_0D_WHILE_2D
+#define REPORT_2D_Z
+#define REPORT_2D_W
+
+#define F12_DATA_15_WORKAROUND
+
+/*
+#define IGNORE_FN_INIT_FAILURE
+*/
+
+#define RPT_TYPE (1 << 0)
+#define RPT_X_LSB (1 << 1)
+#define RPT_X_MSB (1 << 2)
+#define RPT_Y_LSB (1 << 3)
+#define RPT_Y_MSB (1 << 4)
+#define RPT_Z (1 << 5)
+#define RPT_WX (1 << 6)
+#define RPT_WY (1 << 7)
+#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB)
+
+#define EXP_FN_WORK_DELAY_MS 1000 /* ms */
+#define MAX_F11_TOUCH_WIDTH 15
+
+#define CHECK_STATUS_TIMEOUT_MS 100
+
+#define F01_STD_QUERY_LEN 21
+#define F01_BUID_ID_OFFSET 18
+#define F11_STD_QUERY_LEN 9
+#define F11_STD_CTRL_LEN 10
+#define F11_STD_DATA_LEN 12
+
+#define STATUS_NO_ERROR 0x00
+#define STATUS_RESET_OCCURRED 0x01
+#define STATUS_INVALID_CONFIG 0x02
+#define STATUS_DEVICE_FAILURE 0x03
+#define STATUS_CONFIG_CRC_FAILURE 0x04
+#define STATUS_FIRMWARE_CRC_FAILURE 0x05
+#define STATUS_CRC_IN_PROGRESS 0x06
+
+#define NORMAL_OPERATION (0 << 0)
+#define SENSOR_SLEEP (1 << 0)
+#define NO_SLEEP_OFF (0 << 2)
+#define NO_SLEEP_ON (1 << 2)
+#define CONFIGURED (1 << 7)
+
+static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short ctrl28);
+
+static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data);
+static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data);
+static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static void synaptics_rmi4_early_suspend(struct early_suspend *h);
+
+static void synaptics_rmi4_late_resume(struct early_suspend *h);
+#endif
+
+static int synaptics_rmi4_suspend(struct device *dev);
+
+static int synaptics_rmi4_resume(struct device *dev);
+
+static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t synaptics_rmi4_suspend_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+struct synaptics_rmi4_f01_device_status {
+ union {
+ struct {
+ unsigned char status_code:4;
+ unsigned char reserved:2;
+ unsigned char flash_prog:1;
+ unsigned char unconfigured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f12_query_5 {
+ union {
+ struct {
+ unsigned char size_of_query6;
+ struct {
+ unsigned char ctrl0_is_present:1;
+ unsigned char ctrl1_is_present:1;
+ unsigned char ctrl2_is_present:1;
+ unsigned char ctrl3_is_present:1;
+ unsigned char ctrl4_is_present:1;
+ unsigned char ctrl5_is_present:1;
+ unsigned char ctrl6_is_present:1;
+ unsigned char ctrl7_is_present:1;
+ } __packed;
+ struct {
+ unsigned char ctrl8_is_present:1;
+ unsigned char ctrl9_is_present:1;
+ unsigned char ctrl10_is_present:1;
+ unsigned char ctrl11_is_present:1;
+ unsigned char ctrl12_is_present:1;
+ unsigned char ctrl13_is_present:1;
+ unsigned char ctrl14_is_present:1;
+ unsigned char ctrl15_is_present:1;
+ } __packed;
+ struct {
+ unsigned char ctrl16_is_present:1;
+ unsigned char ctrl17_is_present:1;
+ unsigned char ctrl18_is_present:1;
+ unsigned char ctrl19_is_present:1;
+ unsigned char ctrl20_is_present:1;
+ unsigned char ctrl21_is_present:1;
+ unsigned char ctrl22_is_present:1;
+ unsigned char ctrl23_is_present:1;
+ } __packed;
+ struct {
+ unsigned char ctrl24_is_present:1;
+ unsigned char ctrl25_is_present:1;
+ unsigned char ctrl26_is_present:1;
+ unsigned char ctrl27_is_present:1;
+ unsigned char ctrl28_is_present:1;
+ unsigned char ctrl29_is_present:1;
+ unsigned char ctrl30_is_present:1;
+ unsigned char ctrl31_is_present:1;
+ } __packed;
+ };
+ unsigned char data[5];
+ };
+};
+
+struct synaptics_rmi4_f12_query_8 {
+ union {
+ struct {
+ unsigned char size_of_query9;
+ struct {
+ unsigned char data0_is_present:1;
+ unsigned char data1_is_present:1;
+ unsigned char data2_is_present:1;
+ unsigned char data3_is_present:1;
+ unsigned char data4_is_present:1;
+ unsigned char data5_is_present:1;
+ unsigned char data6_is_present:1;
+ unsigned char data7_is_present:1;
+ } __packed;
+ struct {
+ unsigned char data8_is_present:1;
+ unsigned char data9_is_present:1;
+ unsigned char data10_is_present:1;
+ unsigned char data11_is_present:1;
+ unsigned char data12_is_present:1;
+ unsigned char data13_is_present:1;
+ unsigned char data14_is_present:1;
+ unsigned char data15_is_present:1;
+ } __packed;
+ };
+ unsigned char data[3];
+ };
+};
+
+struct synaptics_rmi4_f12_ctrl_8 {
+ union {
+ struct {
+ unsigned char max_x_coord_lsb;
+ unsigned char max_x_coord_msb;
+ unsigned char max_y_coord_lsb;
+ unsigned char max_y_coord_msb;
+ unsigned char rx_pitch_lsb;
+ unsigned char rx_pitch_msb;
+ unsigned char tx_pitch_lsb;
+ unsigned char tx_pitch_msb;
+ unsigned char low_rx_clip;
+ unsigned char high_rx_clip;
+ unsigned char low_tx_clip;
+ unsigned char high_tx_clip;
+ unsigned char num_of_rx;
+ unsigned char num_of_tx;
+ };
+ unsigned char data[14];
+ };
+};
+
+struct synaptics_rmi4_f12_ctrl_23 {
+ union {
+ struct {
+ unsigned char obj_type_enable;
+ unsigned char max_reported_objects;
+ };
+ unsigned char data[2];
+ };
+};
+
+struct synaptics_rmi4_f12_finger_data {
+ unsigned char object_type_and_status;
+ unsigned char x_lsb;
+ unsigned char x_msb;
+ unsigned char y_lsb;
+ unsigned char y_msb;
+#ifdef REPORT_2D_Z
+ unsigned char z;
+#endif
+#ifdef REPORT_2D_W
+ unsigned char wx;
+ unsigned char wy;
+#endif
+};
+
+struct synaptics_rmi4_f1a_query {
+ union {
+ struct {
+ unsigned char max_button_count:3;
+ unsigned char reserved:5;
+ unsigned char has_general_control:1;
+ unsigned char has_interrupt_enable:1;
+ unsigned char has_multibutton_select:1;
+ unsigned char has_tx_rx_map:1;
+ unsigned char has_perbutton_threshold:1;
+ unsigned char has_release_threshold:1;
+ unsigned char has_strongestbtn_hysteresis:1;
+ unsigned char has_filter_strength:1;
+ } __packed;
+ unsigned char data[2];
+ };
+};
+
+struct synaptics_rmi4_f1a_control_0 {
+ union {
+ struct {
+ unsigned char multibutton_report:2;
+ unsigned char filter_mode:2;
+ unsigned char reserved:4;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f1a_control {
+ struct synaptics_rmi4_f1a_control_0 general_control;
+ unsigned char button_int_enable;
+ unsigned char multi_button;
+ unsigned char *txrx_map;
+ unsigned char *button_threshold;
+ unsigned char button_release_threshold;
+ unsigned char strongest_button_hysteresis;
+ unsigned char filter_strength;
+};
+
+struct synaptics_rmi4_f1a_handle {
+ int button_bitmask_size;
+ unsigned char max_count;
+ unsigned char valid_button_count;
+ unsigned char *button_data_buffer;
+ unsigned char *button_map;
+ struct synaptics_rmi4_f1a_query button_query;
+ struct synaptics_rmi4_f1a_control button_control;
+};
+
+struct synaptics_rmi4_exp_fhandler {
+ struct synaptics_rmi4_exp_fn *exp_fn;
+ bool insert;
+ bool remove;
+ struct list_head link;
+};
+
+struct synaptics_rmi4_exp_fn_data {
+ bool initialized;
+ bool queue_work;
+ struct mutex mutex;
+ struct list_head list;
+ struct delayed_work work;
+ struct workqueue_struct *workqueue;
+ struct synaptics_rmi4_data *rmi4_data;
+};
+
+static struct synaptics_rmi4_exp_fn_data exp_data;
+
+static struct device_attribute attrs[] = {
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ __ATTR(full_pm_cycle, (S_IRUGO | S_IWUGO),
+ synaptics_rmi4_full_pm_cycle_show,
+ synaptics_rmi4_full_pm_cycle_store),
+#endif
+ __ATTR(reset, S_IWUGO,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_f01_reset_store),
+ __ATTR(productinfo, S_IRUGO,
+ synaptics_rmi4_f01_productinfo_show,
+ synaptics_rmi4_store_error),
+ __ATTR(buildid, S_IRUGO,
+ synaptics_rmi4_f01_buildid_show,
+ synaptics_rmi4_store_error),
+ __ATTR(flashprog, S_IRUGO,
+ synaptics_rmi4_f01_flashprog_show,
+ synaptics_rmi4_store_error),
+ __ATTR(0dbutton, (S_IRUGO | S_IWUGO),
+ synaptics_rmi4_0dbutton_show,
+ synaptics_rmi4_0dbutton_store),
+ __ATTR(suspend, S_IWUGO,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_suspend_store),
+};
+
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static ssize_t synaptics_rmi4_full_pm_cycle_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ rmi4_data->full_pm_cycle);
+}
+
+static ssize_t synaptics_rmi4_full_pm_cycle_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ rmi4_data->full_pm_cycle = input > 0 ? 1 : 0;
+
+ return count;
+}
+#endif
+
+static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int reset;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (sscanf(buf, "%u", &reset) != 1)
+ return -EINVAL;
+
+ if (reset != 1)
+ return -EINVAL;
+
+ retval = synaptics_rmi4_reset_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to issue reset command, error = %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
+ (rmi4_data->rmi4_mod_info.product_info[0]),
+ (rmi4_data->rmi4_mod_info.product_info[1]));
+}
+
+static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ rmi4_data->firmware_id);
+}
+
+static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int retval;
+ struct synaptics_rmi4_f01_device_status device_status;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ device_status.data,
+ sizeof(device_status.data));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read device status, error = %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ device_status.flash_prog);
+}
+
+static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ rmi4_data->button_0d_enabled);
+}
+
+static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ unsigned char ii;
+ unsigned char intr_enable;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ input = input > 0 ? 1 : 0;
+
+ if (rmi4_data->button_0d_enabled == input)
+ return count;
+
+ if (list_empty(&rmi->support_fn_list))
+ return -ENODEV;
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
+ ii = fhandler->intr_reg_num;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr + 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
+
+ if (input == 1)
+ intr_enable |= fhandler->intr_mask;
+ else
+ intr_enable &= ~fhandler->intr_mask;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr + 1 + ii,
+ &intr_enable,
+ sizeof(intr_enable));
+ if (retval < 0)
+ return retval;
+ }
+ }
+
+ rmi4_data->button_0d_enabled = input;
+
+ return count;
+}
+
+static ssize_t synaptics_rmi4_suspend_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input == 1)
+ synaptics_rmi4_suspend(dev);
+ else if (input == 0)
+ synaptics_rmi4_resume(dev);
+ else
+ return -EINVAL;
+
+ return count;
+}
+
+ /**
+ * synaptics_rmi4_f11_abs_report()
+ *
+ * Called by synaptics_rmi4_report_touch() 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, and returns the number of fingers detected.
+ */
+static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ unsigned char touch_count = 0; /* number of touch points */
+ unsigned char reg_index;
+ unsigned char finger;
+ unsigned char fingers_supported;
+ unsigned char num_of_finger_status_regs;
+ unsigned char finger_shift;
+ unsigned char finger_status;
+ unsigned char data_reg_blk_size;
+ unsigned char finger_status_reg[3];
+ unsigned char data[F11_STD_DATA_LEN];
+ unsigned short data_addr;
+ unsigned short data_offset;
+ int x;
+ int y;
+ int wx;
+ int wy;
+ int temp;
+
+ /*
+ * The number of finger status registers is determined by the
+ * maximum number of fingers supported - 2 bits per finger. So
+ * the number of finger status registers to read is:
+ * register_count = ceil(max_num_of_fingers / 4)
+ */
+ fingers_supported = fhandler->num_of_data_points;
+ num_of_finger_status_regs = (fingers_supported + 3) / 4;
+ data_addr = fhandler->full_addr.data_base;
+ data_reg_blk_size = fhandler->size_of_data_register_block;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ data_addr,
+ finger_status_reg,
+ num_of_finger_status_regs);
+ if (retval < 0)
+ return 0;
+
+ for (finger = 0; finger < fingers_supported; finger++) {
+ reg_index = finger / 4;
+ finger_shift = (finger % 4) * 2;
+ finger_status = (finger_status_reg[reg_index] >> finger_shift)
+ & MASK_2BIT;
+
+ /*
+ * Each 2-bit finger status field represents the following:
+ * 00 = finger not present
+ * 01 = finger present and data accurate
+ * 10 = finger present but data may be inaccurate
+ * 11 = reserved
+ */
+#ifdef TYPE_B_PROTOCOL
+ input_mt_slot(rmi4_data->input_dev, finger);
+ input_mt_report_slot_state(rmi4_data->input_dev,
+ MT_TOOL_FINGER, finger_status);
+#endif
+
+ if (finger_status) {
+ data_offset = data_addr +
+ num_of_finger_status_regs +
+ (finger * data_reg_blk_size);
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ data_offset,
+ data,
+ data_reg_blk_size);
+ if (retval < 0)
+ return 0;
+
+ x = (data[0] << 4) | (data[2] & MASK_4BIT);
+ y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT);
+ wx = (data[3] & MASK_4BIT);
+ wy = (data[3] >> 4) & MASK_4BIT;
+
+ if (rmi4_data->hw_if->board_data->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ temp = wx;
+ wx = wy;
+ wy = temp;
+ }
+
+ if (rmi4_data->hw_if->board_data->x_flip)
+ x = rmi4_data->sensor_max_x - x;
+ if (rmi4_data->hw_if->board_data->y_flip)
+ y = rmi4_data->sensor_max_y - y;
+
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 1);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 1);
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_Y, y);
+#ifdef REPORT_2D_W
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MAJOR, max(wx, wy));
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MINOR, min(wx, wy));
+#endif
+#ifndef TYPE_B_PROTOCOL
+ input_mt_sync(rmi4_data->input_dev);
+#endif
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Finger %d:\n"
+ "status = 0x%02x\n"
+ "x = %d\n"
+ "y = %d\n"
+ "wx = %d\n"
+ "wy = %d\n",
+ __func__, finger,
+ finger_status,
+ x, y, wx, wy);
+
+ touch_count++;
+ }
+ }
+
+ if (touch_count == 0) {
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 0);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 0);
+#ifndef TYPE_B_PROTOCOL
+ input_mt_sync(rmi4_data->input_dev);
+#endif
+ }
+
+ input_sync(rmi4_data->input_dev);
+
+ return touch_count;
+}
+
+ /**
+ * synaptics_rmi4_f12_abs_report()
+ *
+ * Called by synaptics_rmi4_report_touch() when valid Function $12
+ * finger data has been detected.
+ *
+ * This function reads the Function $12 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, and returns the number of fingers detected.
+ */
+static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ unsigned char touch_count = 0; /* number of touch points */
+ unsigned char finger;
+ unsigned char fingers_to_process;
+ unsigned char finger_status;
+ unsigned char size_of_2d_data;
+ unsigned short data_addr;
+ int x;
+ int y;
+ int wx;
+ int wy;
+ int temp;
+ struct synaptics_rmi4_f12_extra_data *extra_data;
+ struct synaptics_rmi4_f12_finger_data *data;
+ struct synaptics_rmi4_f12_finger_data *finger_data;
+#ifdef F12_DATA_15_WORKAROUND
+ static unsigned char fingers_already_present;
+#endif
+
+ fingers_to_process = fhandler->num_of_data_points;
+ data_addr = fhandler->full_addr.data_base;
+ extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
+ size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
+
+
+ /* Determine the total number of fingers to process */
+ if (extra_data->data15_size) {
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ data_addr + extra_data->data15_offset,
+ extra_data->data15_data,
+ extra_data->data15_size);
+ if (retval < 0)
+ return 0;
+
+ /* Start checking from the highest bit */
+ temp = extra_data->data15_size - 1; /* Highest byte */
+ finger = (fingers_to_process - 1) % 8; /* Highest bit */
+ do {
+ if (extra_data->data15_data[temp] & (1 << finger))
+ break;
+
+ if (finger) {
+ finger--;
+ } else {
+ temp--; /* Move to the next lower byte */
+ finger = 7;
+ }
+
+ fingers_to_process--;
+ } while (fingers_to_process);
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Number of fingers to process = %d\n",
+ __func__, fingers_to_process);
+ }
+
+#ifdef F12_DATA_15_WORKAROUND
+ fingers_to_process = max(fingers_to_process, fingers_already_present);
+#endif
+
+ if (!fingers_to_process) {
+ synaptics_rmi4_free_fingers(rmi4_data);
+ return 0;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ data_addr + extra_data->data1_offset,
+ (unsigned char *)fhandler->data,
+ fingers_to_process * size_of_2d_data);
+ if (retval < 0)
+ return 0;
+
+ data = (struct synaptics_rmi4_f12_finger_data *)fhandler->data;
+
+ for (finger = 0; finger < fingers_to_process; finger++) {
+ finger_data = data + finger;
+ finger_status = finger_data->object_type_and_status;
+
+ if (finger_status == F12_FINGER_STATUS) {
+#ifdef TYPE_B_PROTOCOL
+ input_mt_slot(rmi4_data->input_dev, finger);
+ input_mt_report_slot_state(rmi4_data->input_dev,
+ MT_TOOL_FINGER, 1);
+#endif
+
+#ifdef F12_DATA_15_WORKAROUND
+ fingers_already_present = finger + 1;
+#endif
+
+ x = (finger_data->x_msb << 8) | (finger_data->x_lsb);
+ y = (finger_data->y_msb << 8) | (finger_data->y_lsb);
+#ifdef REPORT_2D_W
+ wx = finger_data->wx;
+ wy = finger_data->wy;
+#endif
+
+ if (rmi4_data->hw_if->board_data->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ temp = wx;
+ wx = wy;
+ wy = temp;
+ }
+
+ if (rmi4_data->hw_if->board_data->x_flip)
+ x = rmi4_data->sensor_max_x - x;
+ if (rmi4_data->hw_if->board_data->y_flip)
+ y = rmi4_data->sensor_max_y - y;
+
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 1);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 1);
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_X, x);
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_POSITION_Y, y);
+#ifdef REPORT_2D_W
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MAJOR, max(wx, wy));
+ input_report_abs(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MINOR, min(wx, wy));
+#endif
+#ifndef TYPE_B_PROTOCOL
+ input_mt_sync(rmi4_data->input_dev);
+#endif
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Finger %d:\n"
+ "status = 0x%02x\n"
+ "x = %d\n"
+ "y = %d\n"
+ "wx = %d\n"
+ "wy = %d\n",
+ __func__, finger,
+ finger_status,
+ x, y, wx, wy);
+
+ touch_count++;
+ } else {
+#ifdef TYPE_B_PROTOCOL
+ input_mt_slot(rmi4_data->input_dev, finger);
+ input_mt_report_slot_state(rmi4_data->input_dev,
+ MT_TOOL_FINGER, 0);
+#endif
+ }
+ }
+
+ if (touch_count == 0) {
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 0);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 0);
+#ifndef TYPE_B_PROTOCOL
+ input_mt_sync(rmi4_data->input_dev);
+#endif
+ }
+
+ input_sync(rmi4_data->input_dev);
+
+ return touch_count;
+}
+
+static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ unsigned char touch_count = 0;
+ unsigned char button;
+ unsigned char index;
+ unsigned char shift;
+ unsigned char status;
+ unsigned char *data;
+ unsigned short data_addr = fhandler->full_addr.data_base;
+ struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
+ static unsigned char do_once = 1;
+ static bool current_status[MAX_NUMBER_OF_BUTTONS];
+#ifdef NO_0D_WHILE_2D
+ static bool before_2d_status[MAX_NUMBER_OF_BUTTONS];
+ static bool while_2d_status[MAX_NUMBER_OF_BUTTONS];
+#endif
+
+ if (do_once) {
+ memset(current_status, 0, sizeof(current_status));
+#ifdef NO_0D_WHILE_2D
+ memset(before_2d_status, 0, sizeof(before_2d_status));
+ memset(while_2d_status, 0, sizeof(while_2d_status));
+#endif
+ do_once = 0;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ data_addr,
+ f1a->button_data_buffer,
+ f1a->button_bitmask_size);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read button data registers\n",
+ __func__);
+ return;
+ }
+
+ data = f1a->button_data_buffer;
+
+ for (button = 0; button < f1a->valid_button_count; button++) {
+ index = button / 8;
+ shift = button % 8;
+ status = ((data[index] >> shift) & MASK_1BIT);
+
+ if (current_status[button] == status)
+ continue;
+ else
+ current_status[button] = status;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Button %d (code %d) ->%d\n",
+ __func__, button,
+ f1a->button_map[button],
+ status);
+#ifdef NO_0D_WHILE_2D
+ if (rmi4_data->fingers_on_2d == false) {
+ if (status == 1) {
+ before_2d_status[button] = 1;
+ } else {
+ if (while_2d_status[button] == 1) {
+ while_2d_status[button] = 0;
+ continue;
+ } else {
+ before_2d_status[button] = 0;
+ }
+ }
+ touch_count++;
+ input_report_key(rmi4_data->input_dev,
+ f1a->button_map[button],
+ status);
+ } else {
+ if (before_2d_status[button] == 1) {
+ before_2d_status[button] = 0;
+ touch_count++;
+ input_report_key(rmi4_data->input_dev,
+ f1a->button_map[button],
+ status);
+ } else {
+ if (status == 1)
+ while_2d_status[button] = 1;
+ else
+ while_2d_status[button] = 0;
+ }
+ }
+#else
+ touch_count++;
+ input_report_key(rmi4_data->input_dev,
+ f1a->button_map[button],
+ status);
+#endif
+ }
+
+ if (touch_count)
+ input_sync(rmi4_data->input_dev);
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_report_touch()
+ *
+ * Called by synaptics_rmi4_sensor_report().
+ *
+ * This function calls the appropriate finger data reporting function
+ * based on the function handler it receives and returns the number of
+ * fingers detected.
+ */
+static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ unsigned char touch_count_2d;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Function %02x reporting\n",
+ __func__, fhandler->fn_number);
+
+ switch (fhandler->fn_number) {
+ case SYNAPTICS_RMI4_F11:
+ touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data,
+ fhandler);
+
+ if (touch_count_2d)
+ rmi4_data->fingers_on_2d = true;
+ else
+ rmi4_data->fingers_on_2d = false;
+ break;
+ case SYNAPTICS_RMI4_F12:
+ touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data,
+ fhandler);
+
+ if (touch_count_2d)
+ rmi4_data->fingers_on_2d = true;
+ else
+ rmi4_data->fingers_on_2d = false;
+ break;
+ case SYNAPTICS_RMI4_F1A:
+ synaptics_rmi4_f1a_report(rmi4_data, fhandler);
+ break;
+ default:
+ break;
+ }
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_sensor_report()
+ *
+ * Called by synaptics_rmi4_irq().
+ *
+ * This function determines the interrupt source(s) from the sensor
+ * and calls synaptics_rmi4_report_touch() with the appropriate
+ * function handler for each function with valid data inputs.
+ */
+static void synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char data[MAX_INTR_REGISTERS + 1];
+ unsigned char *intr = &data[1];
+ struct synaptics_rmi4_f01_device_status status;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ /*
+ * Get interrupt status information from F01 Data1 register to
+ * determine the source(s) that are flagging the interrupt.
+ */
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ data,
+ rmi4_data->num_of_intr_regs + 1);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read interrupt status\n",
+ __func__);
+ return;
+ }
+
+ status.data[0] = data[0];
+ if (status.unconfigured && !status.flash_prog) {
+ pr_notice("%s: spontaneous reset detected\n", __func__);
+ retval = synaptics_rmi4_reinit_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to reinit device\n",
+ __func__);
+ }
+ return;
+ }
+
+ /*
+ * Traverse the function handler list and service the source(s)
+ * of the interrupt accordingly.
+ */
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ if (fhandler->intr_mask &
+ intr[fhandler->intr_reg_num]) {
+ synaptics_rmi4_report_touch(rmi4_data,
+ fhandler);
+ }
+ }
+ }
+ }
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link) {
+ if (!exp_fhandler->insert &&
+ !exp_fhandler->remove &&
+ (exp_fhandler->exp_fn->attn != NULL))
+ exp_fhandler->exp_fn->attn(rmi4_data, intr[0]);
+ }
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_irq()
+ *
+ * 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_rmi4_data *rmi4_data = data;
+
+ if (!rmi4_data->touch_stopped)
+ synaptics_rmi4_sensor_report(rmi4_data);
+
+ return IRQ_HANDLED;
+}
+
+ /**
+ * synaptics_rmi4_irq_enable()
+ *
+ * Called by synaptics_rmi4_probe() and the power management functions
+ * in this driver and also exported to other expansion Function modules
+ * such as rmi_dev.
+ *
+ * This function handles the enabling and disabling of the attention
+ * irq including the setting up of the ISR thread.
+ */
+static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ int retval = 0;
+ unsigned char intr_status[MAX_INTR_REGISTERS];
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ if (enable) {
+ if (rmi4_data->irq_enabled)
+ return retval;
+
+ /* Clear interrupts first */
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr + 1,
+ intr_status,
+ rmi4_data->num_of_intr_regs);
+ if (retval < 0)
+ return retval;
+
+ retval = request_threaded_irq(rmi4_data->irq, NULL,
+ synaptics_rmi4_irq, bdata->irq_flags,
+ PLATFORM_DRIVER_NAME, rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create irq thread\n",
+ __func__);
+ return retval;
+ }
+
+ rmi4_data->irq_enabled = true;
+ } else {
+ if (rmi4_data->irq_enabled) {
+ disable_irq(rmi4_data->irq);
+ free_irq(rmi4_data->irq, rmi4_data);
+ rmi4_data->irq_enabled = false;
+ }
+ }
+
+ return retval;
+}
+
+static void synaptics_rmi4_set_intr_mask(struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ unsigned char ii;
+ unsigned char intr_offset;
+
+ fhandler->intr_reg_num = (intr_count + 7) / 8;
+ if (fhandler->intr_reg_num != 0)
+ fhandler->intr_reg_num -= 1;
+
+ /* Set an enable bit for each data source */
+ intr_offset = intr_count % 8;
+ fhandler->intr_mask = 0;
+ for (ii = intr_offset;
+ ii < ((fd->intr_src_count & MASK_3BIT) +
+ intr_offset);
+ ii++)
+ fhandler->intr_mask |= 1 << ii;
+
+ return;
+}
+
+static int synaptics_rmi4_f01_init(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+ fhandler->data = NULL;
+ fhandler->extra = NULL;
+
+ synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+
+ rmi4_data->f01_query_base_addr = fd->query_base_addr;
+ rmi4_data->f01_ctrl_base_addr = fd->ctrl_base_addr;
+ rmi4_data->f01_data_base_addr = fd->data_base_addr;
+ rmi4_data->f01_cmd_base_addr = fd->cmd_base_addr;
+
+ return 0;
+}
+
+ /**
+ * synaptics_rmi4_f11_init()
+ *
+ * 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_f11_init(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ int retval;
+ unsigned char abs_data_size;
+ unsigned char abs_data_blk_size;
+ unsigned char query[F11_STD_QUERY_LEN];
+ unsigned char control[F11_STD_CTRL_LEN];
+
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.query_base,
+ query,
+ sizeof(query));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum number of fingers supported */
+ if ((query[1] & MASK_3BIT) <= 4)
+ fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1;
+ else if ((query[1] & MASK_3BIT) == 5)
+ fhandler->num_of_data_points = 10;
+
+ rmi4_data->num_of_fingers = fhandler->num_of_data_points;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.ctrl_base,
+ control,
+ sizeof(control));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum x and y */
+ rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) |
+ ((control[7] & MASK_4BIT) << 8);
+ rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) |
+ ((control[9] & MASK_4BIT) << 8);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Function %02x max x = %d max y = %d\n",
+ __func__, fhandler->fn_number,
+ rmi4_data->sensor_max_x,
+ rmi4_data->sensor_max_y);
+
+ rmi4_data->max_touch_width = MAX_F11_TOUCH_WIDTH;
+
+ synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+
+ abs_data_size = query[5] & MASK_2BIT;
+ abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0));
+ fhandler->size_of_data_register_block = abs_data_blk_size;
+ fhandler->data = NULL;
+ fhandler->extra = NULL;
+
+ return retval;
+}
+
+static int synaptics_rmi4_f12_set_enables(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short ctrl28)
+{
+ int retval;
+ static unsigned short ctrl_28_address;
+
+ if (ctrl28)
+ ctrl_28_address = ctrl28;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ ctrl_28_address,
+ &rmi4_data->report_enable,
+ sizeof(rmi4_data->report_enable));
+ if (retval < 0)
+ return retval;
+
+ return retval;
+}
+
+ /**
+ * synaptics_rmi4_f12_init()
+ *
+ * Called by synaptics_rmi4_query_device().
+ *
+ * This funtion parses information from the Function 12 registers and
+ * determines the number of fingers supported, offset to the data1
+ * register, x and y data ranges, offset to the associated interrupt
+ * status register, interrupt bit mask, and allocates memory resources
+ * for finger data acquisition.
+ */
+static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ int retval;
+ unsigned char size_of_2d_data;
+ unsigned char size_of_query8;
+ unsigned char ctrl_8_offset;
+ unsigned char ctrl_23_offset;
+ unsigned char ctrl_28_offset;
+ unsigned char num_of_fingers;
+ struct synaptics_rmi4_f12_extra_data *extra_data;
+ struct synaptics_rmi4_f12_query_5 query_5;
+ struct synaptics_rmi4_f12_query_8 query_8;
+ struct synaptics_rmi4_f12_ctrl_8 ctrl_8;
+ struct synaptics_rmi4_f12_ctrl_23 ctrl_23;
+
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+ fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL);
+ extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
+ size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.query_base + 5,
+ query_5.data,
+ sizeof(query_5.data));
+ if (retval < 0)
+ return retval;
+
+ ctrl_8_offset = query_5.ctrl0_is_present +
+ query_5.ctrl1_is_present +
+ query_5.ctrl2_is_present +
+ query_5.ctrl3_is_present +
+ query_5.ctrl4_is_present +
+ query_5.ctrl5_is_present +
+ query_5.ctrl6_is_present +
+ query_5.ctrl7_is_present;
+
+ ctrl_23_offset = ctrl_8_offset +
+ query_5.ctrl8_is_present +
+ query_5.ctrl9_is_present +
+ query_5.ctrl10_is_present +
+ query_5.ctrl11_is_present +
+ query_5.ctrl12_is_present +
+ query_5.ctrl13_is_present +
+ query_5.ctrl14_is_present +
+ query_5.ctrl15_is_present +
+ query_5.ctrl16_is_present +
+ query_5.ctrl17_is_present +
+ query_5.ctrl18_is_present +
+ query_5.ctrl19_is_present +
+ query_5.ctrl20_is_present +
+ query_5.ctrl21_is_present +
+ query_5.ctrl22_is_present;
+
+ ctrl_28_offset = ctrl_23_offset +
+ query_5.ctrl23_is_present +
+ query_5.ctrl24_is_present +
+ query_5.ctrl25_is_present +
+ query_5.ctrl26_is_present +
+ query_5.ctrl27_is_present;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.ctrl_base + ctrl_23_offset,
+ ctrl_23.data,
+ sizeof(ctrl_23.data));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum number of fingers supported */
+ fhandler->num_of_data_points = min(ctrl_23.max_reported_objects,
+ (unsigned char)F12_FINGERS_TO_SUPPORT);
+
+ num_of_fingers = fhandler->num_of_data_points;
+ rmi4_data->num_of_fingers = num_of_fingers;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.query_base + 7,
+ &size_of_query8,
+ sizeof(size_of_query8));
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.query_base + 8,
+ query_8.data,
+ size_of_query8);
+ if (retval < 0)
+ return retval;
+
+ /* Determine the presence of the Data0 register */
+ extra_data->data1_offset = query_8.data0_is_present;
+
+ if ((size_of_query8 >= 3) && (query_8.data15_is_present)) {
+ extra_data->data15_offset = query_8.data0_is_present +
+ query_8.data1_is_present +
+ query_8.data2_is_present +
+ query_8.data3_is_present +
+ query_8.data4_is_present +
+ query_8.data5_is_present +
+ query_8.data6_is_present +
+ query_8.data7_is_present +
+ query_8.data8_is_present +
+ query_8.data9_is_present +
+ query_8.data10_is_present +
+ query_8.data11_is_present +
+ query_8.data12_is_present +
+ query_8.data13_is_present +
+ query_8.data14_is_present;
+ extra_data->data15_size = (num_of_fingers + 7) / 8;
+ } else {
+ extra_data->data15_size = 0;
+ }
+
+ rmi4_data->report_enable = RPT_DEFAULT;
+#ifdef REPORT_2D_Z
+ rmi4_data->report_enable |= RPT_Z;
+#endif
+#ifdef REPORT_2D_W
+ rmi4_data->report_enable |= (RPT_WX | RPT_WY);
+#endif
+
+ retval = synaptics_rmi4_f12_set_enables(rmi4_data,
+ fhandler->full_addr.ctrl_base + ctrl_28_offset);
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.ctrl_base + ctrl_8_offset,
+ ctrl_8.data,
+ sizeof(ctrl_8.data));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum x and y */
+ rmi4_data->sensor_max_x =
+ ((unsigned short)ctrl_8.max_x_coord_lsb << 0) |
+ ((unsigned short)ctrl_8.max_x_coord_msb << 8);
+ rmi4_data->sensor_max_y =
+ ((unsigned short)ctrl_8.max_y_coord_lsb << 0) |
+ ((unsigned short)ctrl_8.max_y_coord_msb << 8);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Function %02x max x = %d max y = %d\n",
+ __func__, fhandler->fn_number,
+ rmi4_data->sensor_max_x,
+ rmi4_data->sensor_max_y);
+
+ rmi4_data->num_of_rx = ctrl_8.num_of_rx;
+ rmi4_data->num_of_tx = ctrl_8.num_of_tx;
+ rmi4_data->max_touch_width = max(rmi4_data->num_of_rx,
+ rmi4_data->num_of_tx);
+
+ synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+
+ /* Allocate memory for finger data storage space */
+ fhandler->data_size = num_of_fingers * size_of_2d_data;
+ fhandler->data = kmalloc(fhandler->data_size, GFP_KERNEL);
+
+ return retval;
+}
+
+static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ struct synaptics_rmi4_f1a_handle *f1a;
+
+ f1a = kzalloc(sizeof(*f1a), GFP_KERNEL);
+ if (!f1a) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for function handle\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ fhandler->data = (void *)f1a;
+ fhandler->extra = NULL;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.query_base,
+ f1a->button_query.data,
+ sizeof(f1a->button_query.data));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read query registers\n",
+ __func__);
+ return retval;
+ }
+
+ f1a->max_count = f1a->button_query.max_button_count + 1;
+
+ f1a->button_control.txrx_map = kzalloc(f1a->max_count * 2, GFP_KERNEL);
+ if (!f1a->button_control.txrx_map) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for tx rx mapping\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ f1a->button_bitmask_size = (f1a->max_count + 7) / 8;
+
+ f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size,
+ sizeof(*(f1a->button_data_buffer)), GFP_KERNEL);
+ if (!f1a->button_data_buffer) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for data buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ f1a->button_map = kcalloc(f1a->max_count,
+ sizeof(*(f1a->button_map)), GFP_KERNEL);
+ if (!f1a->button_map) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for button map\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int synaptics_rmi4_f1a_button_map(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char mapping_offset = 0;
+ struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ mapping_offset = f1a->button_query.has_general_control +
+ f1a->button_query.has_interrupt_enable +
+ f1a->button_query.has_multibutton_select;
+
+ if (f1a->button_query.has_tx_rx_map) {
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fhandler->full_addr.ctrl_base + mapping_offset,
+ f1a->button_control.txrx_map,
+ sizeof(f1a->button_control.txrx_map));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read tx rx mapping\n",
+ __func__);
+ return retval;
+ }
+
+ rmi4_data->button_txrx_mapping = f1a->button_control.txrx_map;
+ }
+
+ if (!bdata->cap_button_map) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: cap_button_map is NULL in board file\n",
+ __func__);
+ return -ENODEV;
+ } else if (!bdata->cap_button_map->map) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Button map is missing in board file\n",
+ __func__);
+ return -ENODEV;
+ } else {
+ if (bdata->cap_button_map->nbuttons != f1a->max_count) {
+ f1a->valid_button_count = min(f1a->max_count,
+ bdata->cap_button_map->nbuttons);
+ } else {
+ f1a->valid_button_count = f1a->max_count;
+ }
+
+ for (ii = 0; ii < f1a->valid_button_count; ii++)
+ f1a->button_map[ii] = bdata->cap_button_map->map[ii];
+ }
+
+ return 0;
+}
+
+static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler)
+{
+ struct synaptics_rmi4_f1a_handle *f1a = fhandler->data;
+
+ if (f1a) {
+ kfree(f1a->button_control.txrx_map);
+ kfree(f1a->button_data_buffer);
+ kfree(f1a->button_map);
+ kfree(f1a);
+ fhandler->data = NULL;
+ }
+
+ return;
+}
+
+static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data,
+ struct synaptics_rmi4_fn *fhandler,
+ struct synaptics_rmi4_fn_desc *fd,
+ unsigned int intr_count)
+{
+ int retval;
+
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+
+ synaptics_rmi4_set_intr_mask(fhandler, fd, intr_count);
+
+ retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler);
+ if (retval < 0)
+ goto error_exit;
+
+ retval = synaptics_rmi4_f1a_button_map(rmi4_data, fhandler);
+ if (retval < 0)
+ goto error_exit;
+
+ rmi4_data->button_0d_enabled = 1;
+
+ return 0;
+
+error_exit:
+ synaptics_rmi4_f1a_kfree(fhandler);
+
+ return retval;
+}
+
+static void synaptics_rmi4_empty_fn_list(struct synaptics_rmi4_data *rmi4_data)
+{
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_fn *fhandler_temp;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry_safe(fhandler,
+ fhandler_temp,
+ &rmi->support_fn_list,
+ link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) {
+ synaptics_rmi4_f1a_kfree(fhandler);
+ } else {
+ kfree(fhandler->extra);
+ kfree(fhandler->data);
+ }
+ list_del(&fhandler->link);
+ kfree(fhandler);
+ }
+ }
+ INIT_LIST_HEAD(&rmi->support_fn_list);
+
+ return;
+}
+
+static int synaptics_rmi4_check_status(struct synaptics_rmi4_data *rmi4_data,
+ bool *was_in_bl_mode)
+{
+ int retval;
+ int timeout = CHECK_STATUS_TIMEOUT_MS;
+ unsigned char command = 0x01;
+ unsigned char intr_status;
+ struct synaptics_rmi4_f01_device_status status;
+
+ /* Do a device reset first */
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_cmd_base_addr,
+ &command,
+ sizeof(command));
+ if (retval < 0)
+ return retval;
+
+ msleep(rmi4_data->hw_if->board_data->reset_delay_ms);
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ status.data,
+ sizeof(status.data));
+ if (retval < 0)
+ return retval;
+
+ while (status.status_code == STATUS_CRC_IN_PROGRESS) {
+ if (timeout > 0)
+ msleep(20);
+ else
+ return -1;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ status.data,
+ sizeof(status.data));
+ if (retval < 0)
+ return retval;
+
+ timeout -= 20;
+ }
+
+ if (timeout != CHECK_STATUS_TIMEOUT_MS)
+ *was_in_bl_mode = true;
+
+ if (status.flash_prog == 1) {
+ rmi4_data->flash_prog_mode = true;
+ pr_notice("%s: In flash prog mode, status = 0x%02x\n",
+ __func__,
+ status.status_code);
+ } else {
+ rmi4_data->flash_prog_mode = false;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr + 1,
+ &intr_status,
+ sizeof(intr_status));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read interrupt status\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void synaptics_rmi4_set_configured(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char device_ctrl;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to set configured\n",
+ __func__);
+ return;
+ }
+
+ rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON;
+ device_ctrl |= CONFIGURED;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to set configured\n",
+ __func__);
+ }
+
+ return;
+}
+
+static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler,
+ struct synaptics_rmi4_fn_desc *rmi_fd, int page_number)
+{
+ *fhandler = kmalloc(sizeof(**fhandler), GFP_KERNEL);
+ if (!(*fhandler))
+ return -ENOMEM;
+
+ (*fhandler)->full_addr.data_base =
+ (rmi_fd->data_base_addr |
+ (page_number << 8));
+ (*fhandler)->full_addr.ctrl_base =
+ (rmi_fd->ctrl_base_addr |
+ (page_number << 8));
+ (*fhandler)->full_addr.cmd_base =
+ (rmi_fd->cmd_base_addr |
+ (page_number << 8));
+ (*fhandler)->full_addr.query_base =
+ (rmi_fd->query_base_addr |
+ (page_number << 8));
+
+ return 0;
+}
+
+ /**
+ * synaptics_rmi4_query_device()
+ *
+ * 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 and Function $12, 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_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char page_number;
+ unsigned char intr_count;
+ unsigned char f01_query[F01_STD_QUERY_LEN];
+ unsigned short pdt_entry_addr;
+ unsigned short intr_addr;
+ bool f01found;
+ bool was_in_bl_mode;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+rescan_pdt:
+ f01found = false;
+ was_in_bl_mode = false;
+ intr_count = 0;
+ INIT_LIST_HEAD(&rmi->support_fn_list);
+
+ /* Scan the page description tables of the pages to service */
+ for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) {
+ for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END;
+ pdt_entry_addr -= PDT_ENTRY_SIZE) {
+ pdt_entry_addr |= (page_number << 8);
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ pdt_entry_addr,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval < 0)
+ return retval;
+
+ pdt_entry_addr &= ~(MASK_8BIT << 8);
+
+ fhandler = NULL;
+
+ if (rmi_fd.fn_number == 0) {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Reached end of PDT\n",
+ __func__);
+ break;
+ }
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: F%02x found (page %d)\n",
+ __func__, rmi_fd.fn_number,
+ page_number);
+
+ switch (rmi_fd.fn_number) {
+ case SYNAPTICS_RMI4_F01:
+ if (rmi_fd.intr_src_count == 0)
+ break;
+
+ f01found = true;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi_fd.fn_number);
+ return retval;
+ }
+
+ retval = synaptics_rmi4_f01_init(rmi4_data,
+ fhandler, &rmi_fd, intr_count);
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_check_status(rmi4_data,
+ &was_in_bl_mode);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to check status\n",
+ __func__);
+ return retval;
+ }
+
+ if (was_in_bl_mode) {
+ kfree(fhandler);
+ fhandler = NULL;
+ goto rescan_pdt;
+ }
+
+ if (rmi4_data->flash_prog_mode)
+ goto flash_prog_mode;
+
+ break;
+ case SYNAPTICS_RMI4_F11:
+ if (rmi_fd.intr_src_count == 0)
+ break;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi_fd.fn_number);
+ return retval;
+ }
+
+ retval = synaptics_rmi4_f11_init(rmi4_data,
+ fhandler, &rmi_fd, intr_count);
+ if (retval < 0)
+ return retval;
+ break;
+ case SYNAPTICS_RMI4_F12:
+ if (rmi_fd.intr_src_count == 0)
+ break;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi_fd.fn_number);
+ return retval;
+ }
+
+ retval = synaptics_rmi4_f12_init(rmi4_data,
+ fhandler, &rmi_fd, intr_count);
+ if (retval < 0)
+ return retval;
+ break;
+ case SYNAPTICS_RMI4_F1A:
+ if (rmi_fd.intr_src_count == 0)
+ break;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc for F%d\n",
+ __func__,
+ rmi_fd.fn_number);
+ return retval;
+ }
+
+ retval = synaptics_rmi4_f1a_init(rmi4_data,
+ fhandler, &rmi_fd, intr_count);
+ if (retval < 0) {
+#ifdef IGNORE_FN_INIT_FAILURE
+ kfree(fhandler);
+ fhandler = NULL;
+#else
+ return retval;
+#endif
+ }
+ break;
+ }
+
+ /* Accumulate the interrupt count */
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+
+ if (fhandler && rmi_fd.intr_src_count) {
+ list_add_tail(&fhandler->link,
+ &rmi->support_fn_list);
+ }
+ }
+ }
+
+ if (!f01found) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to find F01\n",
+ __func__);
+ return -EINVAL;
+ }
+
+flash_prog_mode:
+ rmi4_data->num_of_intr_regs = (intr_count + 7) / 8;
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Number of interrupt registers = %d\n",
+ __func__, rmi4_data->num_of_intr_regs);
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_query_base_addr,
+ f01_query,
+ sizeof(f01_query));
+ if (retval < 0)
+ return retval;
+
+ /* RMI Version 4.0 currently supported */
+ rmi->version_major = 4;
+ rmi->version_minor = 0;
+
+ rmi->manufacturer_id = f01_query[0];
+ rmi->product_props = f01_query[1];
+ rmi->product_info[0] = f01_query[2] & MASK_7BIT;
+ rmi->product_info[1] = f01_query[3] & MASK_7BIT;
+ rmi->date_code[0] = f01_query[4] & MASK_5BIT;
+ rmi->date_code[1] = f01_query[5] & MASK_4BIT;
+ rmi->date_code[2] = f01_query[6] & MASK_5BIT;
+ rmi->tester_id = ((f01_query[7] & MASK_7BIT) << 8) |
+ (f01_query[8] & MASK_7BIT);
+ rmi->serial_number = ((f01_query[9] & MASK_7BIT) << 8) |
+ (f01_query[10] & MASK_7BIT);
+ memcpy(rmi->product_id_string, &f01_query[11], 10);
+
+ if (rmi->manufacturer_id != 1) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Non-Synaptics device found, manufacturer ID = %d\n",
+ __func__, rmi->manufacturer_id);
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_query_base_addr + F01_BUID_ID_OFFSET,
+ rmi->build_id,
+ sizeof(rmi->build_id));
+ if (retval < 0)
+ return retval;
+
+ rmi4_data->firmware_id = (unsigned int)rmi->build_id[0] +
+ (unsigned int)rmi->build_id[1] * 0x100 +
+ (unsigned int)rmi->build_id[2] * 0x10000;
+
+ memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask));
+
+ /*
+ * Map out the interrupt bit masks for the interrupt sources
+ * from the registered function handlers.
+ */
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->num_of_data_sources) {
+ rmi4_data->intr_mask[fhandler->intr_reg_num] |=
+ fhandler->intr_mask;
+ }
+ }
+ }
+
+ /* Enable the interrupt sources */
+ for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) {
+ if (rmi4_data->intr_mask[ii] != 0x00) {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Interrupt enable mask %d = 0x%02x\n",
+ __func__, ii, rmi4_data->intr_mask[ii]);
+ intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii;
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ intr_addr,
+ &(rmi4_data->intr_mask[ii]),
+ sizeof(rmi4_data->intr_mask[ii]));
+ if (retval < 0)
+ return retval;
+ }
+ }
+
+ synaptics_rmi4_set_configured(rmi4_data);
+
+ return 0;
+}
+
+static void synaptics_rmi4_set_params(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char ii;
+ struct synaptics_rmi4_f1a_handle *f1a;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ 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);
+#ifdef REPORT_2D_W
+ input_set_abs_params(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MAJOR, 0,
+ rmi4_data->max_touch_width, 0, 0);
+ input_set_abs_params(rmi4_data->input_dev,
+ ABS_MT_TOUCH_MINOR, 0,
+ rmi4_data->max_touch_width, 0, 0);
+#endif
+
+#ifdef TYPE_B_PROTOCOL
+ input_mt_init_slots(rmi4_data->input_dev,
+ rmi4_data->num_of_fingers);
+#endif
+
+ f1a = NULL;
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F1A)
+ f1a = fhandler->data;
+ }
+ }
+
+ if (f1a) {
+ for (ii = 0; ii < f1a->valid_button_count; ii++) {
+ set_bit(f1a->button_map[ii],
+ rmi4_data->input_dev->keybit);
+ input_set_capability(rmi4_data->input_dev,
+ EV_KEY, f1a->button_map[ii]);
+ }
+ }
+
+ return;
+}
+
+static int synaptics_rmi4_set_input_dev(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ int temp;
+
+ rmi4_data->input_dev = input_allocate_device();
+ if (rmi4_data->input_dev == NULL) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate input device\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_input_device;
+ }
+
+ retval = synaptics_rmi4_query_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to query device\n",
+ __func__);
+ goto err_query_device;
+ }
+
+ rmi4_data->input_dev->name = PLATFORM_DRIVER_NAME;
+ rmi4_data->input_dev->phys = INPUT_PHYS_NAME;
+ rmi4_data->input_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
+ rmi4_data->input_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
+ rmi4_data->input_dev->dev.parent = rmi4_data->pdev->dev.parent;
+ input_set_drvdata(rmi4_data->input_dev, rmi4_data);
+
+ 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);
+ set_bit(BTN_TOUCH, rmi4_data->input_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, rmi4_data->input_dev->keybit);
+#ifdef INPUT_PROP_DIRECT
+ set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit);
+#endif
+
+ if (rmi4_data->hw_if->board_data->swap_axes) {
+ temp = rmi4_data->sensor_max_x;
+ rmi4_data->sensor_max_x = rmi4_data->sensor_max_y;
+ rmi4_data->sensor_max_y = temp;
+ }
+
+ synaptics_rmi4_set_params(rmi4_data);
+
+ retval = input_register_device(rmi4_data->input_dev);
+ if (retval) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to register input device\n",
+ __func__);
+ goto err_register_input;
+ }
+
+ return 0;
+
+err_register_input:
+err_query_device:
+ synaptics_rmi4_empty_fn_list(rmi4_data);
+ input_free_device(rmi4_data->input_dev);
+
+err_input_device:
+ return retval;
+}
+
+static int synaptics_rmi4_set_gpio(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ int power_on;
+ int reset_on;
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ power_on = bdata->power_on_state;
+ reset_on = bdata->reset_on_state;
+
+ retval = bdata->gpio_config(
+ bdata->irq_gpio,
+ true, 0, 0);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to configure attention GPIO\n",
+ __func__);
+ goto err_gpio_irq;
+ }
+
+ if (bdata->power_gpio >= 0) {
+ retval = bdata->gpio_config(
+ bdata->power_gpio,
+ true, 1, !power_on);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to configure power GPIO\n",
+ __func__);
+ goto err_gpio_power;
+ }
+ }
+
+ if (bdata->reset_gpio >= 0) {
+ retval = bdata->gpio_config(
+ bdata->reset_gpio,
+ true, 1, !reset_on);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to configure reset GPIO\n",
+ __func__);
+ goto err_gpio_reset;
+ }
+ }
+
+ if (bdata->power_gpio >= 0) {
+ gpio_set_value(bdata->power_gpio, power_on);
+ msleep(bdata->power_delay_ms);
+ }
+
+ if (bdata->reset_gpio >= 0) {
+ gpio_set_value(bdata->reset_gpio, reset_on);
+ msleep(bdata->reset_active_ms);
+ gpio_set_value(bdata->reset_gpio, !reset_on);
+ msleep(bdata->reset_delay_ms);
+ }
+
+ return 0;
+
+err_gpio_reset:
+ if (bdata->power_gpio >= 0) {
+ bdata->gpio_config(
+ bdata->power_gpio,
+ false, 0, 0);
+ }
+
+err_gpio_power:
+ bdata->gpio_config(
+ bdata->irq_gpio,
+ false, 0, 0);
+
+err_gpio_irq:
+ return retval;
+}
+
+static int synaptics_rmi4_free_fingers(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char ii;
+
+#ifdef TYPE_B_PROTOCOL
+ for (ii = 0; ii < rmi4_data->num_of_fingers; ii++) {
+ input_mt_slot(rmi4_data->input_dev, ii);
+ input_mt_report_slot_state(rmi4_data->input_dev,
+ MT_TOOL_FINGER, 0);
+ }
+#endif
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOUCH, 0);
+ input_report_key(rmi4_data->input_dev,
+ BTN_TOOL_FINGER, 0);
+#ifndef TYPE_B_PROTOCOL
+ input_mt_sync(rmi4_data->input_dev);
+#endif
+ input_sync(rmi4_data->input_dev);
+
+ rmi4_data->fingers_on_2d = false;
+
+ return 0;
+}
+
+static int synaptics_rmi4_reinit_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char ii;
+ unsigned short intr_addr;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ mutex_lock(&(rmi4_data->rmi4_reset_mutex));
+
+ synaptics_rmi4_free_fingers(rmi4_data);
+
+ if (!list_empty(&rmi->support_fn_list)) {
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F12) {
+ synaptics_rmi4_f12_set_enables(rmi4_data, 0);
+ break;
+ }
+ }
+ }
+
+ for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) {
+ if (rmi4_data->intr_mask[ii] != 0x00) {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Interrupt enable mask %d = 0x%02x\n",
+ __func__, ii, rmi4_data->intr_mask[ii]);
+ intr_addr = rmi4_data->f01_ctrl_base_addr + 1 + ii;
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ intr_addr,
+ &(rmi4_data->intr_mask[ii]),
+ sizeof(rmi4_data->intr_mask[ii]));
+ if (retval < 0)
+ goto exit;
+ }
+ }
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link)
+ if (exp_fhandler->exp_fn->reinit != NULL)
+ exp_fhandler->exp_fn->reinit(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ synaptics_rmi4_set_configured(rmi4_data);
+
+ retval = 0;
+
+exit:
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+ return retval;
+}
+
+static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ int temp;
+ unsigned char command = 0x01;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+
+ mutex_lock(&(rmi4_data->rmi4_reset_mutex));
+
+ rmi4_data->touch_stopped = true;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_cmd_base_addr,
+ &command,
+ sizeof(command));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to issue reset command, error = %d\n",
+ __func__, retval);
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+ return retval;
+ }
+
+ msleep(rmi4_data->hw_if->board_data->reset_delay_ms);
+
+ synaptics_rmi4_free_fingers(rmi4_data);
+
+ synaptics_rmi4_empty_fn_list(rmi4_data);
+
+ retval = synaptics_rmi4_query_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to query device\n",
+ __func__);
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+ return retval;
+ }
+
+ if (rmi4_data->hw_if->board_data->swap_axes) {
+ temp = rmi4_data->sensor_max_x;
+ rmi4_data->sensor_max_x = rmi4_data->sensor_max_y;
+ rmi4_data->sensor_max_y = temp;
+ }
+
+ synaptics_rmi4_set_params(rmi4_data);
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link)
+ if (exp_fhandler->exp_fn->reset != NULL)
+ exp_fhandler->exp_fn->reset(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ rmi4_data->touch_stopped = false;
+
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+
+ return 0;
+}
+
+/**
+* synaptics_rmi4_exp_fn_work()
+*
+* Called by the kernel at the scheduled time.
+*
+* This function is a work thread that checks for the insertion and
+* removal of other expansion Function modules such as rmi_dev and calls
+* their initialization and removal callback functions accordingly.
+*/
+static void synaptics_rmi4_exp_fn_work(struct work_struct *work)
+{
+ int retval;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler_temp;
+ struct synaptics_rmi4_data *rmi4_data = exp_data.rmi4_data;
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry_safe(exp_fhandler,
+ exp_fhandler_temp,
+ &exp_data.list,
+ link) {
+ if ((exp_fhandler->exp_fn->init != NULL) &&
+ exp_fhandler->insert) {
+ retval = exp_fhandler->exp_fn->init(rmi4_data);
+ if (retval < 0) {
+ list_del(&exp_fhandler->link);
+ kfree(exp_fhandler);
+ } else {
+ exp_fhandler->insert = false;
+ }
+ } else if ((exp_fhandler->exp_fn->remove != NULL) &&
+ exp_fhandler->remove) {
+ exp_fhandler->exp_fn->remove(rmi4_data);
+ list_del(&exp_fhandler->link);
+ kfree(exp_fhandler);
+ }
+ }
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ return;
+}
+
+/**
+* synaptics_rmi4_new_function()
+*
+* Called by other expansion Function modules in their module init and
+* module exit functions.
+*
+* This function is used by other expansion Function modules such as
+* rmi_dev to register themselves with the driver by providing their
+* initialization and removal callback function pointers so that they
+* can be inserted or removed dynamically at module init and exit times,
+* respectively.
+*/
+void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn,
+ bool insert)
+{
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+
+ if (!exp_data.initialized) {
+ mutex_init(&exp_data.mutex);
+ INIT_LIST_HEAD(&exp_data.list);
+ exp_data.initialized = true;
+ }
+
+ mutex_lock(&exp_data.mutex);
+ if (insert) {
+ exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL);
+ if (!exp_fhandler) {
+ pr_err("%s: Failed to alloc mem for expansion function\n",
+ __func__);
+ goto exit;
+ }
+ exp_fhandler->exp_fn = exp_fn;
+ exp_fhandler->insert = true;
+ exp_fhandler->remove = false;
+ list_add_tail(&exp_fhandler->link, &exp_data.list);
+ } else if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link) {
+ if (exp_fhandler->exp_fn->fn_type == exp_fn->fn_type) {
+ exp_fhandler->insert = false;
+ exp_fhandler->remove = true;
+ goto exit;
+ }
+ }
+ }
+
+exit:
+ mutex_unlock(&exp_data.mutex);
+
+ if (exp_data.queue_work) {
+ queue_delayed_work(exp_data.workqueue,
+ &exp_data.work,
+ msecs_to_jiffies(EXP_FN_WORK_DELAY_MS));
+ }
+
+ return;
+}
+EXPORT_SYMBOL(synaptics_rmi4_new_function);
+
+ /**
+ * synaptics_rmi4_probe()
+ *
+ * Called by the kernel when an association with an I2C device of the
+ * same name is made (after doing i2c_add_driver).
+ *
+ * This funtion allocates and initializes the resources for the driver
+ * as an input driver, turns on the power to the sensor, queries the
+ * sensor for its supported Functions and characteristics, registers
+ * the driver to the input subsystem, sets up the interrupt, handles
+ * the registration of the early_suspend and late_resume functions,
+ * and creates a work queue for detection of other expansion Function
+ * modules.
+ */
+static int __devinit synaptics_rmi4_probe(struct platform_device *pdev)
+{
+ int retval;
+ unsigned char attr_count;
+ struct synaptics_rmi4_data *rmi4_data;
+ const struct synaptics_dsx_hw_interface *hw_if;
+ const struct synaptics_dsx_board_data *bdata;
+
+ hw_if = pdev->dev.platform_data;
+ if (!hw_if) {
+ dev_err(&pdev->dev,
+ "%s: No hardware interface found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ bdata = hw_if->board_data;
+ if (!bdata) {
+ dev_err(&pdev->dev,
+ "%s: No board data found\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL);
+ if (!rmi4_data) {
+ dev_err(&pdev->dev,
+ "%s: Failed to alloc mem for rmi4_data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (*bdata->regulator_name != 0x00) {
+ rmi4_data->regulator = regulator_get(&pdev->dev,
+ bdata->regulator_name);
+ if (IS_ERR(rmi4_data->regulator)) {
+ dev_err(&pdev->dev,
+ "%s: Failed to get regulator\n",
+ __func__);
+ retval = PTR_ERR(rmi4_data->regulator);
+ goto err_regulator;
+ }
+ regulator_enable(rmi4_data->regulator);
+ msleep(bdata->power_delay_ms);
+ }
+
+ rmi4_data->pdev = pdev;
+ rmi4_data->current_page = MASK_8BIT;
+ rmi4_data->hw_if = hw_if;
+ rmi4_data->touch_stopped = false;
+ rmi4_data->sensor_sleep = false;
+ rmi4_data->irq_enabled = false;
+ rmi4_data->fingers_on_2d = false;
+
+ rmi4_data->irq_enable = synaptics_rmi4_irq_enable;
+ rmi4_data->reset_device = synaptics_rmi4_reset_device;
+
+ mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
+ mutex_init(&(rmi4_data->rmi4_reset_mutex));
+
+ platform_set_drvdata(pdev, rmi4_data);
+
+ if (bdata->gpio_config) {
+ retval = synaptics_rmi4_set_gpio(rmi4_data);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to set up GPIO's\n",
+ __func__);
+ goto err_set_gpio;
+ }
+ }
+
+ retval = synaptics_rmi4_set_input_dev(rmi4_data);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to set up input device\n",
+ __func__);
+ goto err_set_input_dev;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ rmi4_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi4_data->early_suspend.suspend = synaptics_rmi4_early_suspend;
+ rmi4_data->early_suspend.resume = synaptics_rmi4_late_resume;
+ register_early_suspend(&rmi4_data->early_suspend);
+#endif
+
+ rmi4_data->irq = gpio_to_irq(bdata->irq_gpio);
+
+ retval = synaptics_rmi4_irq_enable(rmi4_data, true);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to enable attention interrupt\n",
+ __func__);
+ goto err_enable_irq;
+ }
+
+ if (!exp_data.initialized) {
+ mutex_init(&exp_data.mutex);
+ INIT_LIST_HEAD(&exp_data.list);
+ exp_data.initialized = true;
+ }
+
+ exp_data.workqueue = create_singlethread_workqueue("dsx_exp_workqueue");
+ INIT_DELAYED_WORK(&exp_data.work, synaptics_rmi4_exp_fn_work);
+ exp_data.rmi4_data = rmi4_data;
+ exp_data.queue_work = true;
+ queue_delayed_work(exp_data.workqueue,
+ &exp_data.work,
+ msecs_to_jiffies(EXP_FN_WORK_DELAY_MS));
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(&pdev->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ goto err_sysfs;
+ }
+ }
+
+ return retval;
+
+err_sysfs:
+ for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ cancel_delayed_work_sync(&exp_data.work);
+ flush_workqueue(exp_data.workqueue);
+ destroy_workqueue(exp_data.workqueue);
+
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+
+err_enable_irq:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&rmi4_data->early_suspend);
+#endif
+
+ synaptics_rmi4_empty_fn_list(rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+
+err_set_input_dev:
+ if (bdata->gpio_config) {
+ bdata->gpio_config(
+ bdata->irq_gpio,
+ false, 0, 0);
+
+ if (bdata->reset_gpio >= 0) {
+ bdata->gpio_config(
+ bdata->reset_gpio,
+ false, 0, 0);
+ }
+
+ if (bdata->power_gpio >= 0) {
+ bdata->gpio_config(
+ bdata->power_gpio,
+ false, 0, 0);
+ }
+ }
+
+err_set_gpio:
+ if (rmi4_data->regulator) {
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ }
+
+err_regulator:
+ kfree(rmi4_data);
+
+ return retval;
+}
+
+ /**
+ * synaptics_rmi4_remove()
+ *
+ * Called by the kernel when the association with an I2C device of the
+ * same name is broken (when the driver is unloaded).
+ *
+ * This funtion terminates the work queue, stops sensor data acquisition,
+ * frees the interrupt, unregisters the driver from the input subsystem,
+ * turns off the power to the sensor, and frees other allocated resources.
+ */
+static int __devexit synaptics_rmi4_remove(struct platform_device *pdev)
+{
+ unsigned char attr_count;
+ struct synaptics_rmi4_data *rmi4_data = platform_get_drvdata(pdev);
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ cancel_delayed_work_sync(&exp_data.work);
+ flush_workqueue(exp_data.workqueue);
+ destroy_workqueue(exp_data.workqueue);
+
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&rmi4_data->early_suspend);
+#endif
+
+ synaptics_rmi4_empty_fn_list(rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+
+ if (bdata->gpio_config) {
+ bdata->gpio_config(
+ bdata->irq_gpio,
+ false, 0, 0);
+
+ if (bdata->reset_gpio >= 0) {
+ bdata->gpio_config(
+ bdata->reset_gpio,
+ false, 0, 0);
+ }
+
+ if (bdata->power_gpio >= 0) {
+ bdata->gpio_config(
+ bdata->power_gpio,
+ false, 0, 0);
+ }
+ }
+
+ if (rmi4_data->regulator) {
+ regulator_disable(rmi4_data->regulator);
+ regulator_put(rmi4_data->regulator);
+ }
+
+ kfree(rmi4_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+ /**
+ * synaptics_rmi4_sensor_sleep()
+ *
+ * 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_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char device_ctrl;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to enter sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = false;
+ return;
+ }
+
+ device_ctrl = (device_ctrl & ~MASK_3BIT);
+ device_ctrl = (device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP);
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to enter sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = false;
+ return;
+ } else {
+ rmi4_data->sensor_sleep = true;
+ }
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_sensor_wake()
+ *
+ * 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_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char device_ctrl;
+ unsigned char no_sleep_setting = rmi4_data->no_sleep_setting;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to wake from sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = true;
+ return;
+ }
+
+ device_ctrl = (device_ctrl & ~MASK_3BIT);
+ device_ctrl = (device_ctrl | no_sleep_setting | NORMAL_OPERATION);
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to wake from sleep mode\n",
+ __func__);
+ rmi4_data->sensor_sleep = true;
+ return;
+ } else {
+ rmi4_data->sensor_sleep = false;
+ }
+
+ return;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ /**
+ * synaptics_rmi4_early_suspend()
+ *
+ * Called by the kernel during the early suspend phase when the system
+ * enters suspend.
+ *
+ * This function calls synaptics_rmi4_sensor_sleep() to stop finger
+ * data acquisition and put the sensor to sleep.
+ */
+static void synaptics_rmi4_early_suspend(struct early_suspend *h)
+{
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(h, struct synaptics_rmi4_data,
+ early_suspend);
+
+ if (rmi4_data->stay_awake) {
+ rmi4_data->staying_awake = true;
+ return;
+ } else {
+ rmi4_data->staying_awake = false;
+ }
+
+ rmi4_data->touch_stopped = true;
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+ synaptics_rmi4_sensor_sleep(rmi4_data);
+ synaptics_rmi4_free_fingers(rmi4_data);
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link)
+ if (exp_fhandler->exp_fn->early_suspend != NULL)
+ exp_fhandler->exp_fn->early_suspend(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ if (rmi4_data->full_pm_cycle)
+ synaptics_rmi4_suspend(&(rmi4_data->input_dev->dev));
+
+ return;
+}
+
+ /**
+ * synaptics_rmi4_late_resume()
+ *
+ * Called by the kernel during the late resume phase when the system
+ * wakes up from suspend.
+ *
+ * This function goes through the sensor wake process if the system wakes
+ * up from early suspend (without going into suspend).
+ */
+static void synaptics_rmi4_late_resume(struct early_suspend *h)
+{
+ int retval;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_data *rmi4_data =
+ container_of(h, struct synaptics_rmi4_data,
+ early_suspend);
+
+ if (rmi4_data->staying_awake)
+ return;
+
+ if (rmi4_data->full_pm_cycle)
+ synaptics_rmi4_resume(&(rmi4_data->input_dev->dev));
+
+ if (rmi4_data->sensor_sleep == true) {
+ synaptics_rmi4_sensor_wake(rmi4_data);
+ synaptics_rmi4_irq_enable(rmi4_data, true);
+ retval = synaptics_rmi4_reinit_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to reinit device\n",
+ __func__);
+ }
+ }
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link)
+ if (exp_fhandler->exp_fn->late_resume != NULL)
+ exp_fhandler->exp_fn->late_resume(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ rmi4_data->touch_stopped = false;
+
+ return;
+}
+#endif
+
+ /**
+ * synaptics_rmi4_suspend()
+ *
+ * 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_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+
+ if (rmi4_data->staying_awake)
+ return 0;
+
+ if (!rmi4_data->sensor_sleep) {
+ rmi4_data->touch_stopped = true;
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+ synaptics_rmi4_sensor_sleep(rmi4_data);
+ synaptics_rmi4_free_fingers(rmi4_data);
+ }
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link)
+ if (exp_fhandler->exp_fn->suspend != NULL)
+ exp_fhandler->exp_fn->suspend(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ if (rmi4_data->regulator)
+ regulator_disable(rmi4_data->regulator);
+
+ return 0;
+}
+
+ /**
+ * synaptics_rmi4_resume()
+ *
+ * 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)
+{
+ int retval;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev);
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ if (rmi4_data->staying_awake)
+ return 0;
+
+ if (rmi4_data->regulator) {
+ regulator_enable(rmi4_data->regulator);
+ msleep(bdata->reset_delay_ms);
+ rmi4_data->current_page = MASK_8BIT;
+ }
+
+ synaptics_rmi4_sensor_wake(rmi4_data);
+ synaptics_rmi4_irq_enable(rmi4_data, true);
+ retval = synaptics_rmi4_reinit_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to reinit device\n",
+ __func__);
+ return retval;
+ }
+
+ mutex_lock(&exp_data.mutex);
+ if (!list_empty(&exp_data.list)) {
+ list_for_each_entry(exp_fhandler, &exp_data.list, link)
+ if (exp_fhandler->exp_fn->resume != NULL)
+ exp_fhandler->exp_fn->resume(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+ rmi4_data->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 struct platform_driver synaptics_rmi4_driver = {
+ .driver = {
+ .name = PLATFORM_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),
+};
+
+ /**
+ * synaptics_rmi4_init()
+ *
+ * Called by the kernel during do_initcalls (if built-in)
+ * or when the driver is loaded (if a module).
+ *
+ * This function registers the driver to the I2C subsystem.
+ *
+ */
+static int __init synaptics_rmi4_init(void)
+{
+ int retval;
+
+ retval = synaptics_rmi4_bus_init();
+ if (retval)
+ return retval;
+
+ return platform_driver_register(&synaptics_rmi4_driver);
+}
+
+ /**
+ * synaptics_rmi4_exit()
+ *
+ * Called by the kernel when the driver is unloaded.
+ *
+ * This funtion unregisters the driver from the I2C subsystem.
+ *
+ */
+static void __exit synaptics_rmi4_exit(void)
+{
+ platform_driver_unregister(&synaptics_rmi4_driver);
+
+ synaptics_rmi4_bus_exit();
+
+ return;
+}
+
+module_init(synaptics_rmi4_init);
+module_exit(synaptics_rmi4_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics DSX I2C Touch Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
new file mode 100755
index 000000000000..1692c3191ba6
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_core.h
@@ -0,0 +1,336 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#ifndef _SYNAPTICS_DSX_RMI4_H_
+#define _SYNAPTICS_DSX_RMI4_H_
+
+#define SYNAPTICS_DS4 (1 << 0)
+#define SYNAPTICS_DS5 (1 << 1)
+#define SYNAPTICS_DSX_DRIVER_PRODUCT (SYNAPTICS_DS4 | SYNAPTICS_DS5)
+#define SYNAPTICS_DSX_DRIVER_VERSION 0x2001
+
+#include <linux/version.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38))
+#define KERNEL_ABOVE_2_6_38
+#endif
+
+#ifdef KERNEL_ABOVE_2_6_38
+#define sstrtoul(...) kstrtoul(__VA_ARGS__)
+#else
+#define sstrtoul(...) strict_strtoul(__VA_ARGS__)
+#endif
+
+#define PDT_PROPS (0X00EF)
+#define PDT_START (0x00E9)
+#define PDT_END (0x00D0)
+#define PDT_ENTRY_SIZE (0x0006)
+#define PAGES_TO_SERVICE (10)
+#define PAGE_SELECT_LEN (2)
+#define ADDRESS_WORD_LEN (2)
+
+#define SYNAPTICS_RMI4_F01 (0x01)
+#define SYNAPTICS_RMI4_F11 (0x11)
+#define SYNAPTICS_RMI4_F12 (0x12)
+#define SYNAPTICS_RMI4_F1A (0x1a)
+#define SYNAPTICS_RMI4_F34 (0x34)
+#define SYNAPTICS_RMI4_F51 (0x51)
+#define SYNAPTICS_RMI4_F54 (0x54)
+#define SYNAPTICS_RMI4_F55 (0x55)
+
+#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2
+#define SYNAPTICS_RMI4_DATE_CODE_SIZE 3
+#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10
+#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3
+
+#define F12_FINGERS_TO_SUPPORT 10
+#define F12_NO_OBJECT_STATUS 0x00
+#define F12_FINGER_STATUS 0x01
+#define F12_STYLUS_STATUS 0x02
+#define F12_PALM_STATUS 0x03
+#define F12_HOVERING_FINGER_STATUS 0x05
+#define F12_GLOVED_FINGER_STATUS 0x06
+
+#define MAX_NUMBER_OF_BUTTONS 4
+#define MAX_INTR_REGISTERS 4
+
+#define MASK_16BIT 0xFFFF
+#define MASK_8BIT 0xFF
+#define MASK_7BIT 0x7F
+#define MASK_6BIT 0x3F
+#define MASK_5BIT 0x1F
+#define MASK_4BIT 0x0F
+#define MASK_3BIT 0x07
+#define MASK_2BIT 0x03
+#define MASK_1BIT 0x01
+
+enum exp_fn {
+ RMI_DEV = 0,
+ RMI_F54,
+ RMI_FW_UPDATER,
+ RMI_PROXIMITY,
+ RMI_ACTIVE_PEN,
+ RMI_LAST,
+};
+
+struct synaptics_dsx_hw_interface {
+ const struct synaptics_dsx_board_data *board_data;
+ const struct synaptics_dsx_bus_access *bus_access;
+};
+
+/*
+ * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT
+ * @query_base_addr: base address for query registers
+ * @cmd_base_addr: base address for command registers
+ * @ctrl_base_addr: base address for control registers
+ * @data_base_addr: base address for data registers
+ * @intr_src_count: number of interrupt sources
+ * @fn_number: function number
+ */
+struct synaptics_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;
+};
+
+/*
+ * synaptics_rmi4_fn_full_addr - full 16-bit base addresses
+ * @query_base: 16-bit base address for query registers
+ * @cmd_base: 16-bit base address for data registers
+ * @ctrl_base: 16-bit base address for command registers
+ * @data_base: 16-bit base address for control registers
+ */
+struct synaptics_rmi4_fn_full_addr {
+ unsigned short query_base;
+ unsigned short cmd_base;
+ unsigned short ctrl_base;
+ unsigned short data_base;
+};
+
+struct synaptics_rmi4_f12_extra_data {
+ unsigned char data1_offset;
+ unsigned char data15_offset;
+ unsigned char data15_size;
+ unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8];
+};
+
+/*
+ * struct synaptics_rmi4_fn - function handler data structure
+ * @fn_number: function number
+ * @num_of_data_sources: number of data sources
+ * @num_of_data_points: maximum number of fingers supported
+ * @size_of_data_register_block: data register block size
+ * @intr_reg_num: index to associated interrupt register
+ * @intr_mask: interrupt mask
+ * @full_addr: full 16-bit base addresses of function registers
+ * @link: linked list for function handlers
+ * @data_size: size of private data
+ * @data: pointer to private data
+ */
+struct synaptics_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 intr_reg_num;
+ unsigned char intr_mask;
+ struct synaptics_rmi4_fn_full_addr full_addr;
+ struct list_head link;
+ int data_size;
+ void *data;
+ void *extra;
+};
+
+/*
+ * struct synaptics_rmi4_device_info - device information
+ * @version_major: rmi protocol major version number
+ * @version_minor: rmi protocol minor version number
+ * @manufacturer_id: manufacturer id
+ * @product_props: product properties information
+ * @product_info: product info array
+ * @date_code: device manufacture date
+ * @tester_id: tester id array
+ * @serial_number: device serial number
+ * @product_id_string: device product id
+ * @support_fn_list: linked list for function handlers
+ */
+struct synaptics_rmi4_device_info {
+ unsigned int version_major;
+ unsigned int version_minor;
+ unsigned char manufacturer_id;
+ unsigned char product_props;
+ unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+ unsigned char date_code[SYNAPTICS_RMI4_DATE_CODE_SIZE];
+ unsigned short tester_id;
+ unsigned short serial_number;
+ unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE];
+ struct list_head support_fn_list;
+};
+
+/*
+ * struct synaptics_rmi4_data - rmi4 device instance data
+ * @pdev: pointer to platform device
+ * @input_dev: pointer to associated input device
+ * @hw_if: pointer to hardware interface data
+ * @rmi4_mod_info: device information
+ * @regulator: pointer to associated regulator
+ * @rmi4_io_ctrl_mutex: mutex for i2c i/o control
+ * @early_suspend: instance to support early suspend power management
+ * @current_page: current page in sensor to acess
+ * @button_0d_enabled: flag for 0d button support
+ * @full_pm_cycle: flag for full power management cycle in early suspend stage
+ * @num_of_intr_regs: number of interrupt registers
+ * @f01_query_base_addr: query base address for f01
+ * @f01_cmd_base_addr: command base address for f01
+ * @f01_ctrl_base_addr: control base address for f01
+ * @f01_data_base_addr: data base address for f01
+ * @irq: attention interrupt
+ * @sensor_max_x: sensor maximum x value
+ * @sensor_max_y: sensor maximum y value
+ * @irq_enabled: flag for indicating interrupt enable status
+ * @fingers_on_2d: flag to indicate presence of fingers in 2d area
+ * @sensor_sleep: flag to indicate sleep state of sensor
+ * @wait: wait queue for touch data polling in interrupt thread
+ * @irq_enable: pointer to irq enable function
+ */
+struct synaptics_rmi4_data {
+ struct platform_device *pdev;
+ struct input_dev *input_dev;
+ const struct synaptics_dsx_hw_interface *hw_if;
+ struct synaptics_rmi4_device_info rmi4_mod_info;
+ struct regulator *regulator;
+ struct mutex rmi4_reset_mutex;
+ struct mutex rmi4_io_ctrl_mutex;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ unsigned char current_page;
+ unsigned char button_0d_enabled;
+ unsigned char full_pm_cycle;
+ unsigned char num_of_rx;
+ unsigned char num_of_tx;
+ unsigned char num_of_fingers;
+ unsigned char max_touch_width;
+ unsigned char report_enable;
+ unsigned char no_sleep_setting;
+ unsigned char intr_mask[MAX_INTR_REGISTERS];
+ unsigned char *button_txrx_mapping;
+ unsigned short num_of_intr_regs;
+ unsigned short f01_query_base_addr;
+ unsigned short f01_cmd_base_addr;
+ unsigned short f01_ctrl_base_addr;
+ unsigned short f01_data_base_addr;
+ unsigned int firmware_id;
+ int irq;
+ int sensor_max_x;
+ int sensor_max_y;
+ bool flash_prog_mode;
+ bool irq_enabled;
+ bool touch_stopped;
+ bool fingers_on_2d;
+ bool sensor_sleep;
+ bool stay_awake;
+ bool staying_awake;
+ int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
+ int (*reset_device)(struct synaptics_rmi4_data *rmi4_data);
+};
+
+struct synaptics_dsx_bus_access {
+ unsigned char type;
+ int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr,
+ unsigned char *data, unsigned short length);
+};
+
+struct synaptics_rmi4_exp_fn {
+ enum exp_fn fn_type;
+ int (*init)(struct synaptics_rmi4_data *rmi4_data);
+ void (*remove)(struct synaptics_rmi4_data *rmi4_data);
+ void (*reset)(struct synaptics_rmi4_data *rmi4_data);
+ void (*reinit)(struct synaptics_rmi4_data *rmi4_data);
+ void (*early_suspend)(struct synaptics_rmi4_data *rmi4_data);
+ void (*suspend)(struct synaptics_rmi4_data *rmi4_data);
+ void (*resume)(struct synaptics_rmi4_data *rmi4_data);
+ void (*late_resume)(struct synaptics_rmi4_data *rmi4_data);
+ void (*attn)(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask);
+};
+
+int synaptics_rmi4_bus_init(void);
+
+void synaptics_rmi4_bus_exit(void);
+
+void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module,
+ bool insert);
+
+int synaptics_fw_updater(unsigned char *fw_data);
+
+static inline int synaptics_rmi4_reg_read(
+ struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr,
+ unsigned char *data,
+ unsigned short len)
+{
+ return rmi4_data->hw_if->bus_access->read(rmi4_data, addr, data, len);
+}
+
+static inline int synaptics_rmi4_reg_write(
+ struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr,
+ unsigned char *data,
+ unsigned short len)
+{
+ return rmi4_data->hw_if->bus_access->write(rmi4_data, addr, data, len);
+}
+
+static inline ssize_t synaptics_rmi4_show_error(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ dev_warn(dev, "%s Attempted to read from write-only attribute %s\n",
+ __func__, attr->attr.name);
+ return -EPERM;
+}
+
+static inline ssize_t synaptics_rmi4_store_error(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ dev_warn(dev, "%s Attempted to write to read-only attribute %s\n",
+ __func__, attr->attr.name);
+ return -EPERM;
+}
+
+static inline void batohs(unsigned short *dest, unsigned char *src)
+{
+ *dest = src[1] * 0x100 + src[0];
+}
+
+static inline void hstoba(unsigned char *dest, unsigned short src)
+{
+ dest[0] = src % 0x100;
+ dest[1] = src / 0x100;
+}
+
+#endif
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
new file mode 100755
index 000000000000..268ca250c7ca
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_fw_update.c
@@ -0,0 +1,1850 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_dsx_core.h"
+
+#define FW_IMAGE_NAME "synaptics/startup_fw_update.img"
+#define DO_STARTUP_FW_UPDATE
+#define STARTUP_FW_UPDATE_DELAY_MS 1000 /* ms */
+#define FORCE_UPDATE false
+#define DO_LOCKDOWN false
+
+#define MAX_IMAGE_NAME_LEN 256
+#define MAX_FIRMWARE_ID_LEN 10
+
+#define LOCKDOWN_OFFSET 0xb0
+#define FW_IMAGE_OFFSET 0x100
+
+#define BOOTLOADER_ID_OFFSET 0
+#define BLOCK_NUMBER_OFFSET 0
+
+#define V5_PROPERTIES_OFFSET 2
+#define V5_BLOCK_SIZE_OFFSET 3
+#define V5_BLOCK_COUNT_OFFSET 5
+#define V5_BLOCK_DATA_OFFSET 2
+
+#define V6_PROPERTIES_OFFSET 1
+#define V6_BLOCK_SIZE_OFFSET 2
+#define V6_BLOCK_COUNT_OFFSET 3
+#define V6_BLOCK_DATA_OFFSET 1
+#define V6_FLASH_COMMAND_OFFSET 2
+#define V6_FLASH_STATUS_OFFSET 3
+
+#define LOCKDOWN_BLOCK_COUNT 5
+
+#define REG_MAP (1 << 0)
+#define UNLOCKED (1 << 1)
+#define HAS_CONFIG_ID (1 << 2)
+#define HAS_PERM_CONFIG (1 << 3)
+#define HAS_BL_CONFIG (1 << 4)
+#define HAS_DISP_CONFIG (1 << 5)
+#define HAS_CTRL1 (1 << 6)
+
+#define UI_CONFIG_AREA 0x00
+#define PERM_CONFIG_AREA 0x01
+#define BL_CONFIG_AREA 0x02
+#define DISP_CONFIG_AREA 0x03
+
+#define CMD_WRITE_FW_BLOCK 0x2
+#define CMD_ERASE_ALL 0x3
+#define CMD_WRITE_LOCKDOWN_BLOCK 0x4
+#define CMD_READ_CONFIG_BLOCK 0x5
+#define CMD_WRITE_CONFIG_BLOCK 0x6
+#define CMD_ERASE_CONFIG 0x7
+#define CMD_ERASE_BL_CONFIG 0x9
+#define CMD_ERASE_DISP_CONFIG 0xa
+#define CMD_ENABLE_FLASH_PROG 0xf
+
+#define SLEEP_MODE_NORMAL (0x00)
+#define SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define SLEEP_MODE_RESERVED0 (0x02)
+#define SLEEP_MODE_RESERVED1 (0x03)
+
+#define ENABLE_WAIT_MS (1 * 1000)
+#define WRITE_WAIT_MS (3 * 1000)
+#define ERASE_WAIT_MS (5 * 1000)
+
+#define MIN_SLEEP_TIME_US 50
+#define MAX_SLEEP_TIME_US 100
+
+static ssize_t fwu_sysfs_show_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t fwu_sysfs_store_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_write_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_read_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_config_area_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_image_name_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_image_size_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t fwu_sysfs_block_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+enum bl_version {
+ V5 = 5,
+ V6 = 6,
+};
+
+enum flash_area {
+ NONE,
+ UI_FIRMWARE,
+ CONFIG_AREA,
+};
+
+enum update_mode {
+ NORMAL = 1,
+ FORCE = 2,
+ LOCKDOWN = 8,
+};
+
+struct image_header {
+ /* 0x00 - 0x0f */
+ unsigned char checksum[4];
+ unsigned char reserved_04;
+ unsigned char reserved_05;
+ unsigned char options_firmware_id:1;
+ unsigned char options_contain_bootloader:1;
+ unsigned char options_reserved:6;
+ unsigned char bootloader_version;
+ unsigned char firmware_size[4];
+ unsigned char config_size[4];
+ /* 0x10 - 0x1f */
+ unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE];
+ unsigned char package_id[2];
+ unsigned char package_id_revision[2];
+ unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+ /* 0x20 - 0x2f */
+ unsigned char reserved_20_2f[16];
+ /* 0x30 - 0x3f */
+ unsigned char ds_id[16];
+ /* 0x40 - 0x4f */
+ unsigned char ds_info[10];
+ unsigned char reserved_4a_4f[6];
+ /* 0x50 - 0x53 */
+ unsigned char firmware_id[4];
+};
+
+struct image_header_data {
+ bool contains_firmware_id;
+ unsigned int firmware_id;
+ unsigned int checksum;
+ unsigned int firmware_size;
+ unsigned int config_size;
+ unsigned char bootloader_version;
+ unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE];
+};
+
+struct pdt_properties {
+ union {
+ struct {
+ unsigned char reserved_1:6;
+ unsigned char has_bsr:1;
+ unsigned char reserved_2:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f01_device_status {
+ union {
+ struct {
+ unsigned char status_code:4;
+ unsigned char reserved:2;
+ unsigned char flash_prog:1;
+ unsigned char unconfigured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct f01_device_control {
+ union {
+ struct {
+ unsigned char sleep_mode:2;
+ unsigned char nosleep:1;
+ unsigned char reserved:2;
+ unsigned char charger_connected:1;
+ unsigned char report_rate:1;
+ unsigned char configured:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_fwu_handle {
+ enum bl_version bl_version;
+ bool initialized;
+ bool program_enabled;
+ bool has_perm_config;
+ bool has_bl_config;
+ bool has_disp_config;
+ bool force_update;
+ bool in_flash_prog_mode;
+ bool do_lockdown;
+ unsigned int data_pos;
+ unsigned int image_size;
+ unsigned char *image_name;
+ unsigned char *ext_data_source;
+ unsigned char *read_config_buf;
+ unsigned char intr_mask;
+ unsigned char command;
+ unsigned char bootloader_id[2];
+ unsigned char flash_properties;
+ unsigned char flash_status;
+ unsigned char productinfo1;
+ unsigned char productinfo2;
+ unsigned char properties_off;
+ unsigned char blk_size_off;
+ unsigned char blk_count_off;
+ unsigned char blk_data_off;
+ unsigned char flash_cmd_off;
+ unsigned char flash_status_off;
+ unsigned short block_size;
+ unsigned short fw_block_count;
+ unsigned short config_block_count;
+ unsigned short lockdown_block_count;
+ unsigned short perm_config_block_count;
+ unsigned short bl_config_block_count;
+ unsigned short disp_config_block_count;
+ unsigned short config_size;
+ unsigned short config_area;
+ char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1];
+ const unsigned char *firmware_data;
+ const unsigned char *config_data;
+ const unsigned char *lockdown_data;
+ struct workqueue_struct *fwu_workqueue;
+ struct delayed_work fwu_work;
+ struct synaptics_rmi4_fn_desc f34_fd;
+ struct synaptics_rmi4_data *rmi4_data;
+};
+
+static struct bin_attribute dev_attr_data = {
+ .attr = {
+ .name = "data",
+ .mode = (S_IRUGO | S_IWUGO),
+ },
+ .size = 0,
+ .read = fwu_sysfs_show_image,
+ .write = fwu_sysfs_store_image,
+};
+
+static struct device_attribute attrs[] = {
+ __ATTR(doreflash, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_do_reflash_store),
+ __ATTR(writeconfig, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_write_config_store),
+ __ATTR(readconfig, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_read_config_store),
+ __ATTR(configarea, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_config_area_store),
+ __ATTR(imagename, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_image_name_store),
+ __ATTR(imagesize, S_IWUGO,
+ synaptics_rmi4_show_error,
+ fwu_sysfs_image_size_store),
+ __ATTR(blocksize, S_IRUGO,
+ fwu_sysfs_block_size_show,
+ synaptics_rmi4_store_error),
+ __ATTR(fwblockcount, S_IRUGO,
+ fwu_sysfs_firmware_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(configblockcount, S_IRUGO,
+ fwu_sysfs_configuration_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(permconfigblockcount, S_IRUGO,
+ fwu_sysfs_perm_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(blconfigblockcount, S_IRUGO,
+ fwu_sysfs_bl_config_block_count_show,
+ synaptics_rmi4_store_error),
+ __ATTR(dispconfigblockcount, S_IRUGO,
+ fwu_sysfs_disp_config_block_count_show,
+ synaptics_rmi4_store_error),
+};
+
+static struct synaptics_rmi4_fwu_handle *fwu;
+
+DECLARE_COMPLETION(fwu_remove_complete);
+
+static unsigned int extract_uint_le(const unsigned char *ptr)
+{
+ return (unsigned int)ptr[0] +
+ (unsigned int)ptr[1] * 0x100 +
+ (unsigned int)ptr[2] * 0x10000 +
+ (unsigned int)ptr[3] * 0x1000000;
+}
+
+static unsigned int extract_uint_be(const unsigned char *ptr)
+{
+ return (unsigned int)ptr[3] +
+ (unsigned int)ptr[2] * 0x100 +
+ (unsigned int)ptr[1] * 0x10000 +
+ (unsigned int)ptr[0] * 0x1000000;
+}
+
+static void parse_header(struct image_header_data *header,
+ const unsigned char *fw_image)
+{
+ struct image_header *data = (struct image_header *)fw_image;
+
+ header->checksum = extract_uint_le(data->checksum);
+
+ header->bootloader_version = data->bootloader_version;
+
+ header->firmware_size = extract_uint_le(data->firmware_size);
+
+ header->config_size = extract_uint_le(data->config_size);
+
+ memcpy(header->product_id, data->product_id, sizeof(data->product_id));
+ header->product_id[sizeof(data->product_id)] = 0;
+
+ memcpy(header->product_info, data->product_info,
+ sizeof(data->product_info));
+
+ header->contains_firmware_id = data->options_firmware_id;
+ if (header->contains_firmware_id)
+ header->firmware_id = extract_uint_le(data->firmware_id);
+
+ return;
+}
+
+static int fwu_read_f01_device_status(struct f01_device_status *status)
+{
+ int retval;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ status->data,
+ sizeof(status->data));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read F01 device status\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int fwu_read_f34_queries(void)
+{
+ int retval;
+ unsigned char count;
+ unsigned char buf[10];
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.query_base_addr + BOOTLOADER_ID_OFFSET,
+ fwu->bootloader_id,
+ sizeof(fwu->bootloader_id));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read bootloader ID\n",
+ __func__);
+ return retval;
+ }
+
+ if (fwu->bootloader_id[1] == '5') {
+ fwu->bl_version = V5;
+ } else if (fwu->bootloader_id[1] == '6') {
+ fwu->bl_version = V6;
+ } else {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Unrecognized bootloader version\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (fwu->bl_version == V5) {
+ fwu->properties_off = V5_PROPERTIES_OFFSET;
+ fwu->blk_size_off = V5_BLOCK_SIZE_OFFSET;
+ fwu->blk_count_off = V5_BLOCK_COUNT_OFFSET;
+ fwu->blk_data_off = V5_BLOCK_DATA_OFFSET;
+ } else if (fwu->bl_version == V6) {
+ fwu->properties_off = V6_PROPERTIES_OFFSET;
+ fwu->blk_size_off = V6_BLOCK_SIZE_OFFSET;
+ fwu->blk_count_off = V6_BLOCK_COUNT_OFFSET;
+ fwu->blk_data_off = V6_BLOCK_DATA_OFFSET;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.query_base_addr + fwu->properties_off,
+ &fwu->flash_properties,
+ sizeof(fwu->flash_properties));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read flash properties\n",
+ __func__);
+ return retval;
+ }
+
+ count = 4;
+
+ if (fwu->flash_properties & HAS_PERM_CONFIG) {
+ fwu->has_perm_config = 1;
+ count += 2;
+ }
+
+ if (fwu->flash_properties & HAS_BL_CONFIG) {
+ fwu->has_bl_config = 1;
+ count += 2;
+ }
+
+ if (fwu->flash_properties & HAS_DISP_CONFIG) {
+ fwu->has_disp_config = 1;
+ count += 2;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.query_base_addr + fwu->blk_size_off,
+ buf,
+ 2);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read block size info\n",
+ __func__);
+ return retval;
+ }
+
+ batohs(&fwu->block_size, &(buf[0]));
+
+ if (fwu->bl_version == V5) {
+ fwu->flash_cmd_off = fwu->blk_data_off + fwu->block_size;
+ fwu->flash_status_off = fwu->flash_cmd_off;
+ } else if (fwu->bl_version == V6) {
+ fwu->flash_cmd_off = V6_FLASH_COMMAND_OFFSET;
+ fwu->flash_status_off = V6_FLASH_STATUS_OFFSET;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.query_base_addr + fwu->blk_count_off,
+ buf,
+ count);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read block count info\n",
+ __func__);
+ return retval;
+ }
+
+ batohs(&fwu->fw_block_count, &(buf[0]));
+ batohs(&fwu->config_block_count, &(buf[2]));
+
+ count = 4;
+
+ if (fwu->has_perm_config) {
+ batohs(&fwu->perm_config_block_count, &(buf[count]));
+ count += 2;
+ }
+
+ if (fwu->has_bl_config) {
+ batohs(&fwu->bl_config_block_count, &(buf[count]));
+ count += 2;
+ }
+
+ if (fwu->has_disp_config)
+ batohs(&fwu->disp_config_block_count, &(buf[count]));
+
+ return 0;
+}
+
+static int fwu_read_f34_flash_status(void)
+{
+ int retval;
+ unsigned char status;
+ unsigned char command;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.data_base_addr + fwu->flash_status_off,
+ &status,
+ sizeof(status));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read flash status\n",
+ __func__);
+ return retval;
+ }
+
+ fwu->program_enabled = status >> 7;
+
+ if (fwu->bl_version == V5)
+ fwu->flash_status = (status >> 4) & MASK_3BIT;
+ else if (fwu->bl_version == V6)
+ fwu->flash_status = status & MASK_3BIT;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.data_base_addr + fwu->flash_cmd_off,
+ &command,
+ sizeof(command));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read flash command\n",
+ __func__);
+ return retval;
+ }
+
+ fwu->command = command & MASK_4BIT;
+
+ return 0;
+}
+
+static int fwu_write_f34_command(unsigned char cmd)
+{
+ int retval;
+ unsigned char command = cmd & MASK_4BIT;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ fwu->command = cmd;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ fwu->f34_fd.data_base_addr + fwu->flash_cmd_off,
+ &command,
+ sizeof(command));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write command 0x%02x\n",
+ __func__, command);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int fwu_wait_for_idle(int timeout_ms)
+{
+ int count = 0;
+ int timeout_count = ((timeout_ms * 1000) / MAX_SLEEP_TIME_US) + 1;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ do {
+ usleep_range(MIN_SLEEP_TIME_US, MAX_SLEEP_TIME_US);
+
+ count++;
+ if (count == timeout_count)
+ fwu_read_f34_flash_status();
+
+ if ((fwu->command == 0x00) && (fwu->flash_status == 0x00))
+ return 0;
+ } while (count < timeout_count);
+
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Timed out waiting for idle status\n",
+ __func__);
+
+ return -ETIMEDOUT;
+}
+
+static enum flash_area fwu_go_nogo(struct image_header_data *header)
+{
+ int retval;
+ enum flash_area flash_area = NONE;
+ unsigned char index = 0;
+ unsigned char config_id[4];
+ unsigned int device_config_id;
+ unsigned int image_config_id;
+ unsigned int device_fw_id;
+ unsigned long image_fw_id;
+ char *strptr;
+ char *firmware_id;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (fwu->force_update) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+ /* Update both UI and config if device is in bootloader mode */
+ if (fwu->in_flash_prog_mode) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ }
+
+ /* Get device firmware ID */
+ device_fw_id = rmi4_data->firmware_id;
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Device firmware ID = %d\n",
+ __func__, device_fw_id);
+
+ /* Get image firmware ID */
+ if (header->contains_firmware_id) {
+ image_fw_id = header->firmware_id;
+ } else {
+ strptr = strstr(fwu->image_name, "PR");
+ if (!strptr) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: No valid PR number (PRxxxxxxx) "
+ "found in image file name (%s)\n",
+ __func__, fwu->image_name);
+ flash_area = NONE;
+ goto exit;
+ }
+
+ strptr += 2;
+ firmware_id = kzalloc(MAX_FIRMWARE_ID_LEN, GFP_KERNEL);
+ while (strptr[index] >= '0' && strptr[index] <= '9') {
+ firmware_id[index] = strptr[index];
+ index++;
+ }
+
+ retval = sstrtoul(firmware_id, 10, &image_fw_id);
+ kfree(firmware_id);
+ if (retval) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to obtain image firmware ID\n",
+ __func__);
+ flash_area = NONE;
+ goto exit;
+ }
+ }
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Image firmware ID = %d\n",
+ __func__, (unsigned int)image_fw_id);
+
+ if (image_fw_id > device_fw_id) {
+ flash_area = UI_FIRMWARE;
+ goto exit;
+ } else if (image_fw_id < device_fw_id) {
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Image firmware ID older than device firmware ID\n",
+ __func__);
+ flash_area = NONE;
+ goto exit;
+ }
+
+ /* Get device config ID */
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.ctrl_base_addr,
+ config_id,
+ sizeof(config_id));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read device config ID\n",
+ __func__);
+ flash_area = NONE;
+ goto exit;
+ }
+ device_config_id = extract_uint_be(config_id);
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Device config ID = 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ __func__,
+ config_id[0],
+ config_id[1],
+ config_id[2],
+ config_id[3]);
+
+ /* Get image config ID */
+ image_config_id = extract_uint_be(fwu->config_data);
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Image config ID = 0x%02x 0x%02x 0x%02x 0x%02x\n",
+ __func__,
+ fwu->config_data[0],
+ fwu->config_data[1],
+ fwu->config_data[2],
+ fwu->config_data[3]);
+
+ if (image_config_id > device_config_id) {
+ flash_area = CONFIG_AREA;
+ goto exit;
+ }
+
+ flash_area = NONE;
+
+exit:
+ if (flash_area == NONE) {
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: No need to do reflash\n",
+ __func__);
+ } else {
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Updating %s\n",
+ __func__,
+ flash_area == UI_FIRMWARE ?
+ "UI firmware" :
+ "config only");
+ }
+
+ return flash_area;
+}
+
+static int fwu_scan_pdt(void)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char intr_count = 0;
+ unsigned char intr_off;
+ unsigned char intr_src;
+ unsigned short addr;
+ bool f01found = false;
+ bool f34found = false;
+ struct synaptics_rmi4_fn_desc rmi_fd;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ addr,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval < 0)
+ return retval;
+
+ if (rmi_fd.fn_number) {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Found F%02x\n",
+ __func__, rmi_fd.fn_number);
+ switch (rmi_fd.fn_number) {
+ case SYNAPTICS_RMI4_F01:
+ f01found = true;
+
+ rmi4_data->f01_query_base_addr =
+ rmi_fd.query_base_addr;
+ rmi4_data->f01_ctrl_base_addr =
+ rmi_fd.ctrl_base_addr;
+ rmi4_data->f01_data_base_addr =
+ rmi_fd.data_base_addr;
+ rmi4_data->f01_cmd_base_addr =
+ rmi_fd.cmd_base_addr;
+ break;
+ case SYNAPTICS_RMI4_F34:
+ f34found = true;
+ fwu->f34_fd.query_base_addr =
+ rmi_fd.query_base_addr;
+ fwu->f34_fd.ctrl_base_addr =
+ rmi_fd.ctrl_base_addr;
+ fwu->f34_fd.data_base_addr =
+ rmi_fd.data_base_addr;
+
+ fwu->intr_mask = 0;
+ intr_src = rmi_fd.intr_src_count;
+ intr_off = intr_count % 8;
+ for (ii = intr_off;
+ ii < ((intr_src & MASK_3BIT) +
+ intr_off);
+ ii++) {
+ fwu->intr_mask |= 1 << ii;
+ }
+ break;
+ }
+ } else {
+ break;
+ }
+
+ intr_count += (rmi_fd.intr_src_count & MASK_3BIT);
+ }
+
+ if (!f01found || !f34found) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to find both F01 and F34\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fwu_write_blocks(unsigned char *block_ptr, unsigned short block_cnt,
+ unsigned char command)
+{
+ int retval;
+ unsigned char block_offset[] = {0, 0};
+ unsigned short block_num;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ block_offset[1] |= (fwu->config_area << 5);
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
+ block_offset,
+ sizeof(block_offset));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write to block number registers\n",
+ __func__);
+ return retval;
+ }
+
+ for (block_num = 0; block_num < block_cnt; block_num++) {
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ fwu->f34_fd.data_base_addr + fwu->blk_data_off,
+ block_ptr,
+ fwu->block_size);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write block data (block %d)\n",
+ __func__, block_num);
+ return retval;
+ }
+
+ retval = fwu_write_f34_command(command);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write command for block %d\n",
+ __func__, block_num);
+ return retval;
+ }
+
+ retval = fwu_wait_for_idle(WRITE_WAIT_MS);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to wait for idle status (block %d)\n",
+ __func__, block_num);
+ return retval;
+ }
+
+ block_ptr += fwu->block_size;
+ }
+
+ return 0;
+}
+
+static int fwu_write_firmware(void)
+{
+ return fwu_write_blocks((unsigned char *)fwu->firmware_data,
+ fwu->fw_block_count, CMD_WRITE_FW_BLOCK);
+}
+
+static int fwu_write_configuration(void)
+{
+ return fwu_write_blocks((unsigned char *)fwu->config_data,
+ fwu->config_block_count, CMD_WRITE_CONFIG_BLOCK);
+}
+
+static int fwu_write_lockdown(void)
+{
+ return fwu_write_blocks((unsigned char *)fwu->lockdown_data,
+ fwu->lockdown_block_count, CMD_WRITE_LOCKDOWN_BLOCK);
+}
+
+static int fwu_write_bootloader_id(void)
+{
+ int retval;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ fwu->f34_fd.data_base_addr + fwu->blk_data_off,
+ fwu->bootloader_id,
+ sizeof(fwu->bootloader_id));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write bootloader ID\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static int fwu_enter_flash_prog(void)
+{
+ int retval;
+ struct f01_device_status f01_device_status;
+ struct f01_device_control f01_device_control;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = fwu_write_bootloader_id();
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_write_f34_command(CMD_ENABLE_FLASH_PROG);
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_wait_for_idle(ENABLE_WAIT_MS);
+ if (retval < 0)
+ return retval;
+
+ if (!fwu->program_enabled) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Program enabled bit not set\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ retval = fwu_scan_pdt();
+ if (retval < 0)
+ return retval;
+
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ return retval;
+
+ if (!f01_device_status.flash_prog) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Not in flash prog mode\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ retval = fwu_read_f34_queries();
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ f01_device_control.data,
+ sizeof(f01_device_control.data));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read F01 device control\n",
+ __func__);
+ return retval;
+ }
+
+ f01_device_control.nosleep = true;
+ f01_device_control.sleep_mode = SLEEP_MODE_NORMAL;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ f01_device_control.data,
+ sizeof(f01_device_control.data));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write F01 device control\n",
+ __func__);
+ return retval;
+ }
+
+ return retval;
+}
+
+static int fwu_do_reflash(void)
+{
+ int retval;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Entered flash prog mode\n",
+ __func__);
+
+ retval = fwu_write_bootloader_id();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Bootloader ID written\n",
+ __func__);
+
+ retval = fwu_write_f34_command(CMD_ERASE_ALL);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Erase all command written\n",
+ __func__);
+
+ retval = fwu_wait_for_idle(ERASE_WAIT_MS);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Idle status detected\n",
+ __func__);
+
+ if (fwu->firmware_data) {
+ retval = fwu_write_firmware();
+ if (retval < 0)
+ return retval;
+ pr_notice("%s: Firmware programmed\n", __func__);
+ }
+
+ if (fwu->config_data) {
+ retval = fwu_write_configuration();
+ if (retval < 0)
+ return retval;
+ pr_notice("%s: Configuration programmed\n", __func__);
+ }
+
+ return retval;
+}
+
+static int fwu_do_write_config(void)
+{
+ int retval;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Entered flash prog mode\n",
+ __func__);
+
+ if (fwu->config_area == PERM_CONFIG_AREA) {
+ fwu->config_block_count = fwu->perm_config_block_count;
+ goto write_config;
+ }
+
+ retval = fwu_write_bootloader_id();
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Bootloader ID written\n",
+ __func__);
+
+ switch (fwu->config_area) {
+ case UI_CONFIG_AREA:
+ retval = fwu_write_f34_command(CMD_ERASE_CONFIG);
+ break;
+ case BL_CONFIG_AREA:
+ retval = fwu_write_f34_command(CMD_ERASE_BL_CONFIG);
+ fwu->config_block_count = fwu->bl_config_block_count;
+ break;
+ case DISP_CONFIG_AREA:
+ retval = fwu_write_f34_command(CMD_ERASE_DISP_CONFIG);
+ fwu->config_block_count = fwu->disp_config_block_count;
+ break;
+ }
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Erase command written\n",
+ __func__);
+
+ retval = fwu_wait_for_idle(ERASE_WAIT_MS);
+ if (retval < 0)
+ return retval;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Idle status detected\n",
+ __func__);
+
+write_config:
+ retval = fwu_write_configuration();
+ if (retval < 0)
+ return retval;
+
+ pr_notice("%s: Config written\n", __func__);
+
+ return retval;
+}
+
+static int fwu_start_write_config(void)
+{
+ int retval;
+ unsigned short block_count;
+ struct image_header_data header;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ switch (fwu->config_area) {
+ case UI_CONFIG_AREA:
+ block_count = fwu->config_block_count;
+ break;
+ case PERM_CONFIG_AREA:
+ if (!fwu->has_perm_config)
+ return -EINVAL;
+ block_count = fwu->perm_config_block_count;
+ break;
+ case BL_CONFIG_AREA:
+ if (!fwu->has_bl_config)
+ return -EINVAL;
+ block_count = fwu->bl_config_block_count;
+ break;
+ case DISP_CONFIG_AREA:
+ if (!fwu->has_disp_config)
+ return -EINVAL;
+ block_count = fwu->disp_config_block_count;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (fwu->ext_data_source)
+ fwu->config_data = fwu->ext_data_source;
+ else
+ return -EINVAL;
+
+ fwu->config_size = fwu->block_size * block_count;
+
+ /* Jump to the config area if given a packrat image */
+ if ((fwu->config_area == UI_CONFIG_AREA) &&
+ (fwu->config_size != fwu->image_size)) {
+ parse_header(&header, fwu->ext_data_source);
+
+ if (header.config_size) {
+ fwu->config_data = fwu->ext_data_source +
+ FW_IMAGE_OFFSET +
+ header.firmware_size;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ pr_notice("%s: Start of write config process\n", __func__);
+
+ retval = fwu_do_write_config();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write config\n",
+ __func__);
+ }
+
+ rmi4_data->reset_device(rmi4_data);
+
+ pr_notice("%s: End of write config process\n", __func__);
+
+ return retval;
+}
+
+static int fwu_do_read_config(void)
+{
+ int retval;
+ unsigned char block_offset[] = {0, 0};
+ unsigned short block_num;
+ unsigned short block_count;
+ unsigned short index = 0;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ goto exit;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Entered flash prog mode\n",
+ __func__);
+
+ switch (fwu->config_area) {
+ case UI_CONFIG_AREA:
+ block_count = fwu->config_block_count;
+ break;
+ case PERM_CONFIG_AREA:
+ if (!fwu->has_perm_config) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ block_count = fwu->perm_config_block_count;
+ break;
+ case BL_CONFIG_AREA:
+ if (!fwu->has_bl_config) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ block_count = fwu->bl_config_block_count;
+ break;
+ case DISP_CONFIG_AREA:
+ if (!fwu->has_disp_config) {
+ retval = -EINVAL;
+ goto exit;
+ }
+ block_count = fwu->disp_config_block_count;
+ break;
+ default:
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ fwu->config_size = fwu->block_size * block_count;
+
+ kfree(fwu->read_config_buf);
+ fwu->read_config_buf = kzalloc(fwu->config_size, GFP_KERNEL);
+
+ block_offset[1] |= (fwu->config_area << 5);
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ fwu->f34_fd.data_base_addr + BLOCK_NUMBER_OFFSET,
+ block_offset,
+ sizeof(block_offset));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write to block number registers\n",
+ __func__);
+ goto exit;
+ }
+
+ for (block_num = 0; block_num < block_count; block_num++) {
+ retval = fwu_write_f34_command(CMD_READ_CONFIG_BLOCK);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write read config command\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = fwu_wait_for_idle(WRITE_WAIT_MS);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to wait for idle status\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.data_base_addr + fwu->blk_data_off,
+ &fwu->read_config_buf[index],
+ fwu->block_size);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read block data (block %d)\n",
+ __func__, block_num);
+ goto exit;
+ }
+
+ index += fwu->block_size;
+ }
+
+exit:
+ rmi4_data->reset_device(rmi4_data);
+
+ return retval;
+}
+
+static int fwu_do_lockdown(void)
+{
+ int retval;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = fwu_enter_flash_prog();
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ fwu->f34_fd.query_base_addr + fwu->properties_off,
+ &fwu->flash_properties,
+ sizeof(fwu->flash_properties));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read flash properties\n",
+ __func__);
+ return retval;
+ }
+
+ if ((fwu->flash_properties & UNLOCKED) == 0) {
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: Device already locked down\n",
+ __func__);
+ return retval;
+ }
+
+ retval = fwu_write_lockdown();
+ if (retval < 0)
+ return retval;
+
+ pr_notice("%s: Lockdown programmed\n", __func__);
+
+ return retval;
+}
+
+static int fwu_start_reflash(void)
+{
+ int retval = 0;
+ enum flash_area flash_area;
+ struct image_header_data header;
+ struct f01_device_status f01_device_status;
+ const unsigned char *fw_image;
+ const struct firmware *fw_entry = NULL;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (rmi4_data->sensor_sleep) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Sensor sleeping\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ rmi4_data->stay_awake = true;
+
+ pr_notice("%s: Start of reflash process\n", __func__);
+
+ if (fwu->ext_data_source) {
+ fw_image = fwu->ext_data_source;
+ } else {
+ strncpy(fwu->image_name, FW_IMAGE_NAME, MAX_IMAGE_NAME_LEN);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Requesting firmware image %s\n",
+ __func__, fwu->image_name);
+
+ retval = request_firmware(&fw_entry, fwu->image_name,
+ rmi4_data->pdev->dev.parent);
+ if (retval != 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Firmware image %s not available\n",
+ __func__, fwu->image_name);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Firmware image size = %d\n",
+ __func__, fw_entry->size);
+
+ fw_image = fw_entry->data;
+ }
+
+ parse_header(&header, fw_image);
+
+ if (fwu->bl_version != header.bootloader_version) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Bootloader version mismatch\n",
+ __func__);
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ retval = fwu_read_f01_device_status(&f01_device_status);
+ if (retval < 0)
+ goto exit;
+
+ if (f01_device_status.flash_prog) {
+ dev_info(rmi4_data->pdev->dev.parent,
+ "%s: In flash prog mode\n",
+ __func__);
+ fwu->in_flash_prog_mode = true;
+ } else {
+ fwu->in_flash_prog_mode = false;
+ }
+
+ if (fwu->do_lockdown) {
+ switch (fwu->bl_version) {
+ case V5:
+ case V6:
+ fwu->lockdown_data = fw_image + LOCKDOWN_OFFSET;
+ fwu->lockdown_block_count = LOCKDOWN_BLOCK_COUNT;
+ retval = fwu_do_lockdown();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to do lockdown\n",
+ __func__);
+ }
+ default:
+ break;
+ }
+ }
+
+ if (header.firmware_size)
+ fwu->firmware_data = fw_image + FW_IMAGE_OFFSET;
+ if (header.config_size) {
+ fwu->config_data = fw_image + FW_IMAGE_OFFSET +
+ header.firmware_size;
+ }
+
+ flash_area = fwu_go_nogo(&header);
+ switch (flash_area) {
+ case UI_FIRMWARE:
+ retval = fwu_do_reflash();
+ break;
+ case CONFIG_AREA:
+ retval = fwu_do_write_config();
+ break;
+ case NONE:
+ default:
+ goto exit;
+ }
+
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to do reflash\n",
+ __func__);
+ }
+
+exit:
+ rmi4_data->reset_device(rmi4_data);
+
+ if (fw_entry)
+ release_firmware(fw_entry);
+
+ pr_notice("%s: End of reflash process\n", __func__);
+
+ rmi4_data->stay_awake = false;
+
+ return retval;
+}
+
+int synaptics_fw_updater(unsigned char *fw_data)
+{
+ int retval;
+
+ if (!fwu)
+ return -ENODEV;
+
+ if (!fwu->initialized)
+ return -ENODEV;
+
+ fwu->ext_data_source = fw_data;
+ fwu->config_area = UI_CONFIG_AREA;
+
+ retval = fwu_start_reflash();
+
+ return retval;
+}
+EXPORT_SYMBOL(synaptics_fw_updater);
+
+static void fwu_startup_fw_update_work(struct work_struct *work)
+{
+ synaptics_fw_updater(NULL);
+
+ return;
+}
+
+static ssize_t fwu_sysfs_show_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (count < fwu->config_size) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Not enough space (%d bytes) in buffer\n",
+ __func__, count);
+ return -EINVAL;
+ }
+
+ memcpy(buf, fwu->read_config_buf, fwu->config_size);
+
+ return fwu->config_size;
+}
+
+static ssize_t fwu_sysfs_store_image(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ memcpy((void *)(&fwu->ext_data_source[fwu->data_pos]),
+ (const void *)buf,
+ count);
+
+ fwu->data_pos += count;
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_do_reflash_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input & LOCKDOWN) {
+ fwu->do_lockdown = true;
+ input &= ~LOCKDOWN;
+ }
+
+ if ((input != NORMAL) && (input != FORCE)) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input == FORCE)
+ fwu->force_update = true;
+
+ retval = synaptics_fw_updater(fwu->ext_data_source);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to do reflash\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = count;
+
+exit:
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = NULL;
+ fwu->force_update = FORCE_UPDATE;
+ fwu->do_lockdown = DO_LOCKDOWN;
+ return retval;
+}
+
+static ssize_t fwu_sysfs_write_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ if (input != 1) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ retval = fwu_start_write_config();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write config\n",
+ __func__);
+ goto exit;
+ }
+
+ retval = count;
+
+exit:
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = NULL;
+ return retval;
+}
+
+static ssize_t fwu_sysfs_read_config_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input != 1)
+ return -EINVAL;
+
+ retval = fwu_do_read_config();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read config\n",
+ __func__);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_config_area_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned long config_area;
+
+ retval = sstrtoul(buf, 10, &config_area);
+ if (retval)
+ return retval;
+
+ fwu->config_area = config_area;
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_image_name_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ memcpy(fwu->image_name, buf, count);
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_image_size_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned long size;
+ struct synaptics_rmi4_data *rmi4_data = fwu->rmi4_data;
+
+ retval = sstrtoul(buf, 10, &size);
+ if (retval)
+ return retval;
+
+ fwu->image_size = size;
+ fwu->data_pos = 0;
+
+ kfree(fwu->ext_data_source);
+ fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL);
+ if (!fwu->ext_data_source) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for image data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ return count;
+}
+
+static ssize_t fwu_sysfs_block_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size);
+}
+
+static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->fw_block_count);
+}
+
+static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->config_block_count);
+}
+
+static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->perm_config_block_count);
+}
+
+static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->bl_config_block_count);
+}
+
+static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%u\n", fwu->disp_config_block_count);
+}
+
+static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask)
+{
+ if (!fwu)
+ return;
+
+ if (fwu->intr_mask & intr_mask)
+ fwu_read_f34_flash_status();
+
+ return;
+}
+
+static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char attr_count;
+ struct pdt_properties pdt_props;
+
+ fwu = kzalloc(sizeof(*fwu), GFP_KERNEL);
+ if (!fwu) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for fwu\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL);
+ if (!fwu->image_name) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for image name\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit_free_fwu;
+ }
+
+ fwu->rmi4_data = rmi4_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ PDT_PROPS,
+ pdt_props.data,
+ sizeof(pdt_props.data));
+ if (retval < 0) {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read PDT properties, assuming 0x00\n",
+ __func__);
+ } else if (pdt_props.has_bsr) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Reflash for LTS not currently supported\n",
+ __func__);
+ retval = -ENODEV;
+ goto exit_free_mem;
+ }
+
+ retval = fwu_scan_pdt();
+ if (retval < 0)
+ goto exit_free_mem;
+
+ fwu->productinfo1 = rmi4_data->rmi4_mod_info.product_info[0];
+ fwu->productinfo2 = rmi4_data->rmi4_mod_info.product_info[1];
+ memcpy(fwu->product_id, rmi4_data->rmi4_mod_info.product_id_string,
+ SYNAPTICS_RMI4_PRODUCT_ID_SIZE);
+ fwu->product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0;
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: F01 product info: 0x%04x 0x%04x\n",
+ __func__, fwu->productinfo1, fwu->productinfo2);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: F01 product ID: %s\n",
+ __func__, fwu->product_id);
+
+ retval = fwu_read_f34_queries();
+ if (retval < 0)
+ goto exit_free_mem;
+
+ fwu->force_update = FORCE_UPDATE;
+ fwu->do_lockdown = DO_LOCKDOWN;
+ fwu->initialized = true;
+
+ retval = sysfs_create_bin_file(&rmi4_data->input_dev->dev.kobj,
+ &dev_attr_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create sysfs bin file\n",
+ __func__);
+ goto exit_free_mem;
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ retval = -ENODEV;
+ goto exit_remove_attrs;
+ }
+ }
+
+#ifdef DO_STARTUP_FW_UPDATE
+ fwu->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue");
+ INIT_DELAYED_WORK(&fwu->fwu_work, fwu_startup_fw_update_work);
+ queue_delayed_work(fwu->fwu_workqueue,
+ &fwu->fwu_work,
+ msecs_to_jiffies(STARTUP_FW_UPDATE_DELAY_MS));
+#endif
+
+ return 0;
+
+exit_remove_attrs:
+ for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+
+exit_free_mem:
+ kfree(fwu->image_name);
+
+exit_free_fwu:
+ kfree(fwu);
+ fwu = NULL;
+
+exit:
+ return retval;
+}
+
+static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char attr_count;
+
+ if (!fwu)
+ goto exit;
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ sysfs_remove_bin_file(&rmi4_data->input_dev->dev.kobj, &dev_attr_data);
+
+ kfree(fwu->read_config_buf);
+ kfree(fwu->image_name);
+ kfree(fwu);
+ fwu = NULL;
+
+exit:
+ complete(&fwu_remove_complete);
+
+ return;
+}
+
+static struct synaptics_rmi4_exp_fn fwu_module = {
+ .fn_type = RMI_FW_UPDATER,
+ .init = synaptics_rmi4_fwu_init,
+ .remove = synaptics_rmi4_fwu_remove,
+ .reset = NULL,
+ .reinit = NULL,
+ .early_suspend = NULL,
+ .suspend = NULL,
+ .resume = NULL,
+ .late_resume = NULL,
+ .attn = synaptics_rmi4_fwu_attn,
+};
+
+static int __init rmi4_fw_update_module_init(void)
+{
+ synaptics_rmi4_new_function(&fwu_module, true);
+
+ return 0;
+}
+
+static void __exit rmi4_fw_update_module_exit(void)
+{
+ synaptics_rmi4_new_function(&fwu_module, false);
+
+ wait_for_completion(&fwu_remove_complete);
+
+ return;
+}
+
+module_init(rmi4_fw_update_module_init);
+module_exit(rmi4_fw_update_module_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics DSX FW Update Module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
new file mode 100755
index 000000000000..8e7abec6318e
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
@@ -0,0 +1,271 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_dsx_core.h"
+
+#define SYN_I2C_RETRY_TIMES 10
+
+static int synaptics_rmi4_i2c_set_page(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr)
+{
+ int retval;
+ unsigned char retry;
+ unsigned char buf[PAGE_SELECT_LEN];
+ unsigned char page;
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+
+ page = ((addr >> 8) & MASK_8BIT);
+ if (page != rmi4_data->current_page) {
+ buf[0] = MASK_8BIT;
+ buf[1] = page;
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN);
+ if (retval != PAGE_SELECT_LEN) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+ } else {
+ rmi4_data->current_page = page;
+ break;
+ }
+ }
+ } else {
+ retval = PAGE_SELECT_LEN;
+ }
+
+ return retval;
+}
+
+static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval;
+ unsigned char retry;
+ unsigned char buf;
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2c->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &buf,
+ },
+ {
+ .addr = i2c->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = data,
+ },
+ };
+
+ buf = addr & MASK_8BIT;
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ retval = -EIO;
+ goto exit;
+ }
+
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(i2c->adapter, msg, 2) == 2) {
+ retval = length;
+ break;
+ }
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+ }
+
+ if (retry == SYN_I2C_RETRY_TIMES) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: I2C read over retry limit\n",
+ __func__);
+ retval = -EIO;
+ }
+
+exit:
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ return retval;
+}
+
+static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval;
+ unsigned char retry;
+ unsigned char buf[length + 1];
+ struct i2c_client *i2c = to_i2c_client(rmi4_data->pdev->dev.parent);
+ struct i2c_msg msg[] = {
+ {
+ .addr = i2c->addr,
+ .flags = 0,
+ .len = length + 1,
+ .buf = buf,
+ }
+ };
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ retval = synaptics_rmi4_i2c_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ retval = -EIO;
+ goto exit;
+ }
+
+ buf[0] = addr & MASK_8BIT;
+ memcpy(&buf[1], &data[0], length);
+
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(i2c->adapter, msg, 1) == 1) {
+ retval = length;
+ break;
+ }
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+ }
+
+ if (retry == SYN_I2C_RETRY_TIMES) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: I2C write over retry limit\n",
+ __func__);
+ retval = -EIO;
+ }
+
+exit:
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ return retval;
+}
+
+static struct synaptics_dsx_bus_access bus_access = {
+ .type = BUS_I2C,
+ .read = synaptics_rmi4_i2c_read,
+ .write = synaptics_rmi4_i2c_write,
+};
+
+static struct synaptics_dsx_hw_interface hw_if;
+
+static struct platform_device *synaptics_dsx_i2c_device;
+
+static void synaptics_rmi4_i2c_dev_release(struct device *dev)
+{
+ kfree(synaptics_dsx_i2c_device);
+
+ return;
+}
+
+static int synaptics_rmi4_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int retval;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "%s: SMBus byte data commands not supported by host\n",
+ __func__);
+ return -EIO;
+ }
+
+ synaptics_dsx_i2c_device = kzalloc(
+ sizeof(struct platform_device),
+ GFP_KERNEL);
+ if (!synaptics_dsx_i2c_device) {
+ dev_err(&client->dev,
+ "%s: Failed to allocate memory for synaptics_dsx_i2c_device\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ hw_if.board_data = client->dev.platform_data;
+ hw_if.bus_access = &bus_access;
+
+ synaptics_dsx_i2c_device->name = PLATFORM_DRIVER_NAME;
+ synaptics_dsx_i2c_device->id = 0;
+ synaptics_dsx_i2c_device->num_resources = 0;
+ synaptics_dsx_i2c_device->dev.parent = &client->dev;
+ synaptics_dsx_i2c_device->dev.platform_data = &hw_if;
+ synaptics_dsx_i2c_device->dev.release = synaptics_rmi4_i2c_dev_release;
+
+ retval = platform_device_register(synaptics_dsx_i2c_device);
+ if (retval) {
+ dev_err(&client->dev,
+ "%s: Failed to register platform device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int synaptics_rmi4_i2c_remove(struct i2c_client *client)
+{
+ platform_device_unregister(synaptics_dsx_i2c_device);
+
+ return 0;
+}
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+ {I2C_DRIVER_NAME, 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static struct i2c_driver synaptics_rmi4_i2c_driver = {
+ .driver = {
+ .name = I2C_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = synaptics_rmi4_i2c_probe,
+ .remove = __devexit_p(synaptics_rmi4_i2c_remove),
+ .id_table = synaptics_rmi4_id_table,
+};
+
+int synaptics_rmi4_bus_init(void)
+{
+ return i2c_add_driver(&synaptics_rmi4_i2c_driver);
+}
+EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+
+void synaptics_rmi4_bus_exit(void)
+{
+ i2c_del_driver(&synaptics_rmi4_i2c_driver);
+
+ return;
+}
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics DSX I2C Bus Support Module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c
new file mode 100755
index 000000000000..44d3a9ee603b
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_proximity.c
@@ -0,0 +1,671 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_dsx_core.h"
+
+#define PROX_PHYS_NAME "synaptics_dsx/input1"
+
+#define HOVER_Z_MAX (255)
+
+#define HOVERING_FINGER_EN (1 << 4)
+
+static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(hover_finger_en, (S_IRUGO | S_IWUGO),
+ synaptics_rmi4_hover_finger_en_show,
+ synaptics_rmi4_hover_finger_en_store),
+};
+
+struct synaptics_rmi4_f12_query_5 {
+ union {
+ struct {
+ unsigned char size_of_query6;
+ struct {
+ unsigned char ctrl0_is_present:1;
+ unsigned char ctrl1_is_present:1;
+ unsigned char ctrl2_is_present:1;
+ unsigned char ctrl3_is_present:1;
+ unsigned char ctrl4_is_present:1;
+ unsigned char ctrl5_is_present:1;
+ unsigned char ctrl6_is_present:1;
+ unsigned char ctrl7_is_present:1;
+ } __packed;
+ struct {
+ unsigned char ctrl8_is_present:1;
+ unsigned char ctrl9_is_present:1;
+ unsigned char ctrl10_is_present:1;
+ unsigned char ctrl11_is_present:1;
+ unsigned char ctrl12_is_present:1;
+ unsigned char ctrl13_is_present:1;
+ unsigned char ctrl14_is_present:1;
+ unsigned char ctrl15_is_present:1;
+ } __packed;
+ struct {
+ unsigned char ctrl16_is_present:1;
+ unsigned char ctrl17_is_present:1;
+ unsigned char ctrl18_is_present:1;
+ unsigned char ctrl19_is_present:1;
+ unsigned char ctrl20_is_present:1;
+ unsigned char ctrl21_is_present:1;
+ unsigned char ctrl22_is_present:1;
+ unsigned char ctrl23_is_present:1;
+ } __packed;
+ };
+ unsigned char data[4];
+ };
+};
+
+struct synaptics_rmi4_f12_query_8 {
+ union {
+ struct {
+ unsigned char size_of_query9;
+ struct {
+ unsigned char data0_is_present:1;
+ unsigned char data1_is_present:1;
+ unsigned char data2_is_present:1;
+ unsigned char data3_is_present:1;
+ unsigned char data4_is_present:1;
+ unsigned char data5_is_present:1;
+ unsigned char data6_is_present:1;
+ unsigned char data7_is_present:1;
+ } __packed;
+ };
+ unsigned char data[2];
+ };
+};
+
+struct prox_finger_data {
+ union {
+ struct {
+ unsigned char object_type_and_status;
+ unsigned char x_lsb;
+ unsigned char x_msb;
+ unsigned char y_lsb;
+ unsigned char y_msb;
+ unsigned char z;
+ } __packed;
+ unsigned char proximity_data[6];
+ };
+};
+
+struct synaptics_rmi4_prox_handle {
+ bool hover_finger_present;
+ bool hover_finger_en;
+ unsigned char intr_mask;
+ unsigned short query_base_addr;
+ unsigned short control_base_addr;
+ unsigned short data_base_addr;
+ unsigned short command_base_addr;
+ unsigned short hover_finger_en_addr;
+ unsigned short hover_finger_data_addr;
+ struct input_dev *prox_dev;
+ struct prox_finger_data *finger_data;
+ struct synaptics_rmi4_data *rmi4_data;
+};
+
+static struct synaptics_rmi4_prox_handle *prox;
+
+DECLARE_COMPLETION(prox_remove_complete);
+
+static void prox_hover_finger_lift(void)
+{
+ input_report_key(prox->prox_dev, BTN_TOUCH, 0);
+ input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 0);
+ input_sync(prox->prox_dev);
+ prox->hover_finger_present = false;
+
+ return;
+}
+
+static void prox_hover_finger_report(void)
+{
+ int retval;
+ int x;
+ int y;
+ int z;
+ struct prox_finger_data *data;
+ struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
+
+ data = prox->finger_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ prox->hover_finger_data_addr,
+ data->proximity_data,
+ sizeof(data->proximity_data));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read hovering finger data\n",
+ __func__);
+ return;
+ }
+
+ if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) {
+ if (prox->hover_finger_present)
+ prox_hover_finger_lift();
+
+ return;
+ }
+
+ x = (data->x_msb << 8) | (data->x_lsb);
+ y = (data->y_msb << 8) | (data->y_lsb);
+ z = HOVER_Z_MAX - data->z;
+
+ input_report_key(prox->prox_dev, BTN_TOUCH, 0);
+ input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1);
+ input_report_abs(prox->prox_dev, ABS_X, x);
+ input_report_abs(prox->prox_dev, ABS_Y, y);
+ input_report_abs(prox->prox_dev, ABS_DISTANCE, z);
+
+ input_sync(prox->prox_dev);
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: x = %d y = %d z = %d\n",
+ __func__, x, y, z);
+
+ prox->hover_finger_present = true;
+
+ return;
+}
+
+static int prox_set_hover_finger_en(void)
+{
+ int retval;
+ unsigned char object_report_enable;
+ struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ prox->hover_finger_en_addr,
+ &object_report_enable,
+ sizeof(object_report_enable));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read from object report enable register\n",
+ __func__);
+ return retval;
+ }
+
+ if (prox->hover_finger_en)
+ object_report_enable |= HOVERING_FINGER_EN;
+ else
+ object_report_enable &= ~HOVERING_FINGER_EN;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ prox->hover_finger_en_addr,
+ &object_report_enable,
+ sizeof(object_report_enable));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write to object report enable register\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static void prox_set_params(void)
+{
+ input_set_abs_params(prox->prox_dev, ABS_X, 0,
+ prox->rmi4_data->sensor_max_x, 0, 0);
+ input_set_abs_params(prox->prox_dev, ABS_Y, 0,
+ prox->rmi4_data->sensor_max_y, 0, 0);
+ input_set_abs_params(prox->prox_dev, ABS_DISTANCE, 0,
+ HOVER_Z_MAX, 0, 0);
+
+ return;
+}
+
+static int prox_reg_init(void)
+{
+ int retval;
+ unsigned char ctrl_23_offset;
+ unsigned char data_1_offset;
+ struct synaptics_rmi4_f12_query_5 query_5;
+ struct synaptics_rmi4_f12_query_8 query_8;
+ struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ prox->query_base_addr + 5,
+ query_5.data,
+ sizeof(query_5.data));
+ if (retval < 0)
+ return retval;
+
+ ctrl_23_offset = query_5.ctrl0_is_present +
+ query_5.ctrl1_is_present +
+ query_5.ctrl2_is_present +
+ query_5.ctrl3_is_present +
+ query_5.ctrl4_is_present +
+ query_5.ctrl5_is_present +
+ query_5.ctrl6_is_present +
+ query_5.ctrl7_is_present +
+ query_5.ctrl8_is_present +
+ query_5.ctrl9_is_present +
+ query_5.ctrl10_is_present +
+ query_5.ctrl11_is_present +
+ query_5.ctrl12_is_present +
+ query_5.ctrl13_is_present +
+ query_5.ctrl14_is_present +
+ query_5.ctrl15_is_present +
+ query_5.ctrl16_is_present +
+ query_5.ctrl17_is_present +
+ query_5.ctrl18_is_present +
+ query_5.ctrl19_is_present +
+ query_5.ctrl20_is_present +
+ query_5.ctrl21_is_present +
+ query_5.ctrl22_is_present;
+
+ prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset;
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ prox->query_base_addr + 8,
+ query_8.data,
+ sizeof(query_8.data));
+ if (retval < 0)
+ return retval;
+
+ data_1_offset = query_8.data0_is_present;
+ prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset;
+
+ return retval;
+}
+
+static int prox_scan_pdt(void)
+{
+ int retval;
+ unsigned char ii;
+ unsigned char page;
+ unsigned char intr_count = 0;
+ unsigned char intr_off;
+ unsigned char intr_src;
+ unsigned short addr;
+ struct synaptics_rmi4_fn_desc fd;
+ struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
+
+ for (page = 0; page < PAGES_TO_SERVICE; page++) {
+ for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
+ addr |= (page << 8);
+
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ addr,
+ (unsigned char *)&fd,
+ sizeof(fd));
+ if (retval < 0)
+ return retval;
+
+ addr &= ~(MASK_8BIT << 8);
+
+ if (fd.fn_number) {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Found F%02x\n",
+ __func__, fd.fn_number);
+ switch (fd.fn_number) {
+ case SYNAPTICS_RMI4_F12:
+ goto f12_found;
+ break;
+ }
+ } else {
+ break;
+ }
+
+ intr_count += (fd.intr_src_count & MASK_3BIT);
+ }
+ }
+
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to find F12\n",
+ __func__);
+ return -EINVAL;
+
+f12_found:
+ prox->query_base_addr = fd.query_base_addr | (page << 8);
+ prox->control_base_addr = fd.ctrl_base_addr | (page << 8);
+ prox->data_base_addr = fd.data_base_addr | (page << 8);
+ prox->command_base_addr = fd.cmd_base_addr | (page << 8);
+
+ retval = prox_reg_init();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to initialize proximity registers\n",
+ __func__);
+ return retval;
+ }
+
+ prox->intr_mask = 0;
+ intr_src = fd.intr_src_count;
+ intr_off = intr_count % 8;
+ for (ii = intr_off;
+ ii < ((intr_src & MASK_3BIT) +
+ intr_off);
+ ii++) {
+ prox->intr_mask |= 1 << ii;
+ }
+
+ rmi4_data->intr_mask[0] |= prox->intr_mask;
+
+ addr = rmi4_data->f01_ctrl_base_addr + 1;
+
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ addr,
+ &(rmi4_data->intr_mask[0]),
+ sizeof(rmi4_data->intr_mask[0]));
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to set interrupt enable bit\n",
+ __func__);
+ return retval;
+ }
+
+ return 0;
+}
+
+static ssize_t synaptics_rmi4_hover_finger_en_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ if (!prox)
+ return -ENODEV;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ prox->hover_finger_en);
+}
+
+static ssize_t synaptics_rmi4_hover_finger_en_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int retval;
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;
+
+ if (!prox)
+ return -ENODEV;
+
+ if (sscanf(buf, "%x", &input) != 1)
+ return -EINVAL;
+
+ if (input == 1)
+ prox->hover_finger_en = true;
+ else if (input == 0)
+ prox->hover_finger_en = false;
+ else
+ return -EINVAL;
+
+ retval = prox_set_hover_finger_en();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to change hovering finger enable setting\n",
+ __func__);
+ return retval;
+ }
+
+ return count;
+}
+
+int synaptics_rmi4_prox_hover_finger_en(bool enable)
+{
+ int retval;
+
+ if (!prox)
+ return -ENODEV;
+
+ prox->hover_finger_en = enable;
+
+ retval = prox_set_hover_finger_en();
+ if (retval < 0)
+ return retval;
+
+ return 0;
+}
+EXPORT_SYMBOL(synaptics_rmi4_prox_hover_finger_en);
+
+static void synaptics_rmi4_prox_attn(struct synaptics_rmi4_data *rmi4_data,
+ unsigned char intr_mask)
+{
+ if (!prox)
+ return;
+
+ if (prox->intr_mask & intr_mask)
+ prox_hover_finger_report();
+
+ return;
+}
+
+static int synaptics_rmi4_prox_init(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ unsigned char attr_count;
+
+ prox = kzalloc(sizeof(*prox), GFP_KERNEL);
+ if (!prox) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for prox\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ prox->finger_data = kzalloc(sizeof(*(prox->finger_data)), GFP_KERNEL);
+ if (!prox->finger_data) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for finger_data\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit_free_prox;
+ }
+
+ prox->rmi4_data = rmi4_data;
+
+ retval = prox_scan_pdt();
+ if (retval < 0)
+ goto exit_free_finger_data;
+
+ prox->hover_finger_en = true;
+
+ retval = prox_set_hover_finger_en();
+ if (retval < 0)
+ return retval;
+
+ prox->prox_dev = input_allocate_device();
+ if (prox->prox_dev == NULL) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate proximity device\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit_free_finger_data;
+ }
+
+ prox->prox_dev->name = PLATFORM_DRIVER_NAME;
+ prox->prox_dev->phys = PROX_PHYS_NAME;
+ prox->prox_dev->id.product = SYNAPTICS_DSX_DRIVER_PRODUCT;
+ prox->prox_dev->id.version = SYNAPTICS_DSX_DRIVER_VERSION;
+ prox->prox_dev->dev.parent = rmi4_data->pdev->dev.parent;
+ input_set_drvdata(prox->prox_dev, rmi4_data);
+
+ set_bit(EV_KEY, prox->prox_dev->evbit);
+ set_bit(EV_ABS, prox->prox_dev->evbit);
+ set_bit(BTN_TOUCH, prox->prox_dev->keybit);
+ set_bit(BTN_TOOL_FINGER, prox->prox_dev->keybit);
+#ifdef INPUT_PROP_DIRECT
+ set_bit(INPUT_PROP_DIRECT, prox->prox_dev->propbit);
+#endif
+
+ prox_set_params();
+
+ retval = input_register_device(prox->prox_dev);
+ if (retval) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to register proximity device\n",
+ __func__);
+ goto exit_free_input_device;
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ goto exit_free_sysfs;
+ }
+ }
+
+ return 0;
+
+exit_free_sysfs:
+ for (attr_count--; attr_count >= 0; attr_count--) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ input_unregister_device(prox->prox_dev);
+ prox->prox_dev = NULL;
+
+exit_free_input_device:
+ if (prox->prox_dev)
+ input_free_device(prox->prox_dev);
+
+exit_free_finger_data:
+ kfree(prox->finger_data);
+
+exit_free_prox:
+ kfree(prox);
+ prox = NULL;
+
+exit:
+ return retval;
+}
+
+static void synaptics_rmi4_prox_remove(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char attr_count;
+
+ if (!prox)
+ goto exit;
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ sysfs_remove_file(&rmi4_data->input_dev->dev.kobj,
+ &attrs[attr_count].attr);
+ }
+
+ input_unregister_device(prox->prox_dev);
+ kfree(prox->finger_data);
+ kfree(prox);
+ prox = NULL;
+
+exit:
+ complete(&prox_remove_complete);
+
+ return;
+}
+
+static void synaptics_rmi4_prox_reset(struct synaptics_rmi4_data *rmi4_data)
+{
+ if (!prox)
+ return;
+
+ prox_hover_finger_lift();
+
+ prox_scan_pdt();
+
+ prox_set_hover_finger_en();
+
+ prox_set_params();
+
+ return;
+}
+
+static void synaptics_rmi4_prox_reinit(struct synaptics_rmi4_data *rmi4_data)
+{
+ if (!prox)
+ return;
+
+ prox_hover_finger_lift();
+
+ prox_set_hover_finger_en();
+
+ return;
+}
+
+static void synaptics_rmi4_prox_e_suspend(struct synaptics_rmi4_data *rmi4_data)
+{
+ if (!prox)
+ return;
+
+ prox_hover_finger_lift();
+
+ return;
+}
+
+static void synaptics_rmi4_prox_suspend(struct synaptics_rmi4_data *rmi4_data)
+{
+ if (!prox)
+ return;
+
+ prox_hover_finger_lift();
+
+ return;
+}
+
+static struct synaptics_rmi4_exp_fn proximity_module = {
+ .fn_type = RMI_PROXIMITY,
+ .init = synaptics_rmi4_prox_init,
+ .remove = synaptics_rmi4_prox_remove,
+ .reset = synaptics_rmi4_prox_reset,
+ .reinit = synaptics_rmi4_prox_reinit,
+ .early_suspend = synaptics_rmi4_prox_e_suspend,
+ .suspend = synaptics_rmi4_prox_suspend,
+ .resume = NULL,
+ .late_resume = NULL,
+ .attn = synaptics_rmi4_prox_attn,
+};
+
+static int __init rmi4_proximity_module_init(void)
+{
+ synaptics_rmi4_new_function(&proximity_module, true);
+
+ return 0;
+}
+
+static void __exit rmi4_proximity_module_exit(void)
+{
+ synaptics_rmi4_new_function(&proximity_module, false);
+
+ wait_for_completion(&prox_remove_complete);
+
+ return;
+}
+
+module_init(rmi4_proximity_module_init);
+module_exit(rmi4_proximity_module_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics DSX Proximity Module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
new file mode 100755
index 000000000000..016ffe9d91df
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_rmi_dev.c
@@ -0,0 +1,781 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/uaccess.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_dsx_core.h"
+
+#define CHAR_DEVICE_NAME "rmi"
+#define DEVICE_CLASS_NAME "rmidev"
+#define SYSFS_FOLDER_NAME "rmidev"
+#define DEV_NUMBER 1
+#define REG_ADDR_LIMIT 0xFFFF
+
+static ssize_t rmidev_sysfs_data_show(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmidev_sysfs_data_store(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmidev_sysfs_open_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t rmidev_sysfs_release_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count);
+
+static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+struct rmidev_handle {
+ dev_t dev_no;
+ struct device dev;
+ struct synaptics_rmi4_data *rmi4_data;
+ struct kobject *sysfs_dir;
+ void *data;
+ bool irq_enabled;
+};
+
+struct rmidev_data {
+ int ref_count;
+ struct cdev main_dev;
+ struct class *device_class;
+ struct mutex file_mutex;
+ struct rmidev_handle *rmi_dev;
+};
+
+static struct bin_attribute attr_data = {
+ .attr = {
+ .name = "data",
+ .mode = (S_IRUGO | S_IWUGO),
+ },
+ .size = 0,
+ .read = rmidev_sysfs_data_show,
+ .write = rmidev_sysfs_data_store,
+};
+
+static struct device_attribute attrs[] = {
+ __ATTR(open, S_IWUGO,
+ synaptics_rmi4_show_error,
+ rmidev_sysfs_open_store),
+ __ATTR(release, S_IWUGO,
+ synaptics_rmi4_show_error,
+ rmidev_sysfs_release_store),
+ __ATTR(attn_state, S_IRUGO,
+ rmidev_sysfs_attn_state_show,
+ synaptics_rmi4_store_error),
+};
+
+static int rmidev_major_num;
+
+static struct class *rmidev_device_class;
+
+static struct rmidev_handle *rmidev;
+
+DECLARE_COMPLETION(rmidev_remove_complete);
+
+static irqreturn_t rmidev_sysfs_irq(int irq, void *data)
+{
+ struct synaptics_rmi4_data *rmi4_data = data;
+
+ sysfs_notify(&rmi4_data->input_dev->dev.kobj,
+ SYSFS_FOLDER_NAME, "attn_state");
+
+ return IRQ_HANDLED;
+}
+
+static int rmidev_sysfs_irq_enable(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ int retval = 0;
+ unsigned char intr_status[MAX_INTR_REGISTERS];
+ unsigned long irq_flags = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+
+ if (enable) {
+ if (rmidev->irq_enabled)
+ return retval;
+
+ /* Clear interrupts first */
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ rmi4_data->f01_data_base_addr + 1,
+ intr_status,
+ rmi4_data->num_of_intr_regs);
+ if (retval < 0)
+ return retval;
+
+ retval = request_threaded_irq(rmi4_data->irq, NULL,
+ rmidev_sysfs_irq, irq_flags,
+ "synaptics_dsx_rmidev", rmi4_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create irq thread\n",
+ __func__);
+ return retval;
+ }
+
+ rmidev->irq_enabled = true;
+ } else {
+ if (rmidev->irq_enabled) {
+ disable_irq(rmi4_data->irq);
+ free_irq(rmi4_data->irq, rmi4_data);
+ rmidev->irq_enabled = false;
+ }
+ }
+
+ return retval;
+}
+
+static ssize_t rmidev_sysfs_data_show(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ int retval;
+ unsigned int length = (unsigned int)count;
+ unsigned short address = (unsigned short)pos;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+
+ if (length > (REG_ADDR_LIMIT - address)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Out of register map limit\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (length) {
+ retval = synaptics_rmi4_reg_read(rmi4_data,
+ address,
+ (unsigned char *)buf,
+ length);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to read data\n",
+ __func__);
+ return retval;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return length;
+}
+
+static ssize_t rmidev_sysfs_data_store(struct file *data_file,
+ struct kobject *kobj, struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ int retval;
+ unsigned int length = (unsigned int)count;
+ unsigned short address = (unsigned short)pos;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+
+ if (length > (REG_ADDR_LIMIT - address)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Out of register map limit\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (length) {
+ retval = synaptics_rmi4_reg_write(rmi4_data,
+ address,
+ (unsigned char *)buf,
+ length);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to write data\n",
+ __func__);
+ return retval;
+ }
+ } else {
+ return -EINVAL;
+ }
+
+ return length;
+}
+
+static ssize_t rmidev_sysfs_open_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input != 1)
+ return -EINVAL;
+
+ rmi4_data->irq_enable(rmi4_data, false);
+ rmidev_sysfs_irq_enable(rmi4_data, true);
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Attention interrupt disabled\n",
+ __func__);
+
+ return count;
+}
+
+static ssize_t rmidev_sysfs_release_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int input;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+
+ if (sscanf(buf, "%u", &input) != 1)
+ return -EINVAL;
+
+ if (input != 1)
+ return -EINVAL;
+
+ rmi4_data->reset_device(rmi4_data);
+
+ rmidev_sysfs_irq_enable(rmi4_data, false);
+ rmi4_data->irq_enable(rmi4_data, true);
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Attention interrupt enabled\n",
+ __func__);
+
+ return count;
+}
+
+static ssize_t rmidev_sysfs_attn_state_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int attn_state;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ attn_state = gpio_get_value(bdata->irq_gpio);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", attn_state);
+}
+
+/*
+ * rmidev_llseek - used to set up register address
+ *
+ * @filp: file structure for seek
+ * @off: offset
+ * if whence == SEEK_SET,
+ * high 16 bits: page address
+ * low 16 bits: register address
+ * if whence == SEEK_CUR,
+ * offset from current position
+ * if whence == SEEK_END,
+ * offset from end position (0xFFFF)
+ * @whence: SEEK_SET, SEEK_CUR, or SEEK_END
+ */
+static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence)
+{
+ loff_t newpos;
+ struct rmidev_data *dev_data = filp->private_data;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+
+ if (IS_ERR(dev_data)) {
+ pr_err("%s: Pointer of char device data is invalid", __func__);
+ return -EBADF;
+ }
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ switch (whence) {
+ case SEEK_SET:
+ newpos = off;
+ break;
+ case SEEK_CUR:
+ newpos = filp->f_pos + off;
+ break;
+ case SEEK_END:
+ newpos = REG_ADDR_LIMIT + off;
+ break;
+ default:
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: New position 0x%04x is invalid\n",
+ __func__, (unsigned int)newpos);
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ filp->f_pos = newpos;
+
+clean_up:
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return newpos;
+}
+
+/*
+ * rmidev_read: - use to read data from rmi device
+ *
+ * @filp: file structure for read
+ * @buf: user space buffer pointer
+ * @count: number of bytes to read
+ * @f_pos: offset (starting register address)
+ */
+static ssize_t rmidev_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ ssize_t retval;
+ unsigned char tmpbuf[count + 1];
+ struct rmidev_data *dev_data = filp->private_data;
+
+ if (IS_ERR(dev_data)) {
+ pr_err("%s: Pointer of char device data is invalid", __func__);
+ return -EBADF;
+ }
+
+ if (count == 0)
+ return 0;
+
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ retval = synaptics_rmi4_reg_read(rmidev->rmi4_data,
+ *f_pos,
+ tmpbuf,
+ count);
+ if (retval < 0)
+ goto clean_up;
+
+ if (copy_to_user(buf, tmpbuf, count))
+ retval = -EFAULT;
+ else
+ *f_pos += retval;
+
+clean_up:
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return retval;
+}
+
+/*
+ * rmidev_write: - used to write data to rmi device
+ *
+ * @filep: file structure for write
+ * @buf: user space buffer pointer
+ * @count: number of bytes to write
+ * @f_pos: offset (starting register address)
+ */
+static ssize_t rmidev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ ssize_t retval;
+ unsigned char tmpbuf[count + 1];
+ struct rmidev_data *dev_data = filp->private_data;
+
+ if (IS_ERR(dev_data)) {
+ pr_err("%s: Pointer of char device data is invalid", __func__);
+ return -EBADF;
+ }
+
+ if (count == 0)
+ return 0;
+
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ if (copy_from_user(tmpbuf, buf, count))
+ return -EFAULT;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ retval = synaptics_rmi4_reg_write(rmidev->rmi4_data,
+ *f_pos,
+ tmpbuf,
+ count);
+ if (retval >= 0)
+ *f_pos += retval;
+
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return retval;
+}
+
+/*
+ * rmidev_open: enable access to rmi device
+ * @inp: inode struture
+ * @filp: file structure
+ */
+static int rmidev_open(struct inode *inp, struct file *filp)
+{
+ int retval = 0;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+ struct rmidev_data *dev_data =
+ container_of(inp->i_cdev, struct rmidev_data, main_dev);
+
+ if (!dev_data)
+ return -EACCES;
+
+ filp->private_data = dev_data;
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ rmi4_data->irq_enable(rmi4_data, false);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Attention interrupt disabled\n",
+ __func__);
+
+ if (dev_data->ref_count < 1)
+ dev_data->ref_count++;
+ else
+ retval = -EACCES;
+
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return retval;
+}
+
+/*
+ * rmidev_release: - release access to rmi device
+ * @inp: inode structure
+ * @filp: file structure
+ */
+static int rmidev_release(struct inode *inp, struct file *filp)
+{
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+ struct rmidev_data *dev_data =
+ container_of(inp->i_cdev, struct rmidev_data, main_dev);
+
+ if (!dev_data)
+ return -EACCES;
+
+ rmi4_data->reset_device(rmi4_data);
+
+ mutex_lock(&(dev_data->file_mutex));
+
+ dev_data->ref_count--;
+ if (dev_data->ref_count < 0)
+ dev_data->ref_count = 0;
+
+ rmi4_data->irq_enable(rmi4_data, true);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Attention interrupt enabled\n",
+ __func__);
+
+ mutex_unlock(&(dev_data->file_mutex));
+
+ return 0;
+}
+
+static const struct file_operations rmidev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = rmidev_llseek,
+ .read = rmidev_read,
+ .write = rmidev_write,
+ .open = rmidev_open,
+ .release = rmidev_release,
+};
+
+static void rmidev_device_cleanup(struct rmidev_data *dev_data)
+{
+ dev_t devno;
+ struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data;
+
+ if (dev_data) {
+ devno = dev_data->main_dev.dev;
+
+ if (dev_data->device_class)
+ device_destroy(dev_data->device_class, devno);
+
+ cdev_del(&dev_data->main_dev);
+
+ unregister_chrdev_region(devno, 1);
+
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: rmidev device removed\n",
+ __func__);
+ }
+
+ return;
+}
+
+static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+{
+ if (!mode)
+ return NULL;
+
+ *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+
+ return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
+}
+
+static int rmidev_create_device_class(void)
+{
+ rmidev_device_class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
+
+ if (IS_ERR(rmidev_device_class)) {
+ pr_err("%s: Failed to create /dev/%s\n",
+ __func__, CHAR_DEVICE_NAME);
+ return -ENODEV;
+ }
+
+ rmidev_device_class->devnode = rmi_char_devnode;
+
+ return 0;
+}
+
+static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+ dev_t dev_no;
+ unsigned char attr_count;
+ struct rmidev_data *dev_data;
+ struct device *device_ptr;
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL);
+ if (!rmidev) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for rmidev\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_rmidev;
+ }
+
+ rmidev->rmi4_data = rmi4_data;
+
+ retval = rmidev_create_device_class();
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create device class\n",
+ __func__);
+ goto err_device_class;
+ }
+
+ if (rmidev_major_num) {
+ dev_no = MKDEV(rmidev_major_num, DEV_NUMBER);
+ retval = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
+ } else {
+ retval = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate char device region\n",
+ __func__);
+ goto err_device_region;
+ }
+
+ rmidev_major_num = MAJOR(dev_no);
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Major number of rmidev = %d\n",
+ __func__, rmidev_major_num);
+ }
+
+ dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
+ if (!dev_data) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to alloc mem for dev_data\n",
+ __func__);
+ retval = -ENOMEM;
+ goto err_dev_data;
+ }
+
+ mutex_init(&dev_data->file_mutex);
+ dev_data->rmi_dev = rmidev;
+ rmidev->data = dev_data;
+
+ cdev_init(&dev_data->main_dev, &rmidev_fops);
+
+ retval = cdev_add(&dev_data->main_dev, dev_no, 1);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to add rmi char device\n",
+ __func__);
+ goto err_char_device;
+ }
+
+ dev_set_name(&rmidev->dev, "rmidev%d", MINOR(dev_no));
+ dev_data->device_class = rmidev_device_class;
+
+ device_ptr = device_create(dev_data->device_class, NULL, dev_no,
+ NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no));
+ if (IS_ERR(device_ptr)) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create rmi char device\n",
+ __func__);
+ retval = -ENODEV;
+ goto err_char_device;
+ }
+
+ retval = gpio_export(bdata->irq_gpio, false);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to export attention gpio\n",
+ __func__);
+ } else {
+ retval = gpio_export_link(&(rmi4_data->input_dev->dev),
+ "attn", bdata->irq_gpio);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s Failed to create gpio symlink\n",
+ __func__);
+ } else {
+ dev_dbg(rmi4_data->pdev->dev.parent,
+ "%s: Exported attention gpio %d\n",
+ __func__, bdata->irq_gpio);
+ }
+ }
+
+ rmidev->sysfs_dir = kobject_create_and_add(SYSFS_FOLDER_NAME,
+ &rmi4_data->input_dev->dev.kobj);
+ if (!rmidev->sysfs_dir) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create sysfs directory\n",
+ __func__);
+ retval = -ENODEV;
+ goto err_sysfs_dir;
+ }
+
+ retval = sysfs_create_bin_file(rmidev->sysfs_dir,
+ &attr_data);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create sysfs bin file\n",
+ __func__);
+ goto err_sysfs_bin;
+ }
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ retval = sysfs_create_file(rmidev->sysfs_dir,
+ &attrs[attr_count].attr);
+ if (retval < 0) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ retval = -ENODEV;
+ goto err_sysfs_attrs;
+ }
+ }
+
+ return 0;
+
+err_sysfs_attrs:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
+
+ sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
+
+err_sysfs_bin:
+ kobject_put(rmidev->sysfs_dir);
+
+err_sysfs_dir:
+err_char_device:
+ rmidev_device_cleanup(dev_data);
+ kfree(dev_data);
+
+err_dev_data:
+ unregister_chrdev_region(dev_no, 1);
+
+err_device_region:
+ class_destroy(rmidev_device_class);
+
+err_device_class:
+ kfree(rmidev);
+ rmidev = NULL;
+
+err_rmidev:
+ return retval;
+}
+
+static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char attr_count;
+ struct rmidev_data *dev_data;
+
+ if (!rmidev)
+ goto exit;
+
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++)
+ sysfs_remove_file(rmidev->sysfs_dir, &attrs[attr_count].attr);
+
+ sysfs_remove_bin_file(rmidev->sysfs_dir, &attr_data);
+
+ kobject_put(rmidev->sysfs_dir);
+
+ dev_data = rmidev->data;
+ if (dev_data) {
+ rmidev_device_cleanup(dev_data);
+ kfree(dev_data);
+ }
+
+ unregister_chrdev_region(rmidev->dev_no, 1);
+
+ class_destroy(rmidev_device_class);
+
+ kfree(rmidev);
+ rmidev = NULL;
+
+exit:
+ complete(&rmidev_remove_complete);
+
+ return;
+}
+
+static struct synaptics_rmi4_exp_fn rmidev_module = {
+ .fn_type = RMI_DEV,
+ .init = rmidev_init_device,
+ .remove = rmidev_remove_device,
+ .reset = NULL,
+ .reinit = NULL,
+ .early_suspend = NULL,
+ .suspend = NULL,
+ .resume = NULL,
+ .late_resume = NULL,
+ .attn = NULL,
+};
+
+static int __init rmidev_module_init(void)
+{
+ synaptics_rmi4_new_function(&rmidev_module, true);
+
+ return 0;
+}
+
+static void __exit rmidev_module_exit(void)
+{
+ synaptics_rmi4_new_function(&rmidev_module, false);
+
+ wait_for_completion(&rmidev_remove_complete);
+
+ return;
+}
+
+module_init(rmidev_module_init);
+module_exit(rmidev_module_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c
new file mode 100755
index 000000000000..dd797eef3be1
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_spi.c
@@ -0,0 +1,335 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/input/synaptics_dsx.h>
+#include "synaptics_dsx_core.h"
+
+#define SPI_READ 0x80
+#define SPI_WRITE 0x00
+
+static int synaptics_rmi4_spi_set_page(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr)
+{
+ int retval;
+ unsigned int index;
+ unsigned int xfer_count = PAGE_SELECT_LEN + 1;
+ unsigned char txbuf[xfer_count];
+ unsigned char page;
+ struct spi_message msg;
+ struct spi_transfer xfers[xfer_count];
+ struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ page = ((addr >> 8) & ~MASK_7BIT);
+ if (page != rmi4_data->current_page) {
+ spi_message_init(&msg);
+
+ txbuf[0] = SPI_WRITE;
+ txbuf[1] = MASK_8BIT;
+ txbuf[2] = page;
+
+ for (index = 0; index < xfer_count; index++) {
+ memset(&xfers[index], 0, sizeof(struct spi_transfer));
+ xfers[index].len = 1;
+ xfers[index].delay_usecs = bdata->byte_delay_us;
+ xfers[index].tx_buf = &txbuf[index];
+ spi_message_add_tail(&xfers[index], &msg);
+ }
+
+ if (bdata->block_delay_us)
+ xfers[index - 1].delay_usecs = bdata->block_delay_us;
+
+ retval = spi_sync(spi, &msg);
+ if (retval == 0) {
+ rmi4_data->current_page = page;
+ retval = PAGE_SELECT_LEN;
+ } else {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to complete SPI transfer, error = %d\n",
+ __func__, retval);
+ }
+ } else {
+ retval = PAGE_SELECT_LEN;
+ }
+
+ return retval;
+}
+
+static int synaptics_rmi4_spi_read(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval;
+ unsigned int index;
+ unsigned int xfer_count = length + ADDRESS_WORD_LEN;
+ unsigned char txbuf[ADDRESS_WORD_LEN];
+ unsigned char *rxbuf = NULL;
+ struct spi_message msg;
+ struct spi_transfer *xfers = NULL;
+ struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ spi_message_init(&msg);
+
+ xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL);
+ if (!xfers) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate memory for xfers\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ txbuf[0] = (addr >> 8) | SPI_READ;
+ txbuf[1] = addr & MASK_8BIT;
+
+ rxbuf = kmalloc(length, GFP_KERNEL);
+ if (!rxbuf) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate memory for rxbuf\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ retval = -EIO;
+ goto exit;
+ }
+
+ for (index = 0; index < xfer_count; index++) {
+ xfers[index].len = 1;
+ xfers[index].delay_usecs = bdata->byte_delay_us;
+ if (index < ADDRESS_WORD_LEN)
+ xfers[index].tx_buf = &txbuf[index];
+ else
+ xfers[index].rx_buf = &rxbuf[index - ADDRESS_WORD_LEN];
+ spi_message_add_tail(&xfers[index], &msg);
+ }
+
+ if (bdata->block_delay_us)
+ xfers[index - 1].delay_usecs = bdata->block_delay_us;
+
+ retval = spi_sync(spi, &msg);
+ if (retval == 0) {
+ retval = length;
+ memcpy(data, rxbuf, length);
+ } else {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to complete SPI transfer, error = %d\n",
+ __func__, retval);
+ }
+
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+exit:
+ kfree(rxbuf);
+ kfree(xfers);
+
+ return retval;
+}
+
+static int synaptics_rmi4_spi_write(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval;
+ unsigned int index;
+ unsigned int xfer_count = length + ADDRESS_WORD_LEN;
+ unsigned char *txbuf = NULL;
+ struct spi_message msg;
+ struct spi_transfer *xfers = NULL;
+ struct spi_device *spi = to_spi_device(rmi4_data->pdev->dev.parent);
+ const struct synaptics_dsx_board_data *bdata =
+ rmi4_data->hw_if->board_data;
+
+ spi_message_init(&msg);
+
+ xfers = kcalloc(xfer_count, sizeof(struct spi_transfer), GFP_KERNEL);
+ if (!xfers) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate memory for xfers\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ txbuf = kmalloc(xfer_count, GFP_KERNEL);
+ if (!txbuf) {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to allocate memory for txbuf\n",
+ __func__);
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ txbuf[0] = (addr >> 8) & ~SPI_READ;
+ txbuf[1] = addr & MASK_8BIT;
+ memcpy(&txbuf[ADDRESS_WORD_LEN], data, length);
+
+ mutex_lock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+ retval = synaptics_rmi4_spi_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ retval = -EIO;
+ goto exit;
+ }
+
+ for (index = 0; index < xfer_count; index++) {
+ xfers[index].len = 1;
+ xfers[index].delay_usecs = bdata->byte_delay_us;
+ xfers[index].tx_buf = &txbuf[index];
+ spi_message_add_tail(&xfers[index], &msg);
+ }
+
+ if (bdata->block_delay_us)
+ xfers[index - 1].delay_usecs = bdata->block_delay_us;
+
+ retval = spi_sync(spi, &msg);
+ if (retval == 0) {
+ retval = length;
+ } else {
+ dev_err(rmi4_data->pdev->dev.parent,
+ "%s: Failed to complete SPI transfer, error = %d\n",
+ __func__, retval);
+ }
+
+ mutex_unlock(&rmi4_data->rmi4_io_ctrl_mutex);
+
+exit:
+ kfree(txbuf);
+ kfree(xfers);
+
+ return retval;
+}
+
+static struct synaptics_dsx_bus_access bus_access = {
+ .type = BUS_SPI,
+ .read = synaptics_rmi4_spi_read,
+ .write = synaptics_rmi4_spi_write,
+};
+
+static struct synaptics_dsx_hw_interface hw_if;
+
+static struct platform_device *synaptics_dsx_spi_device;
+
+static void synaptics_rmi4_spi_dev_release(struct device *dev)
+{
+ kfree(synaptics_dsx_spi_device);
+
+ return;
+}
+
+static int synaptics_rmi4_spi_probe(struct spi_device *spi)
+{
+ int retval;
+
+ if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
+ dev_err(&spi->dev,
+ "%s: Full duplex not supported by host\n",
+ __func__);
+ return -EIO;
+ }
+
+ synaptics_dsx_spi_device = kzalloc(
+ sizeof(struct platform_device),
+ GFP_KERNEL);
+ if (!synaptics_dsx_spi_device) {
+ dev_err(&spi->dev,
+ "%s: Failed to allocate memory for synaptics_dsx_spi_device\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3;
+
+ retval = spi_setup(spi);
+ if (retval < 0) {
+ dev_err(&spi->dev,
+ "%s: Failed to perform SPI setup\n",
+ __func__);
+ return retval;
+ }
+
+ hw_if.board_data = spi->dev.platform_data;
+ hw_if.bus_access = &bus_access;
+
+ synaptics_dsx_spi_device->name = PLATFORM_DRIVER_NAME;
+ synaptics_dsx_spi_device->id = 0;
+ synaptics_dsx_spi_device->num_resources = 0;
+ synaptics_dsx_spi_device->dev.parent = &spi->dev;
+ synaptics_dsx_spi_device->dev.platform_data = &hw_if;
+ synaptics_dsx_spi_device->dev.release = synaptics_rmi4_spi_dev_release;
+
+ retval = platform_device_register(synaptics_dsx_spi_device);
+ if (retval) {
+ dev_err(&spi->dev,
+ "%s: Failed to register platform device\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int synaptics_rmi4_spi_remove(struct spi_device *spi)
+{
+ platform_device_unregister(synaptics_dsx_spi_device);
+
+ return 0;
+}
+
+static struct spi_driver synaptics_rmi4_spi_driver = {
+ .driver = {
+ .name = SPI_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = synaptics_rmi4_spi_probe,
+ .remove = __devexit_p(synaptics_rmi4_spi_remove),
+};
+
+
+int synaptics_rmi4_bus_init(void)
+{
+ return spi_register_driver(&synaptics_rmi4_spi_driver);
+}
+EXPORT_SYMBOL(synaptics_rmi4_bus_init);
+
+void synaptics_rmi4_bus_exit(void)
+{
+ spi_unregister_driver(&synaptics_rmi4_spi_driver);
+
+ return;
+}
+EXPORT_SYMBOL(synaptics_rmi4_bus_exit);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("Synaptics DSX SPI Bus Support Module");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/input/synaptics_dsx_v2.h b/include/linux/input/synaptics_dsx_v2.h
new file mode 100755
index 000000000000..dc6e6be28ceb
--- /dev/null
+++ b/include/linux/input/synaptics_dsx_v2.h
@@ -0,0 +1,80 @@
+/*
+ * Synaptics DSX 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>
+ *
+ * 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.
+ */
+
+#ifndef _SYNAPTICS_DSX_H_
+#define _SYNAPTICS_DSX_H_
+
+#define PLATFORM_DRIVER_NAME "synaptics_dsx"
+#define I2C_DRIVER_NAME "synaptics_dsx_i2c"
+#define SPI_DRIVER_NAME "synaptics_dsx_spi"
+
+/*
+ * struct synaptics_dsx_cap_button_map - 0d button map
+ * @nbuttons: number of 0d buttons
+ * @map: pointer to array of button types
+ */
+struct synaptics_dsx_cap_button_map {
+ unsigned char nbuttons;
+ unsigned char *map;
+};
+
+/*
+ * struct synaptics_dsx_board_data - dsx board data
+ * @x_flip: x flip flag
+ * @y_flip: y flip flag
+ * @irq_gpio: attention interrupt gpio
+ * @power_gpio: power switch gpio
+ * @power_on_state: power switch active state
+ * @reset_gpio: reset gpio
+ * @reset_on_state: reset active state
+ * @irq_flags: irq flags
+ * @panel_x: x-axis resolution of display panel
+ * @panel_y: y-axis resolution of display panel
+ * @power_delay_ms: delay time to wait after power-on
+ * @reset_delay_ms: delay time to wait after reset
+ * @reset_active_ms: reset active time
+ * @byte_delay_us: delay time between two bytes of SPI data
+ * @block_delay_us: delay time between two SPI transfers
+ * @regulator_name: pointer to name of regulator
+ * @gpio_config: pointer to gpio configuration function
+ * @cap_button_map: pointer to 0d button map
+ */
+struct synaptics_dsx_board_data {
+ bool x_flip;
+ bool y_flip;
+ bool swap_axes;
+ int irq_gpio;
+ int power_gpio;
+ int power_on_state;
+ int reset_gpio;
+ int reset_on_state;
+ unsigned long irq_flags;
+ unsigned int panel_x;
+ unsigned int panel_y;
+ unsigned int power_delay_ms;
+ unsigned int reset_delay_ms;
+ unsigned int reset_active_ms;
+ unsigned int byte_delay_us;
+ unsigned int block_delay_us;
+ unsigned char *regulator_name;
+ int (*gpio_config)(int gpio, bool configure, int dir, int state);
+ struct synaptics_dsx_cap_button_map *cap_button_map;
+};
+
+#endif