summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/crypto/fsl-sec4.txt7
-rw-r--r--Documentation/devicetree/bindings/input/imx-sc-pwrkey.txt22
-rw-r--r--Documentation/devicetree/bindings/input/rpmsg-keys.txt33
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/focaltech-ts.txt48
-rw-r--r--Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt18
-rw-r--r--drivers/input/keyboard/Kconfig17
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/imx_sc_pwrkey.c201
-rw-r--r--drivers/input/keyboard/rpmsg-keys.c310
-rw-r--r--drivers/input/keyboard/snvs_pwrkey.c95
-rw-r--r--drivers/input/touchscreen/Kconfig28
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/ads7846.c8
-rw-r--r--drivers/input/touchscreen/egalax_ts.c59
-rw-r--r--drivers/input/touchscreen/elan_ts.c472
-rw-r--r--drivers/input/touchscreen/focaltech_touch/Kconfig16
-rw-r--r--drivers/input/touchscreen/focaltech_touch/Makefile13
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_common.h211
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_config.h219
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_core.c1439
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_core.h189
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c473
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c357
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c652
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c209
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c151
-rw-r--r--drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c311
-rw-r--r--drivers/input/touchscreen/goodix.c15
-rw-r--r--drivers/input/touchscreen/max11801_ts.c161
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/Kconfig27
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/Makefile2
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx.h86
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c3623
-rw-r--r--drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.h421
-rw-r--r--drivers/input/touchscreen/vtl/Makefile5
-rw-r--r--drivers/input/touchscreen/vtl/vtl_ts.c496
-rw-r--r--drivers/input/touchscreen/vtl/vtl_ts.h181
37 files changed, 10536 insertions, 45 deletions
diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
index 0843f6e666ba..c5dff19cd55d 100644
--- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
+++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
@@ -545,6 +545,13 @@ System ON/OFF key driver
Value type: <boo>
Definition: Button can wake-up the system.
+ - emulate-press:
+ Usage: option
+ Value type: <bool>
+ Definition: For ONOFF key only trigger interrupt after release but
+ not press like i.mx6q/dl/sl, emulate press event before
+ release.
+
- regmap:
Usage: required:
Value type: <phandle>
diff --git a/Documentation/devicetree/bindings/input/imx-sc-pwrkey.txt b/Documentation/devicetree/bindings/input/imx-sc-pwrkey.txt
new file mode 100644
index 000000000000..1084baa48d4f
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/imx-sc-pwrkey.txt
@@ -0,0 +1,22 @@
+Device-Tree bindings for input/keyboard/imx_sc_pwrkey.c poweron/off driver
+over SCU. On i.mx8QM/QXP poweron/off key is connected on SCU side, so need
+to get key event by MU.
+
+Required properties:
+ - compatible = "fsl,imx8-pwrkey";
+
+Each button/key looked as the sub node:
+Required properties:
+ - linux,code: the key value defined in
+ include/dt-bindings/input/input.h
+Optional property:
+ - wakeup-source: wakeup feature, the keys can wakeup from
+ suspend if the keys with this property pressed.
+
+Example nodes:
+ sc_pwrkey: sc-powerkey {
+ compatible = "fsl,imx8-pwrkey";
+ linux,keycode = <KEY_POWER>;
+ wakeup-source;
+ };
+
diff --git a/Documentation/devicetree/bindings/input/rpmsg-keys.txt b/Documentation/devicetree/bindings/input/rpmsg-keys.txt
new file mode 100644
index 000000000000..d9279802cc9c
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/rpmsg-keys.txt
@@ -0,0 +1,33 @@
+Device-Tree bindings for input/keyboard/rpmsg-keys.c keys driver over
+rpmsg. On i.mx7ULP keys are connected on M4 side, so rpmsg-keys driver
+needed to get the key status from M4 side by rpmsg.
+
+Required properties:
+ - compatible = "fsl,rpmsg-keys";
+
+Each button/key looked as the sub node:
+Required properties:
+ - label: the key name
+ - linux,code: the key value defined in
+ include/dt-bindings/input/input.h
+Optional property:
+ - rpmsg-key,wakeup: wakeup feature, the keys can wakeup from
+ suspend if the keys with this property pressed.
+
+Example nodes:
+ rpmsg_keys: rpmsg-keys {
+ compatible = "fsl,rpmsg-keys";
+
+ volume-up {
+ label = "Volume Up";
+ rpmsg-key,wakeup;
+ linux,code = <KEY_VOLUMEUP>;
+ };
+
+ volume-down {
+ label = "Volume Down";
+ rpmsg-key,wakeup;
+ linux,code = <KEY_VOLUMEDOWN>;
+ };
+ };
+
diff --git a/Documentation/devicetree/bindings/input/touchscreen/focaltech-ts.txt b/Documentation/devicetree/bindings/input/touchscreen/focaltech-ts.txt
new file mode 100644
index 000000000000..8e5257db88f6
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/focaltech-ts.txt
@@ -0,0 +1,48 @@
+FocalTech touch controller
+
+The focaltech controller is connected to host processor via i2c.
+The controller generates interrupts when the user touches the panel.
+The host controller is expected to read the touch coordinates over
+i2c and pass the coordinates to the rest of the system.
+
+Required properties:
+ - compatible : should be "focaltech,fts"
+ - reg : i2c slave address of the device, should be <0x38>
+ - interrupt-parent : parent of interrupt
+ - interrupts : irq gpio, "0x02" stands for that the irq triggered by falling edge.
+ - focaltech,irq-gpio : irq gpio, same as "interrupts" node.
+ - focaltech,reset-gpio : reset gpio
+ - focaltech,num-max-touches : maximum number of touches support
+ - focaltech,display-coords : display resolution in pixels. A four tuple consisting of minX, minY, maxX and maxY.
+
+Optional properties:
+ - focaltech,have-key : specify if virtual keys are supported
+ - focaltech,key-number : number of keys
+ - focaltech,keys : virtual key codes mapping to the coords
+ - focaltech,key-y-coord : constant y coordinate of keys, depends on the y resolution
+ - focaltech,key-x-coords : constant x coordinates of keys, depends on the x resolution
+ - focaltech,swap-xy : swap x-y coordinates
+ - focaltech,panel-type : set panel type, default is FT5416 panel
+ - focaltech,scaling-down-half : scale down the x-y coordiantes to half
+
+
+Example:
+ i2c@f9927000 {
+ focaltech@38{
+ compatible = "focaltech,fts";
+ reg = <0x38>;
+ interrupt-parent = <&msm_gpio>;
+ interrupts = <13 0x02>;
+ focaltech,reset-gpio = <&msm_gpio 12 0x01>;
+ focaltech,irq-gpio = <&msm_gpio 13 0x02>;
+ focaltech,max-touch-number = <5>;
+ focaltech,display-coords = <0 0 1080 1920>;
+
+ focaltech,have-key;
+ focaltech,key-number = <3>;
+ focaltech,keys = <139 102 158>;
+ focaltech,key-y-coord = <2000>;
+ focaltech,key-x-coords = <200 600 800>;
+ focaltech,swap-xy;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt
new file mode 100644
index 000000000000..a41a0b993006
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/vtl_ts.txt
@@ -0,0 +1,18 @@
+* VTL Touchscreen Controller
+
+Required properties:
+- compatible: must be "vtl,ct365"
+- reg: i2c slave address
+- interrupt-parent: the phandle for the interrupt controller
+- interrupts: touch controller interrupt
+- gpios: the gpio pin to be used for reset
+
+Example:
+
+ touchscreen@01 {
+ compatible = "vtl,ct365";
+ reg = <0x01>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <14 0>;
+ gpios = <&gpio4 10 0>;
+ };
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index e75650e98c9e..0f9f674fac6c 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -441,6 +441,16 @@ config KEYBOARD_MPR121
To compile this driver as a module, choose M here: the
module will be called mpr121_touchkey.
+config KEYBOARD_RPMSG
+ tristate "i.MX Rpmsg Keys Driver"
+ depends on RPMSG
+ depends on OF
+ help
+ This is rpmsg keys driver on i.mx7ulp, because some keys located
+ in M4 side.
+
+
+
config KEYBOARD_SNVS_PWRKEY
tristate "IMX SNVS Power Key Driver"
depends on ARCH_MXC || (COMPILE_TEST && HAS_IOMEM)
@@ -452,6 +462,13 @@ config KEYBOARD_SNVS_PWRKEY
To compile this driver as a module, choose M here; the
module will be called snvs_pwrkey.
+config KEYBOARD_IMX_SC_PWRKEY
+ tristate "IMX SC Power Key Driver"
+ depends on IMX_SCU
+ help
+ This is the virtual snvs powerkey driver for NXP i.mx8Q/QXP family
+ whose SCU hold snvs inside.
+
config KEYBOARD_IMX
tristate "IMX keypad support"
depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 1d689fdd5c00..f64191201d35 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -56,9 +56,11 @@ obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
obj-$(CONFIG_KEYBOARD_QT1050) += qt1050.o
obj-$(CONFIG_KEYBOARD_QT1070) += qt1070.o
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o
+obj-$(CONFIG_KEYBOARD_RPMSG) += rpmsg-keys.o
obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY) += snvs_pwrkey.o
+obj-$(CONFIG_KEYBOARD_IMX_SC_PWRKEY) += imx_sc_pwrkey.o
obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
diff --git a/drivers/input/keyboard/imx_sc_pwrkey.c b/drivers/input/keyboard/imx_sc_pwrkey.c
new file mode 100644
index 000000000000..a5c2a3d4c6a0
--- /dev/null
+++ b/drivers/input/keyboard/imx_sc_pwrkey.c
@@ -0,0 +1,201 @@
+/*
+ * Driver for the IMX SNVS ON/OFF Power Key over sc api
+ * Copyright (C) 2017 NXP. All Rights Reserved.
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/firmware/imx/sci.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+
+#define DEBOUNCE_TIME 100
+#define REPEAT_INTERVAL 60
+
+#define SC_IRQ_BUTTON (1U << 0U) /* Button interrupt */
+#define SC_IRQ_GROUP_WAKE 3U /* Wakeup interrupts */
+
+#define IMX_SC_MISC_FUNC_GET_BUTTON_STATUS 18U
+
+struct pwrkey_drv_data {
+ int keycode;
+ bool keystate; /* 1: pressed, 0: release */
+ bool delay_check;
+ struct imx_sc_ipc *ipcHandle;
+ int wakeup;
+ struct delayed_work check_work;
+ struct input_dev *input;
+};
+
+struct imx_sc_msg_pwrkey {
+ struct imx_sc_rpc_msg hdr;
+ u32 state;
+};
+
+static struct pwrkey_drv_data *pdata;
+
+static int imx_sc_pwrkey_notify(struct notifier_block *nb,
+ unsigned long event, void *group)
+{
+ /* ignore other irqs */
+ if (!(pdata && pdata->ipcHandle && (event & SC_IRQ_BUTTON) &&
+ (*(u8 *)group == SC_IRQ_GROUP_WAKE)))
+ return 0;
+
+ if (!pdata->delay_check) {
+ pdata->delay_check = 1;
+ schedule_delayed_work(&pdata->check_work,
+ msecs_to_jiffies(REPEAT_INTERVAL));
+ }
+
+ return 0;
+}
+
+static void imx_sc_check_for_events(struct work_struct *work)
+{
+ struct input_dev *input = pdata->input;
+ struct imx_sc_msg_pwrkey msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ bool state;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_MISC;
+ hdr->func = IMX_SC_MISC_FUNC_GET_BUTTON_STATUS;
+ hdr->size = 1;
+
+ /*
+ * Current SCU firmware does NOT have return value for
+ * this API, that means it is always successful.
+ */
+ imx_scu_call_rpc(pdata->ipcHandle, &msg, true);
+
+ /* Only care the least 1 byte */
+ state = (bool)(msg.state & 0xff);
+ /*
+ * restore status back if press interrupt received but pin's status
+ * released, which caused by pressing so quickly.
+ */
+ if (!state && !pdata->keystate)
+ state = true;
+
+ if (state ^ pdata->keystate) {
+ pm_wakeup_event(input->dev.parent, 0);
+ pdata->keystate = !!state;
+ input_event(input, EV_KEY, pdata->keycode, !!state);
+ input_sync(input);
+ if (!state)
+ pdata->delay_check = 0;
+ pm_relax(pdata->input->dev.parent);
+ }
+ /* repeat check if pressed long */
+ if (state)
+ schedule_delayed_work(&pdata->check_work,
+ msecs_to_jiffies(DEBOUNCE_TIME));
+}
+
+static struct notifier_block imx_sc_pwrkey_notifier = {
+ .notifier_call = imx_sc_pwrkey_notify,
+};
+
+static int imx_sc_pwrkey_probe(struct platform_device *pdev)
+{
+ struct input_dev *input = NULL;
+ struct device_node *np = pdev->dev.of_node;
+ int error;
+ int ret;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ ret = imx_scu_get_handle(&pdata->ipcHandle);
+ if (ret)
+ return ret;
+
+ if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) {
+ pdata->keycode = KEY_POWER;
+ dev_warn(&pdev->dev, "KEY_POWER without setting in dts\n");
+ }
+
+ INIT_DELAYED_WORK(&pdata->check_work, imx_sc_check_for_events);
+
+ pdata->wakeup = of_property_read_bool(np, "wakeup-source");
+
+ input = devm_input_allocate_device(&pdev->dev);
+
+ if (!input) {
+ dev_err(&pdev->dev, "failed to allocate the input device\n");
+ return -ENOMEM;
+ }
+
+ input->name = pdev->name;
+ input->phys = "imx-sc-pwrkey/input0";
+ input->id.bustype = BUS_HOST;
+
+ input_set_capability(input, EV_KEY, pdata->keycode);
+
+ error = input_register_device(input);
+ if (error < 0) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ return error;
+ }
+
+ pdata->input = input;
+ platform_set_drvdata(pdev, pdata);
+
+ device_init_wakeup(&pdev->dev, !!(pdata->wakeup));
+
+
+ ret = imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE,
+ SC_IRQ_BUTTON, true);
+ if (ret) {
+ dev_warn(&pdev->dev, "Enable irq failed.\n");
+ return ret;
+ }
+
+ ret = imx_scu_irq_register_notifier(&imx_sc_pwrkey_notifier);
+ if (ret) {
+ imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE,
+ SC_IRQ_BUTTON, false);
+ dev_warn(&pdev->dev, "reqister scu notifier failed.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id imx_sc_pwrkey_ids[] = {
+ { .compatible = "fsl,imx8-pwrkey" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_sc_pwrkey_ids);
+
+static struct platform_driver imx_sc_pwrkey_driver = {
+ .driver = {
+ .name = "imx8-pwrkey",
+ .of_match_table = imx_sc_pwrkey_ids,
+ },
+ .probe = imx_sc_pwrkey_probe,
+};
+module_platform_driver(imx_sc_pwrkey_driver);
+
+MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>");
+MODULE_DESCRIPTION("i.MX8 power key driver based on scu");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/rpmsg-keys.c b/drivers/input/keyboard/rpmsg-keys.c
new file mode 100644
index 000000000000..d438f7d28b47
--- /dev/null
+++ b/drivers/input/keyboard/rpmsg-keys.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * 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/err.h>
+#include <linux/slab.h>
+#include <linux/imx_rpmsg.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
+#include <linux/rpmsg.h>
+#include <linux/uaccess.h>
+#include <linux/virtio.h>
+
+#define RPMSG_TIMEOUT 1000
+
+enum key_cmd_type {
+ KEY_RPMSG_SETUP,
+ KEY_RPMSG_REPLY,
+ KEY_RPMSG_NOTIFY,
+};
+
+enum keys_type {
+ KEY_PRESS = 1,
+ KEY_RELEASE,
+ KEY_BOTH,
+};
+
+struct key_rpmsg_data {
+ struct imx_rpmsg_head header;
+ u8 key_index;
+ union {
+ u8 event;
+ u8 retcode;
+ };
+ u8 wakeup;
+} __attribute__((packed));
+
+struct rpmsg_keys_button {
+ unsigned int code;
+ enum keys_type type;
+ int wakeup;
+ struct input_dev *input;
+};
+
+struct rpmsg_keys_drvdata {
+ struct input_dev *input;
+ struct rpmsg_device *rpdev;
+ struct device *dev;
+ struct key_rpmsg_data *msg;
+ bool ack;
+ struct pm_qos_request pm_qos_req;
+ struct delayed_work keysetup_work;
+ struct completion cmd_complete;
+ int nbuttons;
+ struct rpmsg_keys_button buttons[0];
+};
+
+static struct rpmsg_keys_drvdata *keys_rpmsg;
+
+static int key_send_message(struct key_rpmsg_data *msg,
+ struct rpmsg_keys_drvdata *info, bool ack)
+{
+ int err;
+
+ if (!info->rpdev) {
+ dev_dbg(info->dev,
+ "rpmsg channel not ready, m4 image ready?\n");
+ return -EINVAL;
+ }
+
+ cpu_latency_qos_add_request(&info->pm_qos_req,
+ 0);
+
+ if (ack) {
+ info->ack = true;
+ reinit_completion(&info->cmd_complete);
+ }
+
+ err = rpmsg_send(info->rpdev->ept, (void *)msg,
+ sizeof(struct key_rpmsg_data));
+ if (err) {
+ dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
+ goto err_out;
+ }
+
+ if (ack) {
+ err = wait_for_completion_timeout(&info->cmd_complete,
+ msecs_to_jiffies(RPMSG_TIMEOUT));
+ if (!err) {
+ dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
+ err = -ETIMEDOUT;
+ goto err_out;
+ }
+
+ if (info->msg->retcode != 0) {
+ dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n",
+ info->msg->retcode);
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ err = 0;
+ }
+
+err_out:
+ info->ack = true;
+ cpu_latency_qos_remove_request(&info->pm_qos_req);
+
+ return err;
+}
+
+static int keys_rpmsg_cb(struct rpmsg_device *rpdev,
+ void *data, int len, void *priv, u32 src)
+{
+ struct key_rpmsg_data *msg = (struct key_rpmsg_data *)data;
+
+ if (msg->header.type == KEY_RPMSG_REPLY) {
+ keys_rpmsg->msg = msg;
+ complete(&keys_rpmsg->cmd_complete);
+ return 0;
+ } else if (msg->header.type == KEY_RPMSG_NOTIFY) {
+ keys_rpmsg->msg = msg;
+ keys_rpmsg->ack = false;
+ } else
+ dev_err(&keys_rpmsg->rpdev->dev, "wrong command type!\n");
+
+ input_event(keys_rpmsg->input, EV_KEY, msg->key_index, msg->event);
+ input_sync(keys_rpmsg->input);
+
+ return 0;
+}
+
+static void keys_init_handler(struct work_struct *work)
+{
+ struct key_rpmsg_data msg;
+ int i;
+
+ /* setup keys */
+ for (i = 0; i < keys_rpmsg->nbuttons; i++) {
+ struct rpmsg_keys_button *button = &keys_rpmsg->buttons[i];
+
+ msg.header.cate = IMX_RPMSG_KEY;
+ msg.header.major = IMX_RMPSG_MAJOR;
+ msg.header.minor = IMX_RMPSG_MINOR;
+ msg.header.type = KEY_RPMSG_SETUP;
+ msg.header.cmd = 0;
+ msg.key_index = button->code;
+ msg.wakeup = button->wakeup;
+ msg.event = button->type;
+ if (key_send_message(&msg, keys_rpmsg, true))
+ dev_err(&keys_rpmsg->rpdev->dev,
+ "key %d setup failed!\n", button->code);
+ }
+}
+
+static int keys_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+ keys_rpmsg->rpdev = rpdev;
+
+ dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+ rpdev->src, rpdev->dst);
+
+ init_completion(&keys_rpmsg->cmd_complete);
+
+ INIT_DELAYED_WORK(&keys_rpmsg->keysetup_work,
+ keys_init_handler);
+ schedule_delayed_work(&keys_rpmsg->keysetup_work,
+ msecs_to_jiffies(100));
+
+ return 0;
+}
+
+static struct rpmsg_device_id keys_rpmsg_id_table[] = {
+ { .name = "rpmsg-keypad-channel" },
+ { },
+};
+
+static struct rpmsg_driver keys_rpmsg_driver = {
+ .drv.name = "key_rpmsg",
+ .drv.owner = THIS_MODULE,
+ .id_table = keys_rpmsg_id_table,
+ .probe = keys_rpmsg_probe,
+ .callback = keys_rpmsg_cb,
+};
+
+static struct rpmsg_keys_drvdata *
+rpmsg_keys_get_devtree_pdata(struct device *dev)
+{
+ struct device_node *node, *pp;
+ struct rpmsg_keys_drvdata *ddata;
+ struct rpmsg_keys_button *button;
+ int nbuttons;
+ int i;
+
+ node = dev->of_node;
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ nbuttons = of_get_child_count(node);
+ if (nbuttons == 0)
+ return ERR_PTR(-ENODEV);
+
+ ddata = devm_kzalloc(dev,
+ sizeof(*ddata) + nbuttons *
+ sizeof(struct rpmsg_keys_button),
+ GFP_KERNEL);
+ if (!ddata)
+ return ERR_PTR(-ENOMEM);
+
+ ddata->nbuttons = nbuttons;
+
+ i = 0;
+ for_each_child_of_node(node, pp) {
+ button = &ddata->buttons[i++];
+
+ if (of_property_read_u32(pp, "linux,code", &button->code)) {
+ dev_err(dev, "Button without keycode: 0x%x\n",
+ button->code);
+ return ERR_PTR(-EINVAL);
+ }
+
+ button->wakeup = !!of_get_property(pp, "rpmsg-key,wakeup",
+ NULL);
+ button->type = KEY_BOTH;
+ }
+
+ return ddata;
+}
+
+static int rpmsg_keys_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rpmsg_keys_drvdata *ddata;
+ int i, error;
+ struct input_dev *input;
+
+ ddata = rpmsg_keys_get_devtree_pdata(dev);
+ if (IS_ERR(ddata))
+ return PTR_ERR(ddata);
+
+ input = devm_input_allocate_device(dev);
+ if (!input) {
+ dev_err(dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ ddata->input = input;
+
+ keys_rpmsg = ddata;
+ platform_set_drvdata(pdev, ddata);
+
+ input->name = pdev->name;
+ input->phys = "rpmsg-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+
+ for (i = 0; i < ddata->nbuttons; i++) {
+ struct rpmsg_keys_button *button = &ddata->buttons[i];
+
+ input_set_capability(input, EV_KEY, button->code);
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(dev, "Unable to register input device, error: %d\n",
+ error);
+ goto err_out;
+ }
+
+ return register_rpmsg_driver(&keys_rpmsg_driver);
+err_out:
+ return error;
+}
+
+static const struct of_device_id rpmsg_keys_of_match[] = {
+ { .compatible = "fsl,rpmsg-keys", },
+ { },
+};
+
+MODULE_DEVICE_TABLE(of, rpmsg_keys_of_match);
+
+static struct platform_driver rpmsg_keys_device_driver = {
+ .probe = rpmsg_keys_probe,
+ .driver = {
+ .name = "rpmsg-keys",
+ .of_match_table = of_match_ptr(rpmsg_keys_of_match)
+ }
+};
+
+module_platform_driver(rpmsg_keys_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>");
+MODULE_DESCRIPTION("Keyboard driver based on rpmsg");
diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c
index 65286762b02a..6949b30f1974 100644
--- a/drivers/input/keyboard/snvs_pwrkey.c
+++ b/drivers/input/keyboard/snvs_pwrkey.c
@@ -20,7 +20,6 @@
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
-#define SNVS_HPVIDR1_REG 0xF8
#define SNVS_LPSR_REG 0x4C /* LP Status Register */
#define SNVS_LPCR_REG 0x38 /* LP Control Register */
#define SNVS_HPSR_REG 0x14
@@ -37,9 +36,11 @@ struct pwrkey_drv_data {
int keycode;
int keystate; /* 1:pressed */
int wakeup;
+ bool suspended;
+ struct clk *clk;
struct timer_list check_timer;
struct input_dev *input;
- u8 minor_rev;
+ bool emulate_press;
};
static void imx_imx_snvs_check_for_events(struct timer_list *t)
@@ -48,7 +49,22 @@ static void imx_imx_snvs_check_for_events(struct timer_list *t)
struct input_dev *input = pdata->input;
u32 state;
+ if (pdata->clk) {
+ if (pdata->suspended)
+ clk_prepare_enable(pdata->clk);
+ else
+ clk_enable(pdata->clk);
+ }
+
regmap_read(pdata->snvs, SNVS_HPSR_REG, &state);
+
+ if (pdata->clk) {
+ if (pdata->suspended)
+ clk_disable_unprepare(pdata->clk);
+ else
+ clk_disable(pdata->clk);
+ }
+
state = state & SNVS_HPSR_BTN ? 1 : 0;
/* only report new event if status changed */
@@ -75,9 +91,18 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
pm_wakeup_event(input->dev.parent, 0);
+ if (pdata->clk)
+ clk_enable(pdata->clk);
+
+ if (pdata->suspended) {
+ pdata->keystate = 1;
+ input_event(input, EV_KEY, pdata->keycode, 1);
+ input_sync(input);
+ }
+
regmap_read(pdata->snvs, SNVS_LPSR_REG, &lp_status);
if (lp_status & SNVS_LPSR_SPO) {
- if (pdata->minor_rev == 0) {
+ if (pdata->emulate_press) {
/*
* The first generation i.MX6 SoCs only sends an
* interrupt on button release. To mimic power-key
@@ -97,6 +122,9 @@ static irqreturn_t imx_snvs_pwrkey_interrupt(int irq, void *dev_id)
/* clear SPO status */
regmap_write(pdata->snvs, SNVS_LPSR_REG, SNVS_LPSR_SPO);
+ if (pdata->clk)
+ clk_disable(pdata->clk);
+
return IRQ_HANDLED;
}
@@ -119,7 +147,6 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
struct device_node *np;
struct clk *clk;
int error;
- u32 vid;
/* Get SNVS register Page */
np = pdev->dev.of_node;
@@ -169,8 +196,19 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
if (pdata->irq < 0)
return -EINVAL;
- regmap_read(pdata->snvs, SNVS_HPVIDR1_REG, &vid);
- pdata->minor_rev = vid & 0xff;
+ pdata->emulate_press = of_property_read_bool(np, "emulate-press");
+
+ pdata->clk = devm_clk_get(&pdev->dev, "snvs");
+ if (IS_ERR(pdata->clk)) {
+ pdata->clk = NULL;
+ } else {
+ error = clk_prepare_enable(pdata->clk);
+ if (error) {
+ dev_err(&pdev->dev,
+ "Could not enable the snvs clock\n");
+ return error;
+ }
+ }
regmap_update_bits(pdata->snvs, SNVS_LPCR_REG, SNVS_LPCR_DEP_EN, SNVS_LPCR_DEP_EN);
@@ -182,7 +220,8 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
input = devm_input_allocate_device(&pdev->dev);
if (!input) {
dev_err(&pdev->dev, "failed to allocate the input device\n");
- return -ENOMEM;
+ error = -ENOMEM;
+ goto error_probe;
}
input->name = pdev->name;
@@ -195,7 +234,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
error = devm_add_action(&pdev->dev, imx_snvs_pwrkey_act, pdata);
if (error) {
dev_err(&pdev->dev, "failed to register remove action\n");
- return error;
+ goto error_probe;
}
pdata->input = input;
@@ -207,13 +246,13 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
if (error) {
dev_err(&pdev->dev, "interrupt not available.\n");
- return error;
+ goto error_probe;
}
error = input_register_device(input);
if (error < 0) {
dev_err(&pdev->dev, "failed to register input device\n");
- return error;
+ goto error_probe;
}
device_init_wakeup(&pdev->dev, pdata->wakeup);
@@ -222,8 +261,43 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "irq wake enable failed.\n");
return 0;
+
+error_probe:
+ if (pdata->clk)
+ clk_disable_unprepare(pdata->clk);
+
+ return error;
+}
+
+static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->clk)
+ clk_disable_unprepare(pdata->clk);
+
+ pdata->suspended = true;
+
+ return 0;
}
+static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev);
+
+ if (pdata->clk)
+ clk_prepare_enable(pdata->clk);
+
+ pdata->suspended = false;
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx_snvs_pwrkey_pm_ops, imx_snvs_pwrkey_suspend,
+ imx_snvs_pwrkey_resume);
+
static const struct of_device_id imx_snvs_pwrkey_ids[] = {
{ .compatible = "fsl,sec-v4.0-pwrkey" },
{ /* sentinel */ }
@@ -233,6 +307,7 @@ MODULE_DEVICE_TABLE(of, imx_snvs_pwrkey_ids);
static struct platform_driver imx_snvs_pwrkey_driver = {
.driver = {
.name = "snvs_pwrkey",
+ .pm = &imx_snvs_pwrkey_pm_ops,
.of_match_table = imx_snvs_pwrkey_ids,
},
.probe = imx_snvs_pwrkey_probe,
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index d4e74738c5a8..f9d7cd3be6a8 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -43,6 +43,19 @@ config TOUCHSCREEN_ADS7846
To compile this driver as a module, choose M here: the
module will be called ads7846.
+config TOUCHSCREEN_CT36X_WLD
+ default y
+ tristate "CT36X based touchscreens for WLD"
+ help
+ Say Y here if you have a touchscreen interface using the
+ CT36X controller, i2c touchscreen
+ controller.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called vtl_ts.
+
config TOUCHSCREEN_AD7877
tristate "AD7877 based touchscreens"
depends on SPI_MASTER
@@ -372,6 +385,18 @@ config TOUCHSCREEN_EXC3000
To compile this driver as a module, choose M here: the
module will be called exc3000.
+config TOUCHSCREEN_ELAN_TS
+ tristate "ELAN touchscreen input driver"
+ depends on I2C
+ help
+ Say Y here if you have an I2C ELAN touchscreen
+ attached.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called elan-touch.
+
config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen"
select SERIO
@@ -1366,4 +1391,7 @@ config TOUCHSCREEN_ZINITIX
To compile this driver as a module, choose M here: the
module will be called zinitix.
+source "drivers/input/touchscreen/synaptics_dsx/Kconfig"
endif
+
+source "drivers/input/touchscreen/focaltech_touch/Kconfig"
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 7d34100f7f22..4dd7f23991fd 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o
+obj-$(CONFIG_TOUCHSCREEN_CT36X_WLD) += vtl/
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
@@ -40,9 +41,12 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_EKTF2127) += ektf2127.o
obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+obj-$(CONFIG_TOUCHSCREEN_ELAN_TS) += elan_ts.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_touch/
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX) += synaptics_dsx/
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index f113a27aeb1e..b87e9e2fdd0d 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -1141,6 +1141,7 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
struct device_node *node = dev->of_node;
const struct of_device_id *match;
u32 value;
+ int ret;
if (!node) {
dev_err(dev, "Device does not have associated DT data\n");
@@ -1194,8 +1195,11 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
of_property_read_u16(node, "ti,debounce-tol", &pdata->debounce_tol);
of_property_read_u16(node, "ti,debounce-rep", &pdata->debounce_rep);
- of_property_read_u32(node, "ti,pendown-gpio-debounce",
+ ret = of_property_read_u32(node, "ti,pendown-gpio-debounce",
&pdata->gpio_pendown_debounce);
+ if (!ret)
+ dev_info(dev, "Set pendown gpio debounce time to %d microseconds\n",
+ pdata->gpio_pendown_debounce);
pdata->wakeup = of_property_read_bool(node, "wakeup-source") ||
of_property_read_bool(node, "linux,wakeup");
@@ -1324,7 +1328,7 @@ static int ads7846_probe(struct spi_device *spi)
pdata->y_max ? : MAX_12BIT,
0, 0);
input_set_abs_params(input_dev, ABS_PRESSURE,
- pdata->pressure_min, pdata->pressure_max, 0, 0);
+ pdata->pressure_min, ts->pressure_max, 0, 0);
/*
* Parse common framework properties. Must be done here to ensure the
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
index 83ac8c128192..4935024e57a4 100644
--- a/drivers/input/touchscreen/egalax_ts.c
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -116,6 +116,26 @@ static irqreturn_t egalax_ts_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
+static int egalax_irq_request(struct egalax_ts *ts)
+{
+ int ret;
+ struct i2c_client *client = ts->client;
+
+ ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+ egalax_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "egalax_ts", ts);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register interrupt\n");
+
+ return ret;
+}
+
+static void egalax_free_irq(struct egalax_ts *ts)
+{
+ devm_free_irq(&ts->client->dev, ts->client->irq, ts);
+}
+
/* wake up controller by an falling edge of interrupt gpio. */
static int egalax_wake_up_device(struct i2c_client *client)
{
@@ -183,6 +203,20 @@ static int egalax_ts_probe(struct i2c_client *client,
ts->client = client;
ts->input_dev = input_dev;
+ /* HannStar (HSD100PXN1 Rev: 1-A00C11 F/W:0634) LVDS touch
+ * screen needs to trigger I2C event to device FW at booting
+ * first, and then the FW can switch to I2C interface.
+ * Otherwise, the FW can’t work with I2C interface. So here
+ * just use the exist function egalax_firmware_version() to
+ * send a I2C command to the device, make sure the device FW
+ * switch to I2C interface.
+ */
+ error = egalax_firmware_version(client);
+ if (error) {
+ dev_err(&client->dev, "Failed to switch to I2C interface\n");
+ return error;
+ }
+
/* controller may be in sleep, wake it up. */
error = egalax_wake_up_device(client);
if (error) {
@@ -211,19 +245,16 @@ static int egalax_ts_probe(struct i2c_client *client,
ABS_MT_POSITION_Y, 0, EGALAX_MAX_Y, 0, 0);
input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
- error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
- egalax_ts_interrupt,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "egalax_ts", ts);
- if (error < 0) {
- dev_err(&client->dev, "Failed to register interrupt\n");
+ error = egalax_irq_request(ts);
+ if (error)
return error;
- }
error = input_register_device(ts->input_dev);
if (error)
return error;
+ i2c_set_clientdata(client, ts);
+
return 0;
}
@@ -239,11 +270,10 @@ static int __maybe_unused egalax_ts_suspend(struct device *dev)
0x3, 0x6, 0xa, 0x3, 0x36, 0x3f, 0x2, 0, 0, 0
};
struct i2c_client *client = to_i2c_client(dev);
+ struct egalax_ts *ts = i2c_get_clientdata(client);
int ret;
- if (device_may_wakeup(dev))
- return enable_irq_wake(client->irq);
-
+ egalax_free_irq(ts);
ret = i2c_master_send(client, suspend_cmd, MAX_I2C_DATA_LEN);
return ret > 0 ? 0 : ret;
}
@@ -251,11 +281,14 @@ static int __maybe_unused egalax_ts_suspend(struct device *dev)
static int __maybe_unused egalax_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
+ struct egalax_ts *ts = i2c_get_clientdata(client);
+ int ret;
- if (device_may_wakeup(dev))
- return disable_irq_wake(client->irq);
+ ret = egalax_wake_up_device(client);
+ if (!ret)
+ ret = egalax_irq_request(ts);
- return egalax_wake_up_device(client);
+ return ret;
}
static SIMPLE_DEV_PM_OPS(egalax_ts_pm_ops, egalax_ts_suspend, egalax_ts_resume);
diff --git a/drivers/input/touchscreen/elan_ts.c b/drivers/input/touchscreen/elan_ts.c
new file mode 100644
index 000000000000..8590a947b624
--- /dev/null
+++ b/drivers/input/touchscreen/elan_ts.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2007-2008 HTC Corporation.
+ *
+ * Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
+ *
+ * This driver is adapted from elan8232_i2c.c written by Shan-Fu Chiou
+ * <sfchiou@gmail.com> and Jay Tu <jay_tu@htc.com>.
+ * This driver is also adapted from the ELAN Touch Screen driver
+ * written by Stanley Zeng <stanley.zeng@emc.com.tw>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/input.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio.h>
+
+static const char ELAN_TS_NAME[] = "elan-touch";
+
+#define ELAN_TS_X_MAX 1088
+#define ELAN_TS_Y_MAX 768
+#define ELAN_USER_X_MAX 800
+#define ELAN_USER_Y_MAX 600
+#define IDX_PACKET_SIZE 8
+
+enum {
+ hello_packet = 0x55,
+ idx_coordinate_packet = 0x5a,
+};
+
+enum {
+ idx_finger_state = 7,
+};
+
+static struct workqueue_struct *elan_wq;
+
+static struct elan_data {
+ int intr_gpio;
+ int use_irq;
+ struct hrtimer timer;
+ struct work_struct work;
+ struct i2c_client *client;
+ struct input_dev *input;
+ wait_queue_head_t wait;
+} elan_touch_data;
+
+/*--------------------------------------------------------------*/
+static int elan_touch_detect_int_level(void)
+{
+ int v;
+ v = gpio_get_value(elan_touch_data.intr_gpio);
+
+ return v;
+}
+
+static int __elan_touch_poll(struct i2c_client *client)
+{
+ int status = 0, retry = 20;
+
+ do {
+ status = elan_touch_detect_int_level();
+ retry--;
+ mdelay(20);
+ } while (status == 1 && retry > 0);
+
+ return status == 0 ? 0 : -ETIMEDOUT;
+}
+
+static int elan_touch_poll(struct i2c_client *client)
+{
+ return __elan_touch_poll(client);
+}
+
+static int __hello_packet_handler(struct i2c_client *client)
+{
+ int rc;
+ uint8_t buf_recv[4] = { 0 };
+
+ rc = elan_touch_poll(client);
+
+ if (rc < 0)
+ return -EINVAL;
+
+ rc = i2c_master_recv(client, buf_recv, 4);
+
+ if (rc != 4) {
+ return rc;
+ } else {
+ int i;
+ pr_info("hello packet: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
+ buf_recv[0], buf_recv[1], buf_recv[2], buf_recv[3]);
+
+ for (i = 0; i < 4; i++)
+ if (buf_recv[i] != hello_packet)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static inline int elan_touch_parse_xy(uint8_t *data, uint16_t *x,
+ uint16_t *y)
+{
+ *x = (data[0] & 0xf0);
+ *x <<= 4;
+ *x |= data[1];
+ if (*x >= ELAN_TS_X_MAX)
+ *x = ELAN_TS_X_MAX;
+ *x = ((((ELAN_TS_X_MAX -
+ *x) * 1000) / ELAN_TS_X_MAX) * ELAN_USER_X_MAX) / 1000;
+
+ *y = (data[0] & 0x0f);
+ *y <<= 8;
+ *y |= data[2];
+ if (*y >= ELAN_TS_Y_MAX)
+ *y = ELAN_TS_Y_MAX;
+ *y = ((((ELAN_TS_Y_MAX -
+ *y) * 1000) / ELAN_TS_Y_MAX) * ELAN_USER_Y_MAX) / 1000;
+
+ return 0;
+}
+
+/* __elan_touch_init -- hand shaking with touch panel
+ *
+ * 1.recv hello packet
+ */
+static int __elan_touch_init(struct i2c_client *client)
+{
+ int rc;
+ rc = __hello_packet_handler(client);
+ if (rc < 0)
+ goto hand_shake_failed;
+
+hand_shake_failed:
+ return rc;
+}
+
+static int elan_touch_recv_data(struct i2c_client *client, uint8_t *buf)
+{
+ int rc, bytes_to_recv = IDX_PACKET_SIZE;
+
+ if (buf == NULL)
+ return -EINVAL;
+
+ memset(buf, 0, bytes_to_recv);
+ rc = i2c_master_recv(client, buf, bytes_to_recv);
+ if (rc != bytes_to_recv)
+ return -EINVAL;
+
+ return rc;
+}
+
+static void elan_touch_report_data(struct i2c_client *client, uint8_t *buf)
+{
+ switch (buf[0]) {
+ case idx_coordinate_packet:
+ {
+ uint16_t x1, x2, y1, y2;
+ uint8_t finger_stat;
+
+ finger_stat = (buf[idx_finger_state] & 0x06) >> 1;
+
+ if (finger_stat == 0) {
+ input_report_key(elan_touch_data.input, BTN_TOUCH, 0);
+ input_report_key(elan_touch_data.input, BTN_2, 0);
+ } else if (finger_stat == 1) {
+ elan_touch_parse_xy(&buf[1], &x1, &y1);
+ input_report_abs(elan_touch_data.input, ABS_X, x1);
+ input_report_abs(elan_touch_data.input, ABS_Y, y1);
+ input_report_key(elan_touch_data.input, BTN_TOUCH, 1);
+ input_report_key(elan_touch_data.input, BTN_2, 0);
+ } else if (finger_stat == 2) {
+ elan_touch_parse_xy(&buf[1], &x1, &y1);
+ input_report_abs(elan_touch_data.input, ABS_X, x1);
+ input_report_abs(elan_touch_data.input, ABS_Y, y1);
+ input_report_key(elan_touch_data.input, BTN_TOUCH, 1);
+ elan_touch_parse_xy(&buf[4], &x2, &y2);
+ input_report_abs(elan_touch_data.input, ABS_HAT0X, x2);
+ input_report_abs(elan_touch_data.input, ABS_HAT0Y, y2);
+ input_report_key(elan_touch_data.input, BTN_2, 1);
+ }
+ input_sync(elan_touch_data.input);
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+static void elan_touch_work_func(struct work_struct *work)
+{
+ int rc;
+ uint8_t buf[IDX_PACKET_SIZE] = { 0 };
+ struct i2c_client *client = elan_touch_data.client;
+
+ if (elan_touch_detect_int_level())
+ return;
+
+ rc = elan_touch_recv_data(client, buf);
+ if (rc < 0)
+ return;
+
+ elan_touch_report_data(client, buf);
+}
+
+static irqreturn_t elan_touch_ts_interrupt(int irq, void *dev_id)
+{
+ queue_work(elan_wq, &elan_touch_data.work);
+
+ return IRQ_HANDLED;
+}
+
+static enum hrtimer_restart elan_touch_timer_func(struct hrtimer *timer)
+{
+ queue_work(elan_wq, &elan_touch_data.work);
+ hrtimer_start(&elan_touch_data.timer, ktime_set(0, 12500000),
+ HRTIMER_MODE_REL);
+
+ return HRTIMER_NORESTART;
+}
+
+static int elan_touch_register_interrupt(struct i2c_client *client)
+{
+ int err = 0;
+
+ if (client->irq) {
+ elan_touch_data.use_irq = 1;
+ err =
+ request_irq(client->irq, elan_touch_ts_interrupt,
+ IRQF_TRIGGER_FALLING, ELAN_TS_NAME,
+ &elan_touch_data);
+
+ if (err < 0) {
+ pr_info("%s(%s): Can't allocate irq %d\n", __FILE__,
+ __func__, client->irq);
+ elan_touch_data.use_irq = 0;
+ }
+ }
+
+ if (!elan_touch_data.use_irq) {
+ hrtimer_init(&elan_touch_data.timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ elan_touch_data.timer.function = elan_touch_timer_func;
+ hrtimer_start(&elan_touch_data.timer, ktime_set(1, 0),
+ HRTIMER_MODE_REL);
+ }
+
+ pr_info("elan ts starts in %s mode.\n",
+ elan_touch_data.use_irq == 1 ? "interrupt" : "polling");
+
+ return 0;
+}
+
+static int elan_touch_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device_node *np = client->dev.of_node;
+ int gpio_elan_cs, gpio_elan_rst, err = 0;
+
+ if (!np)
+ return -ENODEV;
+
+ elan_touch_data.intr_gpio = of_get_named_gpio(np, "gpio_intr", 0);
+ if (!gpio_is_valid(elan_touch_data.intr_gpio))
+ return -ENODEV;
+
+ err = devm_gpio_request_one(&client->dev, elan_touch_data.intr_gpio,
+ GPIOF_IN, "gpio_elan_intr");
+ if (err < 0) {
+ dev_err(&client->dev,
+ "request gpio failed: %d\n", err);
+ return err;
+ }
+
+ /* elan touch init */
+ gpio_elan_cs = of_get_named_gpio(np, "gpio_elan_cs", 0);
+ if (!gpio_is_valid(gpio_elan_cs))
+ return -ENODEV;
+
+ err = devm_gpio_request_one(&client->dev, gpio_elan_cs,
+ GPIOF_OUT_INIT_HIGH, "gpio_elan_cs");
+ if (err < 0) {
+ dev_err(&client->dev,
+ "request gpio failed: %d\n", err);
+ return err;
+ }
+ gpio_set_value(gpio_elan_cs, 0);
+
+ gpio_elan_rst = of_get_named_gpio(np, "gpio_elan_rst", 0);
+ if (!gpio_is_valid(gpio_elan_rst))
+ return -ENODEV;
+
+ err = devm_gpio_request_one(&client->dev, gpio_elan_rst,
+ GPIOF_OUT_INIT_HIGH, "gpio_elan_rst");
+ if (err < 0) {
+ dev_err(&client->dev,
+ "request gpio failed: %d\n", err);
+ return err;
+ }
+ gpio_set_value(gpio_elan_rst, 0);
+ msleep(10);
+ gpio_set_value(gpio_elan_rst, 1);
+
+ gpio_set_value(gpio_elan_cs, 1);
+ msleep(100);
+
+ elan_wq = create_singlethread_workqueue("elan_wq");
+ if (!elan_wq) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ elan_touch_data.client = client;
+ strlcpy(client->name, ELAN_TS_NAME, I2C_NAME_SIZE);
+
+ INIT_WORK(&elan_touch_data.work, elan_touch_work_func);
+
+ elan_touch_data.input = input_allocate_device();
+ if (elan_touch_data.input == NULL) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ err = __elan_touch_init(client);
+ if (err < 0) {
+ dev_err(&client->dev, "elan - Read Hello Packet Failed\n");
+ goto fail;
+ }
+
+ elan_touch_data.input->name = ELAN_TS_NAME;
+ elan_touch_data.input->id.bustype = BUS_I2C;
+
+ set_bit(EV_SYN, elan_touch_data.input->evbit);
+
+ set_bit(EV_KEY, elan_touch_data.input->evbit);
+ set_bit(BTN_TOUCH, elan_touch_data.input->keybit);
+ set_bit(BTN_2, elan_touch_data.input->keybit);
+
+ set_bit(EV_ABS, elan_touch_data.input->evbit);
+ set_bit(ABS_X, elan_touch_data.input->absbit);
+ set_bit(ABS_Y, elan_touch_data.input->absbit);
+ set_bit(ABS_HAT0X, elan_touch_data.input->absbit);
+ set_bit(ABS_HAT0Y, elan_touch_data.input->absbit);
+
+ input_set_abs_params(elan_touch_data.input, ABS_X, 0, ELAN_USER_X_MAX,
+ 0, 0);
+ input_set_abs_params(elan_touch_data.input, ABS_Y, 0, ELAN_USER_Y_MAX,
+ 0, 0);
+ input_set_abs_params(elan_touch_data.input, ABS_HAT0X, 0,
+ ELAN_USER_X_MAX, 0, 0);
+ input_set_abs_params(elan_touch_data.input, ABS_HAT0Y, 0,
+ ELAN_USER_Y_MAX, 0, 0);
+
+ err = input_register_device(elan_touch_data.input);
+ if (err < 0)
+ goto fail;
+
+ elan_touch_register_interrupt(elan_touch_data.client);
+
+ return 0;
+
+fail:
+ input_free_device(elan_touch_data.input);
+ if (elan_wq)
+ destroy_workqueue(elan_wq);
+ return err;
+}
+
+static int elan_touch_remove(struct i2c_client *client)
+{
+ if (elan_wq)
+ destroy_workqueue(elan_wq);
+
+ input_unregister_device(elan_touch_data.input);
+
+ if (elan_touch_data.use_irq)
+ free_irq(client->irq, client);
+ else
+ hrtimer_cancel(&elan_touch_data.timer);
+ return 0;
+}
+
+/* -------------------------------------------------------------------- */
+static const struct i2c_device_id elan_touch_id[] = {
+ {"elan-touch", 0},
+ {}
+};
+
+static const struct of_device_id elan_dt_ids[] = {
+ {
+ .compatible = "elan,elan-touch",
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, elan_dt_ids);
+
+static int elan_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int elan_resume(struct device *dev)
+{
+ uint8_t buf[IDX_PACKET_SIZE] = { 0 };
+
+ if (0 == elan_touch_detect_int_level()) {
+ dev_dbg(dev, "Got touch during suspend period.\n");
+ /*
+ * if touch screen during suspend, recv and drop the
+ * data, then touch interrupt pin will return high after
+ * receving data.
+ */
+ elan_touch_recv_data(elan_touch_data.client, buf);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops elan_dev_pm_ops = {
+ .suspend = elan_suspend,
+ .resume = elan_resume,
+};
+
+static struct i2c_driver elan_touch_driver = {
+ .probe = elan_touch_probe,
+ .remove = elan_touch_remove,
+ .id_table = elan_touch_id,
+ .driver = {
+ .name = "elan-touch",
+ .owner = THIS_MODULE,
+ .of_match_table = elan_dt_ids,
+#ifdef CONFIG_PM
+ .pm = &elan_dev_pm_ops,
+#endif
+ },
+};
+
+static int __init elan_touch_init(void)
+{
+ return i2c_add_driver(&elan_touch_driver);
+}
+
+static void __exit elan_touch_exit(void)
+{
+ i2c_del_driver(&elan_touch_driver);
+}
+
+module_init(elan_touch_init);
+module_exit(elan_touch_exit);
+
+MODULE_AUTHOR("Stanley Zeng <stanley.zeng@emc.com.tw>");
+MODULE_DESCRIPTION("ELAN Touch Screen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/focaltech_touch/Kconfig b/drivers/input/touchscreen/focaltech_touch/Kconfig
new file mode 100644
index 000000000000..e1ec3b91202e
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/Kconfig
@@ -0,0 +1,16 @@
+#
+# Focaltech Touchscreen driver configuration
+#
+
+config TOUCHSCREEN_FTS
+ bool "Focaltech Touchscreen"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have Focaltech touch panel.
+ If unsure, say N.
+
+config TOUCHSCREEN_FTS_DIRECTORY
+ string "Focaltech ts directory name"
+ default "focaltech_touch"
+ depends on TOUCHSCREEN_FTS
diff --git a/drivers/input/touchscreen/focaltech_touch/Makefile b/drivers/input/touchscreen/focaltech_touch/Makefile
new file mode 100644
index 000000000000..1b2b8ec8acd4
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the focaltech touchscreen drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_sensor.o
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_common.h b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h
new file mode 100644
index 000000000000..ae4807c7b700
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_common.h
@@ -0,0 +1,211 @@
+/*
+ *
+ * FocalTech fts TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_common.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-16
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_COMMON_H__
+#define __LINUX_FOCALTECH_COMMON_H__
+
+#include "focaltech_config.h"
+
+/*****************************************************************************
+* Macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_VERSION "Focaltech V1.2 20161229"
+
+#define FLAGBIT(x) (0x00000001 << (x))
+#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) << (x))
+
+#define FLAG_ICSERIALS_LEN 5
+#define FLAG_IDC_BIT 11
+
+#define FTS_CHIP_TYPE_MAPPING { \
+ {0x01, 0x58, 0x22, 0x58, 0x22, 0x00, 0x00, 0x58, 0x2C}, \
+ {0x02, 0x54, 0x22, 0x54, 0x22, 0x00, 0x00, 0x54, 0x2C}, \
+ {0x03, 0x64, 0x26, 0x64, 0x26, 0x00, 0x00, 0x79, 0x1C}, \
+ {0x04, 0x33, 0x67, 0x64, 0x26, 0x00, 0x00, 0x79, 0x1C}, \
+ {0x05, 0x87, 0x16, 0x87, 0x16, 0x87, 0xA6, 0x00, 0x00}, \
+ {0x06, 0x87, 0x36, 0x87, 0x36, 0x87, 0xC6, 0x00, 0x00}, \
+ {0x07, 0x80, 0x06, 0x80, 0x06, 0x80, 0xC6, 0x80, 0xB6}, \
+ {0x08, 0x86, 0x06, 0x86, 0x06, 0x86, 0xA6, 0x00, 0x00}, \
+ {0x09, 0x86, 0x07, 0x86, 0x07, 0x86, 0xA7, 0x00, 0x00}, \
+ {0x0A, 0xE7, 0x16, 0x87, 0x16, 0xE7, 0xA6, 0x87, 0xB6}, \
+}
+
+#define I2C_BUFFER_LENGTH_MAXINUM 256
+#define FILE_NAME_LENGTH 128
+#define ENABLE 1
+#define DISABLE 0
+/*register address*/
+#define FTS_REG_INT_CNT 0x8F
+#define FTS_REG_FLOW_WORK_CNT 0x91
+#define FTS_REG_WORKMODE 0x00
+#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40
+#define FTS_REG_WORKMODE_WORK_VALUE 0x00
+#define FTS_REG_CHIP_ID 0xA3
+#define FTS_REG_CHIP_ID2 0x9F
+#define FTS_REG_POWER_MODE 0xA5
+#define FTS_REG_POWER_MODE_SLEEP_VALUE 0x03
+#define FTS_REG_FW_VER 0xA6
+#define FTS_REG_VENDOR_ID 0xA8
+#define FTS_REG_LCD_BUSY_NUM 0xAB
+#define FTS_REG_FACE_DEC_MODE_EN 0xB0
+#define FTS_REG_GLOVE_MODE_EN 0xC0
+#define FTS_REG_COVER_MODE_EN 0xC1
+#define FTS_REG_CHARGER_MODE_EN 0x8B
+#define FTS_REG_GESTURE_EN 0xD0
+#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3
+#define FTS_REG_ESD_SATURATE 0xED
+
+/*****************************************************************************
+* Alternative mode (When something goes wrong, the modules may be able to solve the problem.)
+*****************************************************************************/
+/*
+ * point report check
+ * default: disable
+ */
+#define FTS_POINT_REPORT_CHECK_EN 0
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+struct ft_chip_t {
+ unsigned long type;
+ unsigned char chip_idh;
+ unsigned char chip_idl;
+ unsigned char rom_idh;
+ unsigned char rom_idl;
+ unsigned char pramboot_idh;
+ unsigned char pramboot_idl;
+ unsigned char bootloader_idh;
+ unsigned char bootloader_idl;
+};
+
+/* i2c communication*/
+int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue);
+int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue);
+int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen,
+ char *readbuf, int readlen);
+int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen);
+int fts_i2c_init(void);
+int fts_i2c_exit(void);
+
+/* Gesture functions */
+#if FTS_GESTURE_EN
+int fts_gesture_init(struct input_dev *input_dev, struct i2c_client *client);
+int fts_gesture_exit(struct i2c_client *client);
+void fts_gesture_recovery(struct i2c_client *client);
+int fts_gesture_readdata(struct i2c_client *client);
+int fts_gesture_suspend(struct i2c_client *i2c_client);
+int fts_gesture_resume(struct i2c_client *client);
+#endif
+
+/* Apk and functions */
+#if FTS_APK_NODE_EN
+int fts_create_apk_debug_channel(struct i2c_client *client);
+void fts_release_apk_debug_channel(void);
+#endif
+
+/* ADB functions */
+#if FTS_SYSFS_NODE_EN
+int fts_create_sysfs(struct i2c_client *client);
+int fts_remove_sysfs(struct i2c_client *client);
+#endif
+
+/* ESD */
+#if FTS_ESDCHECK_EN
+int fts_esdcheck_init(void);
+int fts_esdcheck_exit(void);
+int fts_esdcheck_switch(bool enable);
+int fts_esdcheck_proc_busy(bool proc_debug);
+int fts_esdcheck_set_intr(bool intr);
+int fts_esdcheck_suspend(void);
+int fts_esdcheck_resume(void);
+int fts_esdcheck_get_status(void);
+#endif
+
+/* Production test */
+#if FTS_TEST_EN
+int fts_test_init(struct i2c_client *client);
+int fts_test_exit(struct i2c_client *client);
+#endif
+
+/* Point Report Check*/
+#if FTS_POINT_REPORT_CHECK_EN
+int fts_point_report_check_init(void);
+int fts_point_report_check_exit(void);
+void fts_point_report_check_queue_work(void);
+#endif
+
+/* Other */
+extern int g_show_log;
+int fts_reset_proc(int hdelayms);
+int fts_wait_tp_to_valid(struct i2c_client *client);
+void fts_tp_state_recovery(struct i2c_client *client);
+int fts_ex_mode_init(struct i2c_client *client);
+int fts_ex_mode_exit(struct i2c_client *client);
+int fts_ex_mode_recovery(struct i2c_client *client);
+
+void fts_irq_disable(void);
+void fts_irq_enable(void);
+
+/*****************************************************************************
+* DEBUG function define here
+*****************************************************************************/
+#if FTS_DEBUG_EN
+#define FTS_DEBUG_LEVEL 1
+
+#if (FTS_DEBUG_LEVEL == 2)
+#define FTS_DEBUG(fmt, args...) \
+ printk(KERN_ERR "[FTS][%s]"fmt"\n", __func__, ##args)
+#else
+#define FTS_DEBUG(fmt, args...) \
+ printk(KERN_ERR "[FTS]"fmt"\n", ##args)
+#endif
+
+#define FTS_FUNC_ENTER() \
+ printk(KERN_ERR "[FTS]%s: Enter\n", __func__)
+#define FTS_FUNC_EXIT() \
+ printk(KERN_ERR "[FTS]%s: Exit(%d)\n", __func__, __LINE__)
+#else
+#define FTS_DEBUG(fmt, args...)
+#define FTS_FUNC_ENTER()
+#define FTS_FUNC_EXIT()
+#endif
+
+#define FTS_INFO(fmt, args...) do { \
+ if (g_show_log) \
+ printk(KERN_ERR "[FTS][Info]"fmt"\n", ##args); \
+ } while (0)
+
+#define FTS_ERROR(fmt, args...) do { \
+ if (g_show_log) \
+ printk(KERN_ERR "[FTS][Error]"fmt"\n", ##args); \
+ } while (0)
+
+#endif /* __LINUX_FOCALTECH_COMMON_H__ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_config.h b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h
new file mode 100644
index 000000000000..87b622e77942
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_config.h
@@ -0,0 +1,219 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/************************************************************************
+*
+* File Name: focaltech_config.h
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract: global configurations
+*
+* Version: v1.0
+*
+************************************************************************/
+#ifndef _LINUX_FOCLATECH_CONFIG_H_
+#define _LINUX_FOCLATECH_CONFIG_H_
+
+/**************************************************/
+/****** G: A, I: B, S: C, U: D ******************/
+/****** chip type defines, do not modify *********/
+#define _FT8716 0x87160805
+#define _FT8736 0x87360806
+#define _FT8006 0x80060807
+#define _FT8606 0x86060808
+#define _FT8607 0x86070809
+#define _FTE716 0xE716080a
+
+#define _FT5416 0x54160002
+#define _FT5426 0x54260002
+#define _FT5435 0x54350002
+#define _FT5436 0x54360002
+#define _FT5526 0x55260002
+#define _FT5526I 0x5526B002
+#define _FT5446 0x54460002
+#define _FT5346 0x53460002
+#define _FT5446I 0x5446B002
+#define _FT5346I 0x5346B002
+#define _FT7661 0x76610002
+#define _FT7511 0x75110002
+#define _FT7421 0x74210002
+#define _FT7681 0x76810002
+#define _FT3C47U 0x3C47D002
+#define _FT3417 0x34170002
+#define _FT3517 0x35170002
+#define _FT3327 0x33270002
+#define _FT3427 0x34270002
+
+#define _FT5626 0x56260001
+#define _FT5726 0x57260001
+#define _FT5826B 0x5826B001
+#define _FT5826S 0x5826C001
+#define _FT7811 0x78110001
+#define _FT3D47 0x3D470001
+#define _FT3617 0x36170001
+#define _FT3717 0x37170001
+#define _FT3817B 0x3817B001
+
+#define _FT6236U 0x6236D003
+#define _FT6336G 0x6336A003
+#define _FT6336U 0x6336D003
+#define _FT6436U 0x6436D003
+
+#define _FT3267 0x32670004
+#define _FT3367 0x33670004
+
+/******************* Enables *********************/
+/*********** 1 to enable, 0 to disable ***********/
+
+/*
+ * show debug log info
+ * enable it for debug, disable it for release
+ */
+#define FTS_DEBUG_EN 0
+
+/*
+ * Linux MultiTouch Protocol
+ * 1: Protocol B(default), 0: Protocol A
+ */
+#define FTS_MT_PROTOCOL_B_EN 1
+
+/*
+ * Report Pressure in multitouch
+ * 1:enable(default),0:disable
+*/
+#define FTS_REPORT_PRESSURE_EN 1
+
+/*
+ * Force touch support
+ * different pressure for multitouch
+ * 1: true pressure for force touch
+ * 0: constant pressure(default)
+ */
+#define FTS_FORCE_TOUCH_EN 0
+
+/*
+ * Gesture function enable
+ * default: disable
+ */
+#define FTS_GESTURE_EN 0
+
+/*
+ * ESD check & protection
+ * default: disable
+ */
+#define FTS_ESDCHECK_EN 0
+
+/*
+ * Production test enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_TEST_EN 0
+
+/*
+ * Glove mode enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_GLOVE_EN 0
+/*
+ * cover enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_COVER_EN 0
+/*
+ * Charger enable
+ * 1: enable, 0:disable(default)
+ */
+#define FTS_CHARGER_EN 0
+
+/*
+ * Proximity sensor
+ * default: disable
+ */
+#define FTS_PSENSOR_EN 0
+
+/*
+ * Nodes for tools, please keep enable
+ */
+#define FTS_SYSFS_NODE_EN 1
+#define FTS_APK_NODE_EN 1
+
+/*
+ * Customer power enable
+ * enable it when customer need control TP power
+ * default: disable
+ */
+#define FTS_POWER_SOURCE_CUST_EN 0
+
+/****************************************************/
+
+/********************** Upgrade ****************************/
+/*
+ * auto upgrade, please keep enable
+ */
+#define FTS_AUTO_UPGRADE_EN 1
+
+/*
+ * auto upgrade for lcd cfg
+ * default: 0
+ */
+#define FTS_AUTO_UPGRADE_FOR_LCD_CFG_EN 0
+
+/* auto cb check
+ * default: disable
+ */
+#define FTS_AUTO_CLB_EN 0
+
+/*
+ * FW_APP.i file for upgrade
+ * define your own fw_app, the sample one is invalid
+ */
+#define FTS_UPGRADE_FW_APP "include/firmware/FT8716_app_sample.i"
+
+/*
+ * lcd_cfg.i file for lcd cfg upgrade
+ * define your own lcd_cfg.i, the sample one is invalid
+ */
+#define FTS_UPGRADE_LCD_CFG "include/firmware/lcd_cfg.i"
+
+/* get vedor id from flash
+ * default: enable
+ */
+#define FTS_GET_VENDOR_ID 0
+
+/*
+ * vendor_id(s) for the ic
+ * you need confirm vendor_id for upgrade
+ * if only one vendor, ignore vendor_2_id, otherwise
+ * you need define both of them
+ */
+#define FTS_VENDOR_1_ID 0x8d
+#define FTS_VENDOR_2_ID 0x8d
+
+/*
+ * upgrade stress test for debug
+ * enable it for upgrade debug if needed
+ * default: disable
+ */
+#define FTS_UPGRADE_STRESS_TEST 0
+/* stress test times, default: 1000 */
+#define FTS_UPGRADE_TEST_NUMBER 1000
+
+/*********************************************************/
+
+#endif /* _LINUX_FOCLATECH_CONFIG_H_ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
new file mode 100644
index 000000000000..3c180d2189b7
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.c
@@ -0,0 +1,1439 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */
+#endif
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define FTS_DRIVER_NAME "fts_ts"
+#define INTERVAL_READ_REG 20
+#define TIMEOUT_READ_REG 300
+#if FTS_POWER_SOURCE_CUST_EN
+#define FTS_VTG_MIN_UV 2600000
+#define FTS_VTG_MAX_UV 3300000
+#define FTS_I2C_VTG_MIN_UV 1800000
+#define FTS_I2C_VTG_MAX_UV 1800000
+#endif
+#define FTS_READ_TOUCH_BUFFER_DIVIDED 0
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+******************************************************************************/
+struct i2c_client *fts_i2c_client;
+struct fts_ts_data *fts_wq_data;
+struct input_dev *fts_input_dev;
+
+#if FTS_DEBUG_EN
+int g_show_log = 1;
+#else
+int g_show_log;
+#endif
+
+#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
+char g_sz_debug[1024] = { 0 };
+#endif
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static void fts_release_all_finger(void);
+static int fts_ts_suspend(struct device *dev);
+static int fts_ts_resume(struct device *dev);
+
+static bool fts_chip_idc(struct fts_ts_data *data)
+{
+ return ((data->pdata->fts_chip_type & FLAGBIT(FLAG_IDC_BIT)) ==
+ FLAGBIT(FLAG_IDC_BIT));
+}
+
+/*****************************************************************************
+* Name: fts_wait_tp_to_valid
+* Brief: Read chip id until TP FW become valid,
+* need call when reset/power on/resume...
+* 1. Read Chip ID per INTERVAL_READ_REG(20ms)
+* 2. Timeout: TIMEOUT_READ_REG(300ms)
+* Input:
+* Output:
+* Return: 0 - Get correct Device ID
+*****************************************************************************/
+int fts_wait_tp_to_valid(struct i2c_client *client)
+{
+ int ret = 0;
+ int cnt = 0;
+ u8 reg_value = 0;
+
+ do {
+ ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &reg_value);
+ if (ret < 0)
+ FTS_INFO("TP Not Ready, ReadData = 0x%x", reg_value);
+ else {
+ FTS_INFO("TP Ready, Device ID = 0x%x", reg_value);
+ return 0;
+ }
+ cnt++;
+ msleep(INTERVAL_READ_REG);
+ } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG);
+
+ /* error: not get correct reg data */
+ return -1;
+}
+
+/*****************************************************************************
+* Name: fts_recover_state
+* Brief: Need execute this function when reset
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+void fts_tp_state_recovery(struct i2c_client *client)
+{
+ /* wait tp stable */
+ fts_wait_tp_to_valid(client);
+ /* recover TP charger state 0x8B */
+ /* recover TP glove state 0xC0 */
+ /* recover TP cover state 0xC1 */
+ fts_ex_mode_recovery(client);
+ /* recover TP gesture state 0xD0 */
+#if FTS_GESTURE_EN
+ fts_gesture_recovery(client);
+#endif
+
+ fts_release_all_finger();
+}
+
+/*****************************************************************************
+* Name: fts_reset_proc
+* Brief: Execute reset operation
+* Input: hdelayms - delay time unit:ms
+* Output:
+* Return:
+*****************************************************************************/
+int fts_reset_proc(int hdelayms)
+{
+ gpio_direction_output(fts_wq_data->pdata->reset_gpio, 0);
+ msleep(20);
+ gpio_direction_output(fts_wq_data->pdata->reset_gpio, 1);
+ msleep(hdelayms);
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_irq_disable
+* Brief: disable irq
+* Input:
+* sync:
+* Output:
+* Return:
+*****************************************************************************/
+void fts_irq_disable(void)
+{
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&fts_wq_data->irq_lock, irqflags);
+
+ if (!fts_wq_data->irq_disable) {
+ disable_irq_nosync(fts_wq_data->client->irq);
+ fts_wq_data->irq_disable = 1;
+ }
+
+ spin_unlock_irqrestore(&fts_wq_data->irq_lock, irqflags);
+}
+
+/*****************************************************************************
+* Name: fts_irq_enable
+* Brief: enable irq
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+void fts_irq_enable(void)
+{
+ unsigned long irqflags = 0;
+
+ spin_lock_irqsave(&fts_wq_data->irq_lock, irqflags);
+
+ if (fts_wq_data->irq_disable) {
+ enable_irq(fts_wq_data->client->irq);
+ fts_wq_data->irq_disable = 0;
+ }
+
+ spin_unlock_irqrestore(&fts_wq_data->irq_lock, irqflags);
+}
+
+/*****************************************************************************
+* Name: fts_input_dev_init
+* Brief: input dev init
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_input_dev_init(struct i2c_client *client,
+ struct fts_ts_data *data,
+ struct input_dev *input_dev,
+ struct fts_ts_platform_data *pdata)
+{
+ int err, len;
+
+ FTS_FUNC_ENTER();
+
+ /* Init and register Input device */
+ input_dev->name = FTS_DRIVER_NAME;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+
+ input_set_drvdata(input_dev, data);
+ i2c_set_clientdata(client, data);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ if (data->pdata->have_key) {
+ FTS_DEBUG("set key capabilities");
+ for (len = 0; len < data->pdata->key_number; len++) {
+ input_set_capability(input_dev, EV_KEY,
+ data->pdata->keys[len]);
+ }
+ }
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+#if FTS_MT_PROTOCOL_B_EN
+ input_mt_init_slots(input_dev, pdata->max_touch_number,
+ INPUT_MT_DIRECT);
+#else
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, 0x0f, 0, 0);
+#endif
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, pdata->x_min,
+ pdata->x_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, pdata->y_min,
+ pdata->y_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 0xFF, 0, 0);
+#if FTS_REPORT_PRESSURE_EN
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 0xFF, 0, 0);
+#endif
+
+ err = input_register_device(input_dev);
+ if (err) {
+ FTS_ERROR("Input device registration failed");
+ goto free_inputdev;
+ }
+
+ FTS_FUNC_EXIT();
+
+ return 0;
+
+free_inputdev:
+ input_free_device(input_dev);
+ FTS_FUNC_EXIT();
+ return err;
+
+}
+
+/*****************************************************************************
+* Power Control
+*****************************************************************************/
+#if FTS_POWER_SOURCE_CUST_EN
+static int fts_power_source_init(struct fts_ts_data *data)
+{
+ int rc;
+
+ FTS_FUNC_ENTER();
+
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ rc = PTR_ERR(data->vdd);
+ FTS_ERROR("Regulator get failed vdd rc=%d", rc);
+ }
+
+ if (regulator_count_voltages(data->vdd) > 0) {
+ rc = regulator_set_voltage(data->vdd, FTS_VTG_MIN_UV,
+ FTS_VTG_MAX_UV);
+ if (rc) {
+ FTS_ERROR("Regulator set_vtg failed vdd rc=%d", rc);
+ goto reg_vdd_put;
+ }
+ }
+
+ data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c");
+ if (IS_ERR(data->vcc_i2c)) {
+ rc = PTR_ERR(data->vcc_i2c);
+ FTS_ERROR("Regulator get failed vcc_i2c rc=%d", rc);
+ goto reg_vdd_set_vtg;
+ }
+
+ if (regulator_count_voltages(data->vcc_i2c) > 0) {
+ rc = regulator_set_voltage(data->vcc_i2c, FTS_I2C_VTG_MIN_UV,
+ FTS_I2C_VTG_MAX_UV);
+ if (rc) {
+ FTS_ERROR("Regulator set_vtg failed vcc_i2c rc=%d", rc);
+ goto reg_vcc_i2c_put;
+ }
+ }
+
+ FTS_FUNC_EXIT();
+ return 0;
+
+reg_vcc_i2c_put:
+ regulator_put(data->vcc_i2c);
+reg_vdd_set_vtg:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, FTS_VTG_MAX_UV);
+reg_vdd_put:
+ regulator_put(data->vdd);
+ FTS_FUNC_EXIT();
+ return rc;
+}
+
+static int fts_power_source_ctrl(struct fts_ts_data *data, int enable)
+{
+ int rc;
+
+ FTS_FUNC_ENTER();
+ if (enable) {
+ rc = regulator_enable(data->vdd);
+ if (rc)
+ FTS_ERROR("Regulator vdd enable failed rc=%d", rc);
+
+ rc = regulator_enable(data->vcc_i2c);
+ if (rc)
+ FTS_ERROR("Regulator vcc_i2c enable failed rc=%d", rc);
+ } else {
+ rc = regulator_disable(data->vdd);
+ if (rc)
+ FTS_ERROR("Regulator vdd disable failed rc=%d", rc);
+ rc = regulator_disable(data->vcc_i2c);
+ if (rc)
+ FTS_ERROR("Regulator vcc_i2c disable failed rc=%d", rc);
+ }
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+#endif
+
+/*****************************************************************************
+* Reprot related
+*****************************************************************************/
+/*****************************************************************************
+* Name: fts_release_all_finger
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_release_all_finger(void)
+{
+#if FTS_MT_PROTOCOL_B_EN
+ unsigned int finger_count = 0;
+
+ for (finger_count = 0;
+ finger_count < fts_wq_data->pdata->max_touch_number;
+ finger_count++) {
+ input_mt_slot(fts_input_dev, finger_count);
+ input_mt_report_slot_state(fts_input_dev, MT_TOOL_FINGER,
+ false);
+ }
+#else
+ input_mt_sync(fts_input_dev);
+#endif
+ input_report_key(fts_input_dev, BTN_TOUCH, 0);
+ input_sync(fts_input_dev);
+}
+
+#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
+static void fts_show_touch_buffer(u8 *buf, int point_num)
+{
+ int len = point_num * FTS_ONE_TCH_LEN;
+ int count = 0;
+ int i;
+
+ memset(g_sz_debug, 0, 1024);
+ if (len > (POINT_READ_BUF - 3))
+ len = POINT_READ_BUF - 3;
+ else if (len == 0) {
+ len += FTS_ONE_TCH_LEN;
+
+ count += sprintf(g_sz_debug, "%02X,%02X,%02X", buf[0], buf[1], buf[2]);
+ for (i = 0; i < len; i++)
+ count += sprintf(g_sz_debug + count, ",%02X", buf[i + 3]);
+
+ FTS_DEBUG("buffer: %s", g_sz_debug);
+}
+#endif
+
+static int fts_input_dev_report_key_event(struct ts_event *event,
+ struct fts_ts_data *data)
+{
+ int i;
+
+ if (data->pdata->have_key) {
+ if ((1 == event->touch_point || 1 == event->point_num)
+ && (event->au16_y[0] == data->pdata->key_y_coord)) {
+
+ if (event->point_num == 0) {
+ FTS_DEBUG("Keys All Up!");
+ for (i = 0; i < data->pdata->key_number; i++) {
+ input_report_key(data->input_dev,
+ data->pdata->keys[i],
+ 0);
+ }
+ } else {
+ for (i = 0; i < data->pdata->key_number; i++) {
+ if (event->au16_x[0] >
+ (data->pdata->key_x_coords[i] -
+ FTS_KEY_WIDTH)
+ && event->au16_x[0] <
+ (data->pdata->key_x_coords[i] +
+ FTS_KEY_WIDTH)) {
+
+ if (event->au8_touch_event[i] ==
+ 0
+ || event->au8_touch_event[i]
+ == 2) {
+ input_report_key
+ (data->input_dev,
+ data->pdata->keys
+ [i], 1);
+ FTS_DEBUG
+ ("Key%d(%d, %d) DOWN!",
+ i,
+ event->au16_x[0],
+ event->au16_y[0]);
+ } else {
+ input_report_key
+ (data->input_dev,
+ data->pdata->keys
+ [i], 0);
+ FTS_DEBUG
+ ("Key%d(%d, %d) Up!",
+ i,
+ event->au16_x[0],
+ event->au16_y[0]);
+ }
+ break;
+ }
+ }
+ }
+ input_sync(data->input_dev);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+#if FTS_MT_PROTOCOL_B_EN
+static int fts_input_dev_report_b(struct ts_event *event,
+ struct fts_ts_data *data)
+{
+ int i = 0;
+ int uppoint = 0;
+ int touchs = 0;
+
+ for (i = 0; i < event->touch_point; i++) {
+ input_mt_slot(data->input_dev, event->au8_finger_id[i]);
+
+ if (event->au8_touch_event[i] == FTS_TOUCH_DOWN
+ || event->au8_touch_event[i] == FTS_TOUCH_CONTACT) {
+ input_mt_report_slot_state(data->input_dev,
+ MT_TOOL_FINGER, true);
+
+#if FTS_REPORT_PRESSURE_EN
+#if FTS_FORCE_TOUCH_EN
+ if (event->pressure[i] <= 0) {
+ FTS_ERROR("[B]Illegal pressure: %d",
+ event->pressure[i]);
+ event->pressure[i] = 1;
+ }
+#else
+ event->pressure[i] = 0x3f;
+#endif
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ event->pressure[i]);
+#endif
+
+ if (event->area[i] <= 0) {
+ FTS_ERROR("[B]Illegal touch-major: %d",
+ event->area[i]);
+ event->area[i] = 1;
+ }
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ event->area[i]);
+
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ event->au16_x[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ event->au16_y[i]);
+ touchs |= BIT(event->au8_finger_id[i]);
+ data->touchs |= BIT(event->au8_finger_id[i]);
+
+#if FTS_REPORT_PRESSURE_EN
+ FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+ event->au8_finger_id[i], event->au16_x[i],
+ event->au16_y[i], event->pressure[i],
+ event->area[i]);
+#else
+ FTS_DEBUG("[B]P%d(%d, %d)[tm:%d] DOWN!",
+ event->au8_finger_id[i], event->au16_x[i],
+ event->au16_y[i], event->area[i]);
+#endif
+ } else {
+ uppoint++;
+ input_mt_report_slot_state(data->input_dev,
+ MT_TOOL_FINGER, false);
+#if FTS_REPORT_PRESSURE_EN
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE, 0);
+#endif
+ data->touchs &= ~BIT(event->au8_finger_id[i]);
+ FTS_DEBUG("[B]P%d UP!", event->au8_finger_id[i]);
+ }
+ }
+
+ if (unlikely(data->touchs ^ touchs)) {
+ for (i = 0; i < data->pdata->max_touch_number; i++) {
+ if (BIT(i) & (data->touchs ^ touchs)) {
+ FTS_DEBUG("[B]P%d UP!", i);
+ input_mt_slot(data->input_dev, i);
+ input_mt_report_slot_state(data->input_dev,
+ MT_TOOL_FINGER,
+ false);
+#if FTS_REPORT_PRESSURE_EN
+ input_report_abs(data->input_dev,
+ ABS_MT_PRESSURE, 0);
+#endif
+ }
+ }
+ }
+
+ data->touchs = touchs;
+ if (event->touch_point == uppoint) {
+ FTS_DEBUG("Points All Up!");
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ } else {
+ input_report_key(data->input_dev, BTN_TOUCH,
+ event->touch_point > 0);
+ }
+
+ input_sync(data->input_dev);
+
+ return 0;
+
+}
+
+#else
+static int fts_input_dev_report_a(struct ts_event *event,
+ struct fts_ts_data *data)
+{
+ int i = 0;
+ int uppoint = 0;
+ int touchs = 0;
+
+ for (i = 0; i < event->touch_point; i++) {
+
+ if (event->au8_touch_event[i] == FTS_TOUCH_DOWN
+ || event->au8_touch_event[i] == FTS_TOUCH_CONTACT) {
+ input_report_abs(data->input_dev, ABS_MT_TRACKING_ID,
+ event->au8_finger_id[i]);
+#if FTS_REPORT_PRESSURE_EN
+#if FTS_FORCE_TOUCH_EN
+ if (event->pressure[i] <= 0) {
+ FTS_ERROR("[B]Illegal pressure: %d",
+ event->pressure[i]);
+ event->pressure[i] = 1;
+ }
+#else
+ event->pressure[i] = 0x3f;
+#endif
+ input_report_abs(data->input_dev, ABS_MT_PRESSURE,
+ event->pressure[i]);
+#endif
+
+ if (event->area[i] <= 0) {
+ FTS_ERROR("[B]Illegal touch-major: %d",
+ event->area[i]);
+ event->area[i] = 1;
+ }
+ input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR,
+ event->area[i]);
+
+ input_report_abs(data->input_dev, ABS_MT_POSITION_X,
+ event->au16_x[i]);
+ input_report_abs(data->input_dev, ABS_MT_POSITION_Y,
+ event->au16_y[i]);
+
+ input_mt_sync(data->input_dev);
+
+#if FTS_REPORT_PRESSURE_EN
+ FTS_DEBUG("[B]P%d(%d, %d)[p:%d,tm:%d] DOWN!",
+ event->au8_finger_id[i], event->au16_x[i],
+ event->au16_y[i], event->pressure[i],
+ event->area[i]);
+#else
+ FTS_DEBUG("[B]P%d(%d, %d)[tm:%d] DOWN!",
+ event->au8_finger_id[i], event->au16_x[i],
+ event->au16_y[i], event->area[i]);
+#endif
+ } else {
+ uppoint++;
+ }
+ }
+
+ data->touchs = touchs;
+ if (event->touch_point == uppoint) {
+ FTS_DEBUG("Points All Up!");
+ input_report_key(data->input_dev, BTN_TOUCH, 0);
+ input_mt_sync(data->input_dev);
+ } else {
+ input_report_key(data->input_dev, BTN_TOUCH,
+ event->touch_point > 0);
+ }
+
+ input_sync(data->input_dev);
+
+ return 0;
+}
+#endif
+
+/*****************************************************************************
+* Name: fts_read_touchdata
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_read_touchdata(struct fts_ts_data *data)
+{
+ u8 buf[POINT_READ_BUF] = { 0 };
+ u8 pointid = FTS_MAX_ID;
+ int ret = -1;
+ int i;
+ struct ts_event *event = &(data->event);
+
+#if FTS_GESTURE_EN
+ {
+ u8 state;
+
+ if (data->suspended) {
+ fts_i2c_read_reg(data->client, FTS_REG_GESTURE_EN,
+ &state);
+ if (state == 1) {
+ fts_gesture_readdata(data->client);
+ return 1;
+ }
+ }
+ }
+#endif
+
+#if FTS_PSENSOR_EN
+ if ((fts_sensor_read_data(data) != 0) && (data->suspended == 1))
+ return 1;
+#endif
+
+#if FTS_READ_TOUCH_BUFFER_DIVIDED
+ memset(buf, 0xFF, POINT_READ_BUF);
+ memset(event, 0, sizeof(struct ts_event));
+
+ buf[0] = 0x00;
+ ret = fts_i2c_read(data->client, buf, 1, buf, (3 + FTS_ONE_TCH_LEN));
+ if (ret < 0) {
+ FTS_ERROR("%s read touchdata failed.", __func__);
+ return ret;
+ }
+ event->touch_point = 0;
+ event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
+ if (event->point_num > data->pdata->max_touch_number)
+ event->point_num = data->pdata->max_touch_number;
+
+ if (event->point_num > 1) {
+ buf[9] = 0x09;
+ fts_i2c_read(data->client, buf + 9, 1, buf + 9,
+ (event->point_num - 1) * FTS_ONE_TCH_LEN);
+ }
+#else
+ ret = fts_i2c_read(data->client, buf, 1, buf, POINT_READ_BUF);
+ if (ret < 0) {
+ FTS_ERROR("[B]Read touchdata failed, ret: %d", ret);
+ return ret;
+ }
+#if FTS_POINT_REPORT_CHECK_EN
+ fts_point_report_check_queue_work();
+#endif
+
+ memset(event, 0, sizeof(struct ts_event));
+ event->point_num = buf[FTS_TOUCH_POINT_NUM] & 0x0F;
+ if (event->point_num > data->pdata->max_touch_number)
+ event->point_num = data->pdata->max_touch_number;
+ event->touch_point = 0;
+#endif
+
+#if (FTS_DEBUG_EN && (FTS_DEBUG_LEVEL == 2))
+ fts_show_touch_buffer(buf, event->point_num);
+#endif
+
+ for (i = 0; i < data->pdata->max_touch_number; i++) {
+ pointid = (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4;
+ if (pointid >= FTS_MAX_ID)
+ break;
+
+ event->touch_point++;
+ event->au16_x[i] =
+ (s16) (buf[FTS_TOUCH_X_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F)
+ << 8 | (s16) buf[FTS_TOUCH_X_L_POS + FTS_ONE_TCH_LEN * i];
+ event->au16_y[i] =
+ (s16) (buf[FTS_TOUCH_Y_H_POS + FTS_ONE_TCH_LEN * i] & 0x0F)
+ << 8 | (s16) buf[FTS_TOUCH_Y_L_POS + FTS_ONE_TCH_LEN * i];
+ event->au8_touch_event[i] =
+ buf[FTS_TOUCH_EVENT_POS + FTS_ONE_TCH_LEN * i] >> 6;
+
+ if (data->pdata->swap)
+ swap(event->au16_x[i], event->au16_y[i]);
+
+ if (data->pdata->scaling_down_half) {
+ event->au16_x[i] = event->au16_x[i] >> 1;
+ event->au16_y[i] = event->au16_y[i] >> 1;
+ }
+
+ event->au8_finger_id[i] =
+ (buf[FTS_TOUCH_ID_POS + FTS_ONE_TCH_LEN * i]) >> 4;
+ event->area[i] =
+ (buf[FTS_TOUCH_AREA_POS + FTS_ONE_TCH_LEN * i]) >> 4;
+ event->pressure[i] =
+ (s16) buf[FTS_TOUCH_PRE_POS + FTS_ONE_TCH_LEN * i];
+
+ if (0 == event->area[i])
+ event->area[i] = 0x09;
+
+ if (0 == event->pressure[i])
+ event->pressure[i] = 0x3f;
+
+ if ((event->au8_touch_event[i] == 0
+ || event->au8_touch_event[i] == 2)
+ && (event->point_num == 0))
+ break;
+ }
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_report_value
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_report_value(struct fts_ts_data *data)
+{
+ struct ts_event *event = &data->event;
+
+ FTS_DEBUG("point number: %d, touch point: %d", event->point_num,
+ event->touch_point);
+
+ if (0 == fts_input_dev_report_key_event(event, data))
+ return;
+#if FTS_MT_PROTOCOL_B_EN
+ fts_input_dev_report_b(event, data);
+#else
+ fts_input_dev_report_a(event, data);
+#endif
+
+ return;
+
+}
+
+/*****************************************************************************
+* Name: fts_ts_interrupt
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static irqreturn_t fts_ts_interrupt(int irq, void *dev_id)
+{
+ struct fts_ts_data *fts_ts = dev_id;
+ int ret = -1;
+
+ if (!fts_ts) {
+ FTS_ERROR("[INTR]: Invalid fts_ts");
+ return IRQ_HANDLED;
+ }
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_set_intr(1);
+#endif
+
+ ret = fts_read_touchdata(fts_wq_data);
+
+ if (ret == 0)
+ fts_report_value(fts_wq_data);
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_set_intr(0);
+#endif
+
+ return IRQ_HANDLED;
+}
+
+/*****************************************************************************
+* Name: fts_gpio_configure
+* Brief: Configure IRQ&RESET GPIO
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_gpio_configure(struct fts_ts_data *data)
+{
+ int err = 0;
+
+ FTS_FUNC_ENTER();
+ /* request irq gpio */
+ if (gpio_is_valid(data->pdata->irq_gpio)) {
+ err = gpio_request(data->pdata->irq_gpio, "fts_irq_gpio");
+ if (err) {
+ FTS_ERROR("[GPIO]irq gpio request failed");
+ goto err_irq_gpio_req;
+ }
+
+ err = gpio_direction_input(data->pdata->irq_gpio);
+ if (err) {
+ FTS_ERROR("[GPIO]set_direction for irq gpio failed");
+ goto err_irq_gpio_dir;
+ }
+ }
+ /* request reset gpio */
+ if (gpio_is_valid(data->pdata->reset_gpio)) {
+ err = gpio_request(data->pdata->reset_gpio, "fts_reset_gpio");
+ if (err) {
+ FTS_ERROR("[GPIO]reset gpio request failed");
+ goto err_irq_gpio_dir;
+ }
+
+ err = gpio_direction_output(data->pdata->reset_gpio, 1);
+ if (err) {
+ FTS_ERROR("[GPIO]set_direction for reset gpio failed");
+ goto err_reset_gpio_dir;
+ }
+ }
+
+ FTS_FUNC_EXIT();
+ return 0;
+
+err_reset_gpio_dir:
+ if (gpio_is_valid(data->pdata->reset_gpio))
+ gpio_free(data->pdata->reset_gpio);
+err_irq_gpio_dir:
+ if (gpio_is_valid(data->pdata->irq_gpio))
+ gpio_free(data->pdata->irq_gpio);
+err_irq_gpio_req:
+ FTS_FUNC_EXIT();
+ return err;
+}
+
+/*****************************************************************************
+* Name: fts_get_dt_coords
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_get_dt_coords(struct device *dev, char *name,
+ struct fts_ts_platform_data *pdata)
+{
+ u32 coords[FTS_COORDS_ARR_SIZE];
+ struct property *prop;
+ struct device_node *np = dev->of_node;
+ int coords_size, rc;
+
+ prop = of_find_property(np, name, NULL);
+ if (!prop)
+ return -EINVAL;
+ if (!prop->value)
+ return -ENODATA;
+
+ coords_size = prop->length / sizeof(u32);
+ if (coords_size != FTS_COORDS_ARR_SIZE) {
+ FTS_ERROR("invalid %s", name);
+ return -EINVAL;
+ }
+
+ rc = of_property_read_u32_array(np, name, coords, coords_size);
+ if (rc && (rc != -EINVAL)) {
+ FTS_ERROR("Unable to read %s", name);
+ return rc;
+ }
+
+ if (!strcmp(name, "focaltech,display-coords")) {
+ pdata->x_min = coords[0];
+ pdata->y_min = coords[1];
+ pdata->x_max = coords[2];
+ pdata->y_max = coords[3];
+ } else {
+ FTS_ERROR("unsupported property %s", name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_parse_dt
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_parse_dt(struct device *dev, struct fts_ts_platform_data *pdata)
+{
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+
+ FTS_FUNC_ENTER();
+
+ pdata->fts_chip_type = _FT5416;
+ rc = of_property_read_u32(np, "focaltech,panel-type",
+ &pdata->fts_chip_type);
+ if (rc)
+ FTS_ERROR("Panel type is undefined, use default panel FT5416");
+
+ rc = fts_get_dt_coords(dev, "focaltech,display-coords", pdata);
+ if (rc)
+ FTS_ERROR("Unable to get display-coords");
+
+ /* key */
+ pdata->have_key = of_property_read_bool(np, "focaltech,have-key");
+ if (pdata->have_key) {
+ rc = of_property_read_u32(np, "focaltech,key-number",
+ &pdata->key_number);
+ if (rc)
+ FTS_ERROR("Key number undefined!");
+ rc = of_property_read_u32_array(np, "focaltech,keys",
+ pdata->keys, pdata->key_number);
+ if (rc)
+ FTS_ERROR("Keys undefined!");
+ rc = of_property_read_u32(np, "focaltech,key-y-coord",
+ &pdata->key_y_coord);
+ if (rc)
+ FTS_ERROR("Key Y Coord undefined!");
+ rc = of_property_read_u32_array(np, "focaltech,key-x-coords",
+ pdata->key_x_coords,
+ pdata->key_number);
+ if (rc)
+ FTS_ERROR("Key X Coords undefined!");
+
+ FTS_DEBUG("%d: (%d, %d, %d), [%d, %d, %d][%d]",
+ pdata->key_number, pdata->keys[0], pdata->keys[1],
+ pdata->keys[2], pdata->key_x_coords[0],
+ pdata->key_x_coords[1], pdata->key_x_coords[2],
+ pdata->key_y_coord);
+ }
+
+ /* reset, irq gpio info */
+ pdata->reset_gpio =
+ of_get_named_gpio_flags(np, "focaltech,reset-gpio", 0,
+ &pdata->reset_gpio_flags);
+ if (pdata->reset_gpio < 0)
+ FTS_ERROR("Unable to get reset_gpio");
+
+ pdata->irq_gpio =
+ of_get_named_gpio_flags(np, "focaltech,irq-gpio", 0,
+ &pdata->irq_gpio_flags);
+ if (pdata->irq_gpio < 0)
+ FTS_ERROR("Unable to get irq_gpio");
+
+ rc = of_property_read_u32(np, "focaltech,max-touch-number", &temp_val);
+ if (!rc) {
+ pdata->max_touch_number = temp_val;
+ FTS_DEBUG("max_touch_number=%d", pdata->max_touch_number);
+ } else {
+ FTS_ERROR("Unable to get max-touch-number");
+ pdata->max_touch_number = FTS_MAX_POINTS;
+ }
+
+ pdata->swap = of_property_read_bool(np, "focaltech,swap-xy");
+ pdata->scaling_down_half = of_property_read_bool(np,
+ "focaltech,scaling-down-half");
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+#if defined(CONFIG_FB)
+/*****************************************************************************
+* Name: fb_notifier_callback
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+ struct fts_ts_data *fts_data =
+ container_of(self, struct fts_ts_data, fb_notif);
+
+ if (evdata && evdata->data && event == FB_EVENT_BLANK && fts_data
+ && fts_data->client) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK)
+ fts_ts_resume(&fts_data->client->dev);
+ else if (*blank == FB_BLANK_POWERDOWN)
+ fts_ts_suspend(&fts_data->client->dev);
+ }
+
+ return 0;
+}
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+/*****************************************************************************
+* Name: fts_ts_early_suspend
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_ts_early_suspend(struct early_suspend *handler)
+{
+ struct fts_ts_data *data = container_of(handler,
+ struct fts_ts_data,
+ early_suspend);
+
+ fts_ts_suspend(&data->client->dev);
+}
+
+/*****************************************************************************
+* Name: fts_ts_late_resume
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_ts_late_resume(struct early_suspend *handler)
+{
+ struct fts_ts_data *data = container_of(handler,
+ struct fts_ts_data,
+ early_suspend);
+
+ fts_ts_resume(&data->client->dev);
+}
+#endif
+
+/*****************************************************************************
+* Name: fts_ts_probe
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fts_ts_platform_data *pdata;
+ struct fts_ts_data *data;
+ struct input_dev *input_dev;
+ int err;
+
+ FTS_FUNC_ENTER();
+ /* 1. Get Platform data */
+ if (client->dev.of_node) {
+ pdata =
+ devm_kzalloc(&client->dev,
+ sizeof(struct fts_ts_platform_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ FTS_ERROR("[MEMORY]Failed to allocate memory");
+ FTS_FUNC_EXIT();
+ return -ENOMEM;
+ }
+ err = fts_parse_dt(&client->dev, pdata);
+ if (err)
+ FTS_ERROR("[DTS]DT parsing failed");
+ } else
+ pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ FTS_ERROR("Invalid pdata");
+ FTS_FUNC_EXIT();
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ FTS_ERROR("I2C not supported");
+ FTS_FUNC_EXIT();
+ return -ENODEV;
+ }
+
+ data =
+ devm_kzalloc(&client->dev, sizeof(struct fts_ts_data), GFP_KERNEL);
+ if (!data) {
+ FTS_ERROR("[MEMORY]Failed to allocate memory");
+ FTS_FUNC_EXIT();
+ return -ENOMEM;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ FTS_ERROR("[INPUT]Failed to allocate input device");
+ FTS_FUNC_EXIT();
+ return -ENOMEM;
+ }
+
+ data->input_dev = input_dev;
+ data->client = client;
+ data->pdata = pdata;
+
+ fts_wq_data = data;
+ fts_i2c_client = client;
+ fts_input_dev = input_dev;
+
+ spin_lock_init(&fts_wq_data->irq_lock);
+
+ fts_input_dev_init(client, data, input_dev, pdata);
+
+#if FTS_POWER_SOURCE_CUST_EN
+ fts_power_source_init(data);
+ fts_power_source_ctrl(data, 1);
+#endif
+
+ err = fts_gpio_configure(data);
+ if (err < 0) {
+ FTS_ERROR("[GPIO]Failed to configure the gpios");
+ goto free_gpio;
+ }
+
+ fts_reset_proc(200);
+ fts_wait_tp_to_valid(client);
+
+ err =
+ request_threaded_irq(client->irq, NULL, fts_ts_interrupt,
+ pdata->irq_gpio_flags | IRQF_ONESHOT |
+ IRQF_TRIGGER_FALLING, client->dev.driver->name,
+ data);
+ if (err) {
+ FTS_ERROR("Request irq failed!");
+ goto free_gpio;
+ }
+
+ fts_irq_disable();
+
+#if FTS_PSENSOR_EN
+ if (fts_sensor_init(data) != 0) {
+ FTS_ERROR("fts_sensor_init failed!");
+ FTS_FUNC_EXIT();
+ return 0;
+ }
+#endif
+
+#if FTS_POINT_REPORT_CHECK_EN
+ fts_point_report_check_init();
+#endif
+
+ fts_ex_mode_init(client);
+
+#if FTS_GESTURE_EN
+ fts_gesture_init(input_dev, client);
+#endif
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_init();
+#endif
+
+ fts_irq_enable();
+
+#if FTS_TEST_EN
+ fts_test_init(client);
+#endif
+
+#if defined(CONFIG_FB)
+ data->fb_notif.notifier_call = fb_notifier_callback;
+ err = fb_register_client(&data->fb_notif);
+ if (err)
+ FTS_ERROR("[FB]Unable to register fb_notifier: %d", err);
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ data->early_suspend.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + FTS_SUSPEND_LEVEL;
+ data->early_suspend.suspend = fts_ts_early_suspend;
+ data->early_suspend.resume = fts_ts_late_resume;
+ register_early_suspend(&data->early_suspend);
+#endif
+
+ FTS_FUNC_EXIT();
+ return 0;
+
+free_gpio:
+ if (gpio_is_valid(pdata->reset_gpio))
+ gpio_free(pdata->reset_gpio);
+ if (gpio_is_valid(pdata->irq_gpio))
+ gpio_free(pdata->irq_gpio);
+ return err;
+
+}
+
+/*****************************************************************************
+* Name: fts_ts_remove
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_ts_remove(struct i2c_client *client)
+{
+ struct fts_ts_data *data = i2c_get_clientdata(client);
+
+ FTS_FUNC_ENTER();
+ cancel_work_sync(&data->touch_event_work);
+
+#if FTS_PSENSOR_EN
+ fts_sensor_remove(data);
+#endif
+
+#if FTS_POINT_REPORT_CHECK_EN
+ fts_point_report_check_exit();
+#endif
+
+ fts_ex_mode_exit(client);
+
+#if defined(CONFIG_FB)
+ if (fb_unregister_client(&data->fb_notif))
+ FTS_ERROR("Error occurred while unregistering fb_notifier.");
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&data->early_suspend);
+#endif
+ free_irq(client->irq, data);
+
+ if (gpio_is_valid(data->pdata->reset_gpio))
+ gpio_free(data->pdata->reset_gpio);
+
+ if (gpio_is_valid(data->pdata->irq_gpio))
+ gpio_free(data->pdata->irq_gpio);
+
+ input_unregister_device(data->input_dev);
+
+#if FTS_TEST_EN
+ fts_test_exit(client);
+#endif
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_exit();
+#endif
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_ts_suspend
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_ts_suspend(struct device *dev)
+{
+ struct fts_ts_data *data = dev_get_drvdata(dev);
+ int retval = 0;
+
+ FTS_FUNC_ENTER();
+ if (data->suspended) {
+ FTS_INFO("Already in suspend state");
+ FTS_FUNC_EXIT();
+ return -1;
+ }
+
+ fts_release_all_finger();
+
+#if FTS_GESTURE_EN
+ retval = fts_gesture_suspend(data->client);
+ if (retval == 0) {
+ /* Enter into gesture mode(suspend) */
+ retval = enable_irq_wake(fts_wq_data->client->irq);
+ if (retval)
+ FTS_ERROR("%s: set_irq_wake failed", __func__);
+ data->suspended = true;
+ FTS_FUNC_EXIT();
+ return 0;
+ }
+#endif
+
+#if FTS_PSENSOR_EN
+ if (fts_sensor_suspend(data) != 0) {
+ enable_irq_wake(data->client->irq);
+ data->suspended = true;
+ return 0;
+ }
+#endif
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_suspend();
+#endif
+
+ fts_irq_disable();
+
+ /* TP enter sleep mode */
+ retval =
+ fts_i2c_write_reg(data->client, FTS_REG_POWER_MODE,
+ FTS_REG_POWER_MODE_SLEEP_VALUE);
+ if (retval < 0)
+ FTS_ERROR("Set TP to sleep mode fail, ret=%d!", retval);
+ data->suspended = true;
+
+ FTS_FUNC_EXIT();
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_ts_resume
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_ts_resume(struct device *dev)
+{
+ struct fts_ts_data *data = dev_get_drvdata(dev);
+
+ FTS_FUNC_ENTER();
+ if (!data->suspended) {
+ FTS_DEBUG("Already in awake state");
+ FTS_FUNC_EXIT();
+ return -1;
+ }
+
+ if (!fts_chip_idc(data))
+ fts_reset_proc(200);
+
+ fts_tp_state_recovery(data->client);
+
+#if FTS_GESTURE_EN
+ if (fts_gesture_resume(data->client) == 0) {
+ int err;
+
+ err = disable_irq_wake(data->client->irq);
+ if (err)
+ FTS_ERROR("%s: disable_irq_wake failed", __func__);
+ data->suspended = false;
+ FTS_FUNC_EXIT();
+ return 0;
+ }
+#endif
+
+#if FTS_PSENSOR_EN
+ if (fts_sensor_resume(data) != 0) {
+ disable_irq_wake(data->client->irq);
+ data->suspended = false;
+ FTS_FUNC_EXIT();
+ return 0;
+ }
+#endif
+
+ data->suspended = false;
+
+ fts_irq_enable();
+
+#if FTS_ESDCHECK_EN
+ fts_esdcheck_resume();
+#endif
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* I2C Driver
+*****************************************************************************/
+static const struct i2c_device_id fts_ts_id[] = {
+ {FTS_DRIVER_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, fts_ts_id);
+
+static struct of_device_id fts_match_table[] = {
+ {.compatible = "focaltech,fts",},
+ {},
+};
+
+static struct i2c_driver fts_ts_driver = {
+ .probe = fts_ts_probe,
+ .remove = fts_ts_remove,
+ .driver = {
+ .name = FTS_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = fts_match_table,
+ },
+ .id_table = fts_ts_id,
+};
+
+/*****************************************************************************
+* Name: fts_ts_init
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int __init fts_ts_init(void)
+{
+ int ret = 0;
+
+ FTS_FUNC_ENTER();
+ ret = i2c_add_driver(&fts_ts_driver);
+ if (ret != 0)
+ FTS_ERROR("Focaltech touch screen driver init failed!");
+ FTS_FUNC_EXIT();
+ return ret;
+}
+
+/*****************************************************************************
+* Name: fts_ts_exit
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void __exit fts_ts_exit(void)
+{
+ i2c_del_driver(&fts_ts_driver);
+}
+
+module_init(fts_ts_init);
+module_exit(fts_ts_exit);
+
+MODULE_AUTHOR("FocalTech Driver Team");
+MODULE_DESCRIPTION("FocalTech Touchscreen Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
new file mode 100644
index 000000000000..b8ef41f58420
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_core.h
@@ -0,0 +1,189 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+/*****************************************************************************
+*
+* File Name: focaltech_core.h
+
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+#ifndef __LINUX_FOCALTECH_CORE_H__
+#define __LINUX_FOCALTECH_CORE_H__
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/firmware.h>
+#include <linux/debugfs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/workqueue.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/init.h>
+#include <linux/cdev.h>
+#include <linux/device.h>
+#include <linux/mount.h>
+#include <linux/netdevice.h>
+#include <linux/unistd.h>
+#include <linux/ioctl.h>
+#include "focaltech_common.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define LEN_FLASH_ECC_MAX 0xFFFE
+
+#define FTS_WORKQUEUE_NAME "fts_wq"
+
+#define FTS_MAX_POINTS 10
+#define FTS_KEY_WIDTH 50
+#define FTS_ONE_TCH_LEN 6
+#define POINT_READ_BUF (3 + FTS_ONE_TCH_LEN * FTS_MAX_POINTS)
+
+#define FTS_MAX_ID 0x0F
+#define FTS_TOUCH_X_H_POS 3
+#define FTS_TOUCH_X_L_POS 4
+#define FTS_TOUCH_Y_H_POS 5
+#define FTS_TOUCH_Y_L_POS 6
+#define FTS_TOUCH_PRE_POS 7
+#define FTS_TOUCH_AREA_POS 8
+#define FTS_TOUCH_POINT_NUM 2
+#define FTS_TOUCH_EVENT_POS 3
+#define FTS_TOUCH_ID_POS 5
+#define FTS_COORDS_ARR_SIZE 4
+
+#define FTS_TOUCH_DOWN 0
+#define FTS_TOUCH_UP 1
+#define FTS_TOUCH_CONTACT 2
+
+#define FTS_SYSFS_ECHO_ON(buf) ((strnicmp(buf, "1", 1) == 0) || \
+ (strnicmp(buf, "on", 2) == 0))
+#define FTS_SYSFS_ECHO_OFF(buf) ((strnicmp(buf, "0", 1) == 0) || \
+ (strnicmp(buf, "off", 3) == 0))
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+struct fts_ts_platform_data {
+ u32 fts_chip_type;
+ u32 irq_gpio;
+ u32 irq_gpio_flags;
+ u32 reset_gpio;
+ u32 reset_gpio_flags;
+ bool have_key;
+ u32 key_number;
+ u32 keys[4];
+ u32 key_y_coord;
+ u32 key_x_coords[4];
+ u32 x_max;
+ u32 y_max;
+ u32 x_min;
+ u32 y_min;
+ u32 max_touch_number;
+
+ bool swap;
+ bool scaling_down_half;
+};
+
+struct ts_event {
+ u16 au16_x[FTS_MAX_POINTS]; /*x coordinate */
+ u16 au16_y[FTS_MAX_POINTS]; /*y coordinate */
+ u16 pressure[FTS_MAX_POINTS];
+ /* touch event: 0 -- down; 1-- up; 2 -- contact */
+ u8 au8_touch_event[FTS_MAX_POINTS];
+ u8 au8_finger_id[FTS_MAX_POINTS]; /*touch ID */
+ u8 area[FTS_MAX_POINTS];
+ u8 touch_point;
+ u8 point_num;
+};
+
+struct fts_ts_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct ts_event event;
+ const struct fts_ts_platform_data *pdata;
+#if FTS_PSENSOR_EN
+ struct fts_psensor_platform_data *psensor_pdata;
+#endif
+ struct work_struct touch_event_work;
+ struct workqueue_struct *ts_workqueue;
+ struct regulator *vdd;
+ struct regulator *vcc_i2c;
+ spinlock_t irq_lock;
+ u16 addr;
+ bool suspended;
+ u8 fw_ver[3];
+ u8 fw_vendor_id;
+ int touchs;
+ int irq_disable;
+
+#if defined(CONFIG_FB)
+ struct notifier_block fb_notif;
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+};
+
+#if FTS_PSENSOR_EN
+struct fts_psensor_platform_data {
+ struct input_dev *input_psensor_dev;
+ struct sensors_classdev ps_cdev;
+ int tp_psensor_opened;
+ char tp_psensor_data; /* 0 near, 1 far */
+ struct fts_ts_data *data;
+};
+
+int fts_sensor_init(struct fts_ts_data *data);
+int fts_sensor_read_data(struct fts_ts_data *data);
+int fts_sensor_suspend(struct fts_ts_data *data);
+int fts_sensor_resume(struct fts_ts_data *data);
+int fts_sensor_remove(struct fts_ts_data *data);
+#endif
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+extern struct i2c_client *fts_i2c_client;
+extern struct fts_ts_data *fts_wq_data;
+extern struct input_dev *fts_input_dev;
+
+#endif /* __LINUX_FOCALTECH_CORE_H__ */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c
new file mode 100644
index 000000000000..565507d02523
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_esdcheck.c
@@ -0,0 +1,473 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_esdcheck.c
+*
+* Author: luoguojin
+*
+* Created: 2016-08-03
+*
+* Abstract: ESD check function
+*
+* Version: v1.0
+*
+* Revision History:
+* v1.0:
+* First release. By luougojin 2016-08-03
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_ESDCHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define ESDCHECK_WAIT_TIME 1000
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_esdcheck_st {
+ u8 active:1;
+ u8 suspend:1;
+ u8 proc_debug:1; /* apk or adb is accessing I2C */
+ u8 intr:1; /* 1- Interrupt trigger */
+ u8 unused:4;
+ u8 flow_work_hold_cnt;
+ u8 flow_work_cnt_last;
+ u32 hardware_reset_cnt;
+ u32 i2c_nack_cnt;
+ u32 i2c_dataerror_cnt;
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct delayed_work fts_esdcheck_work;
+static struct workqueue_struct *fts_esdcheck_workqueue;
+static struct fts_esdcheck_st fts_esdcheck_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+* Name: lcd_esdcheck
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int lcd_need_reset;
+static int tp_need_recovery; /* LCD reset cause Tp reset */
+int idc_esdcheck_lcderror(void)
+{
+ u8 val;
+ int ret;
+
+ FTS_DEBUG("[ESD]Check LCD ESD");
+ if ((tp_need_recovery == 1) && (lcd_need_reset == 0)) {
+ tp_need_recovery = 0;
+ /* LCD reset, need recover TP state */
+ fts_tp_state_recovery(fts_i2c_client);
+ }
+
+ ret = fts_i2c_read_reg(fts_i2c_client, FTS_REG_ESD_SATURATE, &val);
+ if (ret < 0) {
+ FTS_ERROR("[ESD]: Read ESD_SATURATE(0xED) failed ret=%d!", ret);
+ return -EIO;
+ }
+
+ if (val == 0xAA) {
+ /*
+ * 1. Set flag lcd_need_reset = 1;
+ * 2. LCD driver need reset(recovery) LCD and
+ * set lcd_need_reset to 0
+ * 3. recover TP state
+ */
+ FTS_INFO("LCD ESD, Execute LCD reset!");
+ lcd_need_reset = 1;
+ tp_need_recovery = 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_tp_reset
+* Brief: esd check algorithm
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_esdcheck_tp_reset(void)
+{
+ FTS_FUNC_ENTER();
+
+ fts_esdcheck_data.flow_work_hold_cnt = 0;
+ fts_esdcheck_data.hardware_reset_cnt++;
+
+ fts_reset_proc(200);
+ fts_tp_state_recovery(fts_i2c_client);
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: get_chip_id
+* Brief: Read Chip Id 3 times
+* Input:
+* Output:
+* Return: 1 - Read Chip Id 3 times failed
+* 0 - Read Chip Id pass
+*****************************************************************************/
+static bool get_chip_id(void)
+{
+ int err = 0;
+ int i = 0;
+ u8 reg_value = 0;
+ u8 reg_addr = 0;
+
+ for (i = 0; i < 3; i++) {
+ reg_addr = FTS_REG_CHIP_ID;
+ err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
+
+ if (err < 0) {
+ FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!",
+ err);
+ fts_esdcheck_data.i2c_nack_cnt++;
+ } else {
+ /* Upgrade sometimes can't detect */
+ if ((reg_value == chip_types.chip_idh)
+ || (reg_value == 0xEF))
+ break;
+ else
+ fts_esdcheck_data.i2c_dataerror_cnt++;
+ }
+ }
+
+ /* if can't get correct data in 3 times, then need hardware reset */
+ if (i >= 3) {
+ FTS_ERROR
+ ("[ESD]: Read Chip id 3 times failed,"
+ "need execute TP reset!!");
+ return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: get_flow_cnt
+* Brief: Read flow cnt(0x91)
+* Input:
+* Output:
+* Return: 1 - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
+* 0 - Reg 0x91(flow cnt) normal
+*****************************************************************************/
+static bool get_flow_cnt(void)
+{
+ int err = 0;
+ u8 reg_value = 0;
+ u8 reg_addr = 0;
+
+ reg_addr = FTS_REG_FLOW_WORK_CNT;
+ err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
+ if (err < 0) {
+ FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", err);
+ fts_esdcheck_data.i2c_nack_cnt++;
+ } else {
+ if (reg_value == fts_esdcheck_data.flow_work_cnt_last)
+ fts_esdcheck_data.flow_work_hold_cnt++;
+ else
+ fts_esdcheck_data.flow_work_hold_cnt = 0;
+
+ fts_esdcheck_data.flow_work_cnt_last = reg_value;
+ }
+
+ /*
+ * if read flow work cnt 5 times and the value are
+ * all the same, then need hardware_reset
+ */
+ if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
+ FTS_DEBUG("[ESD]: Flow Work Cnt(reg0x91) keep a value"
+ "for 5 times, need execute TP reset!!");
+ return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: esdcheck_algorithm
+* Brief: esd check algorithm
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int esdcheck_algorithm(void)
+{
+ int err = 0;
+ u8 reg_value = 0;
+ u8 reg_addr = 0;
+ bool hardware_reset = 0;
+
+ /* 1. esdcheck is interrupt, then return */
+ if (fts_esdcheck_data.intr == 1) {
+ FTS_INFO("[ESD]: In interrupt state,"
+ "not check esd, return immediately!!");
+ return 0;
+ }
+
+ /* 2. check power state, if suspend, no need check esd */
+ if (fts_esdcheck_data.suspend == 1) {
+ FTS_INFO("[ESD]: In suspend, not check esd,"
+ "return immediately!!");
+ /* because in suspend state, adb can be used,
+ * when upgrade FW, will active ESD check(active = 1)
+ * But in suspend, then will don't queue_delayed_work,
+ * when resume, don't check ESD again
+ */
+ fts_esdcheck_data.active = 0;
+ return 0;
+ }
+
+ /*
+ * 3. check fts_esdcheck_data.proc_debug state,
+ * if 1-proc busy, no need check esd
+ */
+ if (fts_esdcheck_data.proc_debug == 1) {
+ FTS_INFO("[ESD]: In apk or adb command mode,"
+ "not check esd, return immediately!!");
+ return 0;
+ }
+
+ /* 4. In factory mode, can't check esd */
+ reg_addr = FTS_REG_WORKMODE;
+ err = fts_i2c_read(fts_i2c_client, &reg_addr, 1, &reg_value, 1);
+ if (err < 0) {
+ fts_esdcheck_data.i2c_nack_cnt++;
+ } else if ((reg_value & 0x70) == FTS_REG_WORKMODE_FACTORY_VALUE) {
+ FTS_INFO("[ESD]: In factory mode,"
+ "not check esd, return immediately!!");
+ return 0;
+ }
+
+ /* 5. Get Chip ID */
+ hardware_reset = get_chip_id();
+
+ /*
+ * 6. get Flow work cnt: 0x91 If no change for 5 times,
+ * then ESD and reset
+ */
+ if (!hardware_reset)
+ hardware_reset = get_flow_cnt();
+
+ /* 7. If need hardware reset, then handle it here */
+ if (hardware_reset == 1)
+ fts_esdcheck_tp_reset();
+
+ FTS_INFO("[ESD]: NoACK=%d, Error Data=%d, Hardware Reset=%d\n",
+ fts_esdcheck_data.i2c_nack_cnt,
+ fts_esdcheck_data.i2c_dataerror_cnt,
+ fts_esdcheck_data.hardware_reset_cnt);
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_func
+* Brief: fts_esdcheck_func
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void esdcheck_func(struct work_struct *work)
+{
+ FTS_FUNC_ENTER();
+
+ idc_esdcheck_lcderror();
+
+ esdcheck_algorithm();
+
+ if (fts_esdcheck_data.suspend == 0) {
+ queue_delayed_work(fts_esdcheck_workqueue, &fts_esdcheck_work,
+ msecs_to_jiffies(ESDCHECK_WAIT_TIME));
+ }
+
+ FTS_FUNC_EXIT();
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_set_intr
+* Brief: interrupt flag (main used in interrupt tp report)
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_set_intr(bool intr)
+{
+ /* interrupt don't add debug message */
+ fts_esdcheck_data.intr = intr;
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_get_status(void)
+* Brief: get current status
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_get_status(void)
+{
+ /* interrupt don't add debug message */
+ return fts_esdcheck_data.active;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_proc_busy
+* Brief: When APK or ADB command access TP via driver,
+* then need set proc_debug,
+* then will not check ESD.
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_proc_busy(bool proc_debug)
+{
+ fts_esdcheck_data.proc_debug = proc_debug;
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_switch
+* Brief: FTS esd check function switch.
+* Input: enable: 1 - Enable esd check
+* 0 - Disable esd check
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_switch(bool enable)
+{
+ FTS_FUNC_ENTER();
+ if (enable == 1) {
+ if (fts_esdcheck_data.active == 0) {
+ FTS_INFO("[ESD]: ESD check start!!");
+ fts_esdcheck_data.active = 1;
+ queue_delayed_work(fts_esdcheck_workqueue,
+ &fts_esdcheck_work,
+ msecs_to_jiffies
+ (ESDCHECK_WAIT_TIME));
+ }
+ } else {
+ if (fts_esdcheck_data.active == 1) {
+ FTS_INFO("[ESD]: ESD check stop!!");
+ fts_esdcheck_data.active = 0;
+ cancel_delayed_work_sync(&fts_esdcheck_work);
+ }
+ }
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_suspend
+* Brief: Run when tp enter into suspend
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_suspend(void)
+{
+ FTS_FUNC_ENTER();
+ fts_esdcheck_switch(DISABLE);
+ fts_esdcheck_data.suspend = 1;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_resume
+* Brief: Run when tp resume
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_resume(void)
+{
+ FTS_FUNC_ENTER();
+ fts_esdcheck_switch(ENABLE);
+ fts_esdcheck_data.suspend = 0;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_init
+* Brief: Init and create a queue work to check esd
+* Input:
+* Output:
+* Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_esdcheck_init(void)
+{
+ FTS_FUNC_ENTER();
+
+ INIT_DELAYED_WORK(&fts_esdcheck_work, esdcheck_func);
+ fts_esdcheck_workqueue = create_workqueue("fts_esdcheck_wq");
+ if (fts_esdcheck_workqueue == NULL)
+ FTS_INFO("[ESD]: Failed to create esd work queue!!");
+
+ memset((u8 *) &fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
+
+ fts_esdcheck_switch(ENABLE);
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_esdcheck_exit
+* Brief: When FTS TP driver is removed,
+* then call this function to destroy work queue
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_esdcheck_exit(void)
+{
+ FTS_FUNC_ENTER();
+
+ destroy_workqueue(fts_esdcheck_workqueue);
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+#endif /* FTS_ESDCHECK_EN */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c
new file mode 100644
index 000000000000..6ab5d47170cc
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_ex_mode.c
@@ -0,0 +1,357 @@
+/*
+ *
+ * FocalTech ftxxxx TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_ex_mode.c
+*
+* Author: Liu WeiGuang
+*
+* Created: 2016-08-31
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* 2.Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* 3.Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_mode_flag {
+ int fts_glove_mode_flag;
+ int fts_cover_mode_flag;
+ int fts_charger_mode_flag;
+};
+
+struct fts_mode_flag g_fts_mode_flag;
+
+/*****************************************************************************
+* 4.Static variables
+*****************************************************************************/
+
+/*****************************************************************************
+* 5.Global variable or extern global variabls/functions
+*****************************************************************************/
+int fts_enter_glove_mode(struct i2c_client *client, int mode);
+int fts_glove_init(struct i2c_client *client);
+int fts_glove_exit(struct i2c_client *client);
+
+int fts_enter_cover_mode(struct i2c_client *client, int mode);
+int fts_cover_init(struct i2c_client *client);
+int fts_cover_exit(struct i2c_client *client);
+
+int fts_enter_charger_mode(struct i2c_client *client, int mode);
+int fts_charger_init(struct i2c_client *client);
+int fts_charger_exit(struct i2c_client *client);
+
+/*****************************************************************************
+* 6.Static function prototypes
+*******************************************************************************/
+
+#if FTS_GLOVE_EN
+static ssize_t fts_touch_glove_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "Glove: %s\n",
+ g_fts_mode_flag.fts_glove_mode_flag ? "On" : "Off");
+}
+
+static ssize_t fts_touch_glove_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ if (!g_fts_mode_flag.fts_glove_mode_flag) {
+ FTS_INFO("[Mode]enter glove mode");
+ ret = fts_enter_glove_mode(fts_i2c_client, true);
+ if (ret >= 0)
+ g_fts_mode_flag.fts_glove_mode_flag = true;
+ }
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ if (g_fts_mode_flag.fts_glove_mode_flag) {
+ FTS_INFO("[Mode]exit glove mode");
+ ret = fts_enter_glove_mode(fts_i2c_client, false);
+ if (ret >= 0)
+ g_fts_mode_flag.fts_glove_mode_flag = false;
+ }
+ }
+ FTS_INFO("[Mode]glove mode status: %d",
+ g_fts_mode_flag.fts_glove_mode_flag);
+ return count;
+}
+
+/************************************************************************
+* Name: fts_enter_glove_mode
+* Brief: change glove mode
+* Input: glove mode
+* Output: no
+* Return: success >=0, otherwise failed
+***********************************************************************/
+int fts_enter_glove_mode(struct i2c_client *client, int mode)
+{
+ int ret = 0;
+ static u8 buf_addr[2] = { 0 };
+ static u8 buf_value[2] = { 0 };
+
+ buf_addr[0] = FTS_REG_GLOVE_MODE_EN;
+
+ if (mode)
+ buf_value[0] = 0x01;
+ else
+ buf_value[0] = 0x00;
+
+ ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]);
+ if (ret < 0)
+ FTS_ERROR("[Mode]fts_enter_glove_mode write value fail");
+
+ return ret;
+
+}
+
+/* read and write glove mode
+* read example: cat fts_touch_glove_mode---read glove mode
+* write example:echo 01 > fts_touch_glove_mode ---write glove mode to 01
+*
+*/
+static DEVICE_ATTR(fts_glove_mode, S_IRUGO | S_IWUSR, fts_touch_glove_show,
+ fts_touch_glove_store);
+
+#endif
+
+#if FTS_COVER_EN
+static ssize_t fts_touch_cover_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "Cover: %s\n",
+ g_fts_mode_flag.fts_cover_mode_flag ? "On" : "Off");
+}
+
+static ssize_t fts_touch_cover_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ if (!g_fts_mode_flag.fts_cover_mode_flag) {
+ FTS_INFO("[Mode]enter cover mode");
+ ret = fts_enter_cover_mode(fts_i2c_client, true);
+ if (ret >= 0)
+ g_fts_mode_flag.fts_cover_mode_flag = true;
+ }
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ if (g_fts_mode_flag.fts_cover_mode_flag) {
+ FTS_INFO("[Mode]exit cover mode");
+ ret = fts_enter_cover_mode(fts_i2c_client, false);
+ if (ret >= 0)
+ g_fts_mode_flag.fts_cover_mode_flag = false;
+ }
+ }
+ FTS_INFO("[Mode]cover mode status: %d",
+ g_fts_mode_flag.fts_cover_mode_flag);
+ return count;
+}
+
+/************************************************************************
+* Name: fts_enter_cover_mode
+* Brief: change cover mode
+* Input: cover mode
+* Output: no
+* Return: success >=0, otherwise failed
+***********************************************************************/
+int fts_enter_cover_mode(struct i2c_client *client, int mode)
+{
+ int ret = 0;
+ static u8 buf_addr[2] = { 0 };
+ static u8 buf_value[2] = { 0 };
+
+ buf_addr[0] = FTS_REG_COVER_MODE_EN;
+
+ if (mode)
+ buf_value[0] = 0x01;
+ else
+ buf_value[0] = 0x00;
+
+ ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]);
+ if (ret < 0)
+ FTS_ERROR("[Mode] fts_enter_cover_mode write value fail\n");
+
+ return ret;
+
+}
+
+/* read and write cover mode
+* read example: cat fts_touch_cover_mode---read cover mode
+* write example:echo 01 > fts_touch_cover_mode ---write cover mode to 01
+*
+*/
+static DEVICE_ATTR(fts_cover_mode, S_IRUGO | S_IWUSR, fts_touch_cover_show,
+ fts_touch_cover_store);
+
+#endif
+
+#if FTS_CHARGER_EN
+static ssize_t fts_touch_charger_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "Charger: %s\n",
+ g_fts_mode_flag.fts_charger_mode_flag ? "On" : "Off");
+}
+
+static ssize_t fts_touch_charger_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret;
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ if (!g_fts_mode_flag.fts_charger_mode_flag) {
+ FTS_INFO("[Mode]enter charger mode");
+ ret = fts_enter_charger_mode(fts_i2c_client, true);
+ if (ret >= 0)
+ g_fts_mode_flag.fts_charger_mode_flag = true;
+ }
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ if (g_fts_mode_flag.fts_charger_mode_flag) {
+ FTS_INFO("[Mode]exit charger mode");
+ ret = fts_enter_charger_mode(fts_i2c_client, false);
+ if (ret >= 0)
+ g_fts_mode_flag.fts_charger_mode_flag = false;
+ }
+ }
+ FTS_INFO("[Mode]charger mode status: %d",
+ g_fts_mode_flag.fts_charger_mode_flag);
+ return count;
+}
+
+/************************************************************************
+* Name: fts_enter_charger_mode
+* Brief: change charger mode
+* Input: charger mode
+* Output: no
+* Return: success >=0, otherwise failed
+***********************************************************************/
+int fts_enter_charger_mode(struct i2c_client *client, int mode)
+{
+ int ret = 0;
+ static u8 buf_addr[2] = { 0 };
+ static u8 buf_value[2] = { 0 };
+
+ buf_addr[0] = FTS_REG_CHARGER_MODE_EN;
+
+ if (mode)
+ buf_value[0] = 0x01;
+ else
+ buf_value[0] = 0x00;
+
+ ret = fts_i2c_write_reg(client, buf_addr[0], buf_value[0]);
+ if (ret < 0)
+ FTS_DEBUG("[Mode]fts_enter_charger_mode write value fail");
+
+ return ret;
+
+}
+
+/* read and write charger mode
+* read example: cat fts_touch_charger_mode---read charger mode
+* write example:echo 01 > fts_touch_charger_mode ---write charger mode to 01
+*
+*/
+static DEVICE_ATTR(fts_charger_mode, S_IRUGO | S_IWUSR, fts_touch_charger_show,
+ fts_touch_charger_store);
+
+#endif
+
+static struct attribute *fts_touch_mode_attrs[] = {
+#if FTS_GLOVE_EN
+ &dev_attr_fts_glove_mode.attr,
+#endif
+
+#if FTS_COVER_EN
+ &dev_attr_fts_cover_mode.attr,
+#endif
+
+#if FTS_CHARGER_EN
+ &dev_attr_fts_charger_mode.attr,
+#endif
+
+ NULL,
+};
+
+static struct attribute_group fts_touch_mode_group = {
+ .attrs = fts_touch_mode_attrs,
+};
+
+int fts_ex_mode_init(struct i2c_client *client)
+{
+ int err = 0;
+
+ g_fts_mode_flag.fts_glove_mode_flag = false;
+ g_fts_mode_flag.fts_cover_mode_flag = false;
+ g_fts_mode_flag.fts_charger_mode_flag = false;
+
+ err = sysfs_create_group(&client->dev.kobj, &fts_touch_mode_group);
+ if (0 != err) {
+ FTS_ERROR("[Mode]create sysfs failed.");
+ sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group);
+ return -EIO;
+ }
+
+ FTS_DEBUG("[Mode]create sysfs succeeded");
+
+ return err;
+
+}
+
+int fts_ex_mode_exit(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &fts_touch_mode_group);
+ return 0;
+}
+
+int fts_ex_mode_recovery(struct i2c_client *client)
+{
+ int ret = 0;
+#if FTS_GLOVE_EN
+ if (g_fts_mode_flag.fts_glove_mode_flag)
+ ret = fts_enter_glove_mode(client, true);
+#endif
+
+#if FTS_COVER_EN
+ if (g_fts_mode_flag.fts_cover_mode_flag)
+ ret = fts_enter_cover_mode(client, true);
+#endif
+
+#if FTS_CHARGER_EN
+ if (g_fts_mode_flag.fts_charger_mode_flag)
+ ret = fts_enter_charger_mode(client, true);
+#endif
+
+ return ret;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c
new file mode 100644
index 000000000000..9faabb8681bb
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_gesture.c
@@ -0,0 +1,652 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, Focaltech Ltd. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_gestrue.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-08
+*
+* Abstract:
+*
+* Reference:
+*
+*****************************************************************************/
+
+/*****************************************************************************
+* 1.Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+#if FTS_GESTURE_EN
+/******************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define KEY_GESTURE_U KEY_U
+#define KEY_GESTURE_UP KEY_UP
+#define KEY_GESTURE_DOWN KEY_DOWN
+#define KEY_GESTURE_LEFT KEY_LEFT
+#define KEY_GESTURE_RIGHT KEY_RIGHT
+#define KEY_GESTURE_O KEY_O
+#define KEY_GESTURE_E KEY_E
+#define KEY_GESTURE_M KEY_M
+#define KEY_GESTURE_L KEY_L
+#define KEY_GESTURE_W KEY_W
+#define KEY_GESTURE_S KEY_S
+#define KEY_GESTURE_V KEY_V
+#define KEY_GESTURE_C KEY_C
+#define KEY_GESTURE_Z KEY_Z
+
+#define GESTURE_LEFT 0x20
+#define GESTURE_RIGHT 0x21
+#define GESTURE_UP 0x22
+#define GESTURE_DOWN 0x23
+#define GESTURE_DOUBLECLICK 0x24
+#define GESTURE_O 0x30
+#define GESTURE_W 0x31
+#define GESTURE_M 0x32
+#define GESTURE_E 0x33
+#define GESTURE_L 0x44
+#define GESTURE_S 0x46
+#define GESTURE_V 0x54
+#define GESTURE_Z 0x41
+#define GESTURE_C 0x34
+#define FTS_GESTRUE_POINTS 255
+#define FTS_GESTRUE_POINTS_HEADER 8
+#define FTS_GESTURE_OUTPUT_ADRESS 0xD3
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+struct fts_gesture_st {
+ u8 header[FTS_GESTRUE_POINTS_HEADER];
+ u16 coordinate_x[FTS_GESTRUE_POINTS];
+ u16 coordinate_y[FTS_GESTRUE_POINTS];
+ u8 mode;
+ u8 active;
+};
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct fts_gesture_st fts_gesture_data;
+extern struct fts_ts_data *fts_wq_data;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+static ssize_t fts_gesture_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t fts_gesture_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count);
+static ssize_t fts_gesture_buf_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t fts_gesture_buf_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+/* sysfs gesture node
+ * read example: cat fts_gesture_mode ---read gesture mode
+ * write example:echo 01 > fts_gesture_mode ---write gesture mode to 01
+ *
+ */
+static DEVICE_ATTR(fts_gesture_mode, S_IRUGO | S_IWUSR, fts_gesture_show,
+ fts_gesture_store);
+/*
+ * read example: cat fts_gesture_buf ---read gesture buf
+ */
+static DEVICE_ATTR(fts_gesture_buf, S_IRUGO | S_IWUSR, fts_gesture_buf_show,
+ fts_gesture_buf_store);
+static struct attribute *fts_gesture_mode_attrs[] = {
+
+ &dev_attr_fts_gesture_mode.attr,
+ &dev_attr_fts_gesture_buf.attr,
+ NULL,
+};
+
+static struct attribute_group fts_gesture_group = {
+ .attrs = fts_gesture_mode_attrs,
+};
+
+/************************************************************************
+* Name: fts_gesture_show
+* Brief: no
+* Input: device, device attribute, char buf
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count;
+ u8 val;
+ struct i2c_client *client = container_of(dev, struct i2c_client, dev);
+
+ mutex_lock(&fts_input_dev->mutex);
+ fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &val);
+ count =
+ sprintf(buf, "Gesture Mode: %s\n",
+ fts_gesture_data.mode ? "On" : "Off");
+ count += sprintf(buf + count, "Reg(0xD0) = %d\n", val);
+ mutex_unlock(&fts_input_dev->mutex);
+
+ return count;
+}
+
+/************************************************************************
+* Name: fts_gesture_store
+* Brief: no
+* Input: device, device attribute, char buf, char count
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ mutex_lock(&fts_input_dev->mutex);
+
+ if (FTS_SYSFS_ECHO_ON(buf)) {
+ FTS_INFO("[GESTURE]enable gesture");
+ fts_gesture_data.mode = ENABLE;
+ } else if (FTS_SYSFS_ECHO_OFF(buf)) {
+ FTS_INFO("[GESTURE]disable gesture");
+ fts_gesture_data.mode = DISABLE;
+ }
+
+ mutex_unlock(&fts_input_dev->mutex);
+
+ return count;
+}
+
+/************************************************************************
+* Name: fts_gesture_buf_show
+* Brief: no
+* Input: device, device attribute, char buf
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_buf_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int count;
+ int i = 0;
+
+ mutex_lock(&fts_input_dev->mutex);
+ count =
+ snprintf(buf, PAGE_SIZE, "Gesture ID: 0x%x\n",
+ fts_gesture_data.header[0]);
+ count +=
+ snprintf(buf + count, PAGE_SIZE, "Gesture PointNum: %d\n",
+ fts_gesture_data.header[1]);
+ count += snprintf(buf + count, PAGE_SIZE, "Gesture Point Buf:\n");
+ for (i = 0; i < fts_gesture_data.header[1]; i++) {
+ count +=
+ snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i,
+ fts_gesture_data.coordinate_x[i],
+ fts_gesture_data.coordinate_y[i]);
+ if ((i + 1) % 4 == 0)
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ }
+ count += snprintf(buf + count, PAGE_SIZE, "\n");
+ mutex_unlock(&fts_input_dev->mutex);
+
+ return count;
+}
+
+/************************************************************************
+* Name: fts_gesture_buf_store
+* Brief: no
+* Input: device, device attribute, char buf, char count
+* Output: no
+* Return:
+***********************************************************************/
+static ssize_t fts_gesture_buf_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ /* place holder for future use */
+ return -EPERM;
+}
+
+/*****************************************************************************
+* Name: fts_create_gesture_sysfs
+* Brief:
+* Input:
+* Output: None
+* Return: 0-success or error
+*****************************************************************************/
+int fts_create_gesture_sysfs(struct i2c_client *client)
+{
+ int ret = 0;
+
+ ret = sysfs_create_group(&client->dev.kobj, &fts_gesture_group);
+ if (ret != 0) {
+ FTS_ERROR
+ ("[GESTURE]fts_gesture_mode_group(sysfs) create failed!");
+ sysfs_remove_group(&client->dev.kobj, &fts_gesture_group);
+ return ret;
+ }
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_gesture_recovery
+* Brief: recovery gesture state when reset
+* Input:
+* Output: None
+* Return:
+*****************************************************************************/
+void fts_gesture_recovery(struct i2c_client *client)
+{
+ if (fts_gesture_data.mode && fts_gesture_data.active) {
+ fts_i2c_write_reg(client, 0xD1, 0xff);
+ fts_i2c_write_reg(client, 0xD2, 0xff);
+ fts_i2c_write_reg(client, 0xD5, 0xff);
+ fts_i2c_write_reg(client, 0xD6, 0xff);
+ fts_i2c_write_reg(client, 0xD7, 0xff);
+ fts_i2c_write_reg(client, 0xD8, 0xff);
+ fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, ENABLE);
+ }
+}
+
+/*****************************************************************************
+* Name: fts_gesture_init
+* Brief:
+* Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+int fts_gesture_init(struct input_dev *input_dev, struct i2c_client *client)
+{
+ FTS_FUNC_ENTER();
+ input_set_capability(input_dev, EV_KEY, KEY_POWER);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z);
+ input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C);
+
+ __set_bit(KEY_GESTURE_RIGHT, input_dev->keybit);
+ __set_bit(KEY_GESTURE_LEFT, input_dev->keybit);
+ __set_bit(KEY_GESTURE_UP, input_dev->keybit);
+ __set_bit(KEY_GESTURE_DOWN, input_dev->keybit);
+ __set_bit(KEY_GESTURE_U, input_dev->keybit);
+ __set_bit(KEY_GESTURE_O, input_dev->keybit);
+ __set_bit(KEY_GESTURE_E, input_dev->keybit);
+ __set_bit(KEY_GESTURE_M, input_dev->keybit);
+ __set_bit(KEY_GESTURE_W, input_dev->keybit);
+ __set_bit(KEY_GESTURE_L, input_dev->keybit);
+ __set_bit(KEY_GESTURE_S, input_dev->keybit);
+ __set_bit(KEY_GESTURE_V, input_dev->keybit);
+ __set_bit(KEY_GESTURE_C, input_dev->keybit);
+ __set_bit(KEY_GESTURE_Z, input_dev->keybit);
+
+ fts_create_gesture_sysfs(client);
+ fts_gesture_data.mode = 0;
+ fts_gesture_data.active = 0;
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/************************************************************************
+* Name: fts_gesture_exit
+* Brief: remove sys
+* Input: i2c info
+* Output: no
+* Return: no
+***********************************************************************/
+int fts_gesture_exit(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &fts_gesture_group);
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_check_gesture
+* Brief:
+* Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+static void fts_check_gesture(struct input_dev *input_dev, int gesture_id)
+{
+ char *envp[2];
+ int gesture;
+
+ FTS_FUNC_ENTER();
+ switch (gesture_id) {
+ case GESTURE_LEFT:
+ envp[0] = "GESTURE=LEFT";
+ gesture = KEY_GESTURE_LEFT;
+ break;
+ case GESTURE_RIGHT:
+ envp[0] = "GESTURE=RIGHT";
+ gesture = KEY_GESTURE_RIGHT;
+ break;
+ case GESTURE_UP:
+ envp[0] = "GESTURE=UP";
+ gesture = KEY_GESTURE_UP;
+ break;
+ case GESTURE_DOWN:
+ envp[0] = "GESTURE=DOWN";
+ gesture = KEY_GESTURE_DOWN;
+ break;
+ case GESTURE_DOUBLECLICK:
+ envp[0] = "GESTURE=DOUBLE_CLICK";
+ gesture = KEY_POWER;
+ break;
+ case GESTURE_O:
+ envp[0] = "GESTURE=O";
+ gesture = KEY_GESTURE_O;
+ break;
+ case GESTURE_W:
+ envp[0] = "GESTURE=W";
+ gesture = KEY_GESTURE_W;
+ break;
+ case GESTURE_M:
+ envp[0] = "GESTURE=M";
+ gesture = KEY_GESTURE_M;
+ break;
+ case GESTURE_E:
+ envp[0] = "GESTURE=E";
+ gesture = KEY_GESTURE_E;
+ break;
+ case GESTURE_L:
+ envp[0] = "GESTURE=L";
+ gesture = KEY_GESTURE_L;
+ break;
+ case GESTURE_S:
+ envp[0] = "GESTURE=S";
+ gesture = KEY_GESTURE_S;
+ break;
+ case GESTURE_V:
+ envp[0] = "GESTURE=V";
+ gesture = KEY_GESTURE_V;
+ break;
+ case GESTURE_Z:
+ envp[0] = "GESTURE=Z";
+ gesture = KEY_GESTURE_Z;
+ break;
+ case GESTURE_C:
+ envp[0] = "GESTURE=C";
+ gesture = KEY_GESTURE_C;
+ break;
+ default:
+ envp[0] = "GESTURE=NONE";
+ gesture = -1;
+ break;
+ }
+ FTS_DEBUG("envp[0]: %s", envp[0]);
+ /* report event key */
+ /*if (gesture != -1)
+ {
+ input_report_key(input_dev, gesture, 1);
+ input_sync(input_dev);
+ input_report_key(input_dev, gesture, 0);
+ input_sync(input_dev);
+ } */
+
+ envp[1] = NULL;
+ kobject_uevent_env(&fts_wq_data->client->dev.kobj, KOBJ_CHANGE, envp);
+ sysfs_notify(&fts_wq_data->client->dev.kobj, NULL, "GESTURE_ID");
+
+ FTS_FUNC_EXIT();
+}
+
+/************************************************************************
+* Name: fts_gesture_readdata
+* Brief: read data from TP register
+* Input: no
+* Output: no
+* Return: fail <0
+***********************************************************************/
+static int fts_gesture_read_buffer(struct i2c_client *client, u8 *buf,
+ int read_bytes)
+{
+ int remain_bytes;
+ int ret;
+ int i;
+
+ if (read_bytes <= I2C_BUFFER_LENGTH_MAXINUM) {
+ ret = fts_i2c_read(client, buf, 1, buf, read_bytes);
+ } else {
+ ret =
+ fts_i2c_read(client, buf, 1, buf,
+ I2C_BUFFER_LENGTH_MAXINUM);
+ remain_bytes = read_bytes - I2C_BUFFER_LENGTH_MAXINUM;
+ for (i = 1; remain_bytes > 0; i++) {
+ if (remain_bytes <= I2C_BUFFER_LENGTH_MAXINUM)
+ ret =
+ fts_i2c_read(client, buf, 0,
+ buf +
+ I2C_BUFFER_LENGTH_MAXINUM * i,
+ remain_bytes);
+ else
+ ret =
+ fts_i2c_read(client, buf, 0,
+ buf +
+ I2C_BUFFER_LENGTH_MAXINUM * i,
+ I2C_BUFFER_LENGTH_MAXINUM);
+ remain_bytes -= I2C_BUFFER_LENGTH_MAXINUM;
+ }
+ }
+
+ return ret;
+}
+
+/************************************************************************
+* Name: fts_gesture_readdata
+* Brief: read data from TP register
+* Input: no
+* Output: no
+* Return: fail <0
+***********************************************************************/
+int fts_gesture_readdata(struct i2c_client *client)
+{
+ u8 buf[FTS_GESTRUE_POINTS * 4] = { 0 };
+ int ret = -1;
+ int i = 0;
+ int gestrue_id = 0;
+ int read_bytes = 0;
+ u8 pointnum;
+
+ FTS_FUNC_ENTER();
+ /* init variable before read gesture point */
+ memset(fts_gesture_data.header, 0, FTS_GESTRUE_POINTS_HEADER);
+ memset(fts_gesture_data.coordinate_x, 0,
+ FTS_GESTRUE_POINTS * sizeof(u16));
+ memset(fts_gesture_data.coordinate_y, 0,
+ FTS_GESTRUE_POINTS * sizeof(u16));
+
+ buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+ ret = fts_i2c_read(client, buf, 1, buf, FTS_GESTRUE_POINTS_HEADER);
+ if (ret < 0) {
+ FTS_ERROR("[GESTURE]Read gesture header data failed!!");
+ FTS_FUNC_EXIT();
+ return ret;
+ }
+
+ /* FW recognize gesture */
+ if (chip_types.chip_idh == 0x54 || chip_types.chip_idh == 0x58
+ || chip_types.chip_idh == 0x86 || chip_types.chip_idh == 0x87
+ || chip_types.chip_idh == 0x64) {
+ memcpy(fts_gesture_data.header, buf, FTS_GESTRUE_POINTS_HEADER);
+ gestrue_id = buf[0];
+ pointnum = buf[1];
+ read_bytes = ((int)pointnum) * 4 + 2;
+ buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+ FTS_DEBUG("[GESTURE]PointNum=%d", pointnum);
+ ret = fts_gesture_read_buffer(client, buf, read_bytes);
+ if (ret < 0) {
+ FTS_ERROR("[GESTURE]Read gesture touch data failed!!");
+ FTS_FUNC_EXIT();
+ return ret;
+ }
+
+ fts_check_gesture(fts_input_dev, gestrue_id);
+ for (i = 0; i < pointnum; i++) {
+ fts_gesture_data.coordinate_x[i] =
+ (((s16) buf[0 + (4 * i + 2)]) & 0x0F) << 8 |
+ (((s16) buf[1 + (4 * i + 2)]) & 0xFF);
+ fts_gesture_data.coordinate_y[i] =
+ (((s16) buf[2 + (4 * i + 2)]) & 0x0F) << 8 |
+ (((s16) buf[3 + (4 * i + 2)]) & 0xFF);
+ }
+ FTS_FUNC_EXIT();
+ return 0;
+ }
+ /* other IC's gestrue in driver */
+ if (0x24 == buf[0]) {
+ gestrue_id = 0x24;
+ fts_check_gesture(fts_input_dev, gestrue_id);
+ FTS_DEBUG("[GESTURE]%d check_gesture gestrue_id", gestrue_id);
+ FTS_FUNC_EXIT();
+ return -1;
+ }
+
+ /* Host Driver recognize gesture */
+ pointnum = buf[1];
+ read_bytes = ((int)pointnum) * 4 + 2;
+ buf[0] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
+ ret = fts_gesture_read_buffer(client, buf, read_bytes);
+ if (ret < 0) {
+ FTS_ERROR ("[GESTURE]Driver recognize gesture -"
+ "Read gesture touch data failed!!");
+ FTS_FUNC_EXIT();
+ return ret;
+ }
+
+ /*
+ * Host Driver recognize gesture, need gesture lib.a
+ * Not use now for compatibility
+ gestrue_id = fetch_object_sample(buf, pointnum);
+ */
+ gestrue_id = 0x24;
+ fts_check_gesture(fts_input_dev, gestrue_id);
+ FTS_DEBUG("[GESTURE]%d read gestrue_id", gestrue_id);
+
+ for (i = 0; i < pointnum; i++) {
+ fts_gesture_data.coordinate_x[i] =
+ (((s16) buf[0 + (4 * i + 8)]) & 0x0F) << 8 |
+ (((s16) buf[1 + (4 * i + 8)]) & 0xFF);
+ fts_gesture_data.coordinate_y[i] =
+ (((s16) buf[2 + (4 * i + 8)]) & 0x0F) << 8 |
+ (((s16) buf[3 + (4 * i + 8)]) & 0xFF);
+ }
+ FTS_FUNC_EXIT();
+ return -1;
+}
+
+/*****************************************************************************
+* Name: fts_gesture_suspend
+* Brief:
+* Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+int fts_gesture_suspend(struct i2c_client *i2c_client)
+{
+ int i;
+ u8 state;
+
+ FTS_FUNC_ENTER();
+
+ /* gesture not enable, return immediately */
+ if (fts_gesture_data.mode == 0) {
+ FTS_DEBUG("gesture is disabled");
+ FTS_FUNC_EXIT();
+ return -1;
+ }
+
+ for (i = 0; i < 5; i++) {
+ fts_i2c_write_reg(i2c_client, 0xd1, 0xff);
+ fts_i2c_write_reg(i2c_client, 0xd2, 0xff);
+ fts_i2c_write_reg(i2c_client, 0xd5, 0xff);
+ fts_i2c_write_reg(i2c_client, 0xd6, 0xff);
+ fts_i2c_write_reg(i2c_client, 0xd7, 0xff);
+ fts_i2c_write_reg(i2c_client, 0xd8, 0xff);
+ fts_i2c_write_reg(i2c_client, FTS_REG_GESTURE_EN, 0x01);
+ msleep(1);
+ fts_i2c_read_reg(i2c_client, FTS_REG_GESTURE_EN, &state);
+ if (state == 1)
+ break;
+ }
+
+ if (i >= 5) {
+ FTS_ERROR("[GESTURE]Enter into gesture(suspend) failed!\n");
+ FTS_FUNC_EXIT();
+ return -1;
+ }
+
+ fts_gesture_data.active = 1;
+ FTS_DEBUG("[GESTURE]Enter into gesture(suspend) successfully!");
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_gesture_resume
+* Brief:
+* Input:
+* Output: None
+* Return: None
+*****************************************************************************/
+int fts_gesture_resume(struct i2c_client *client)
+{
+ int i;
+ u8 state;
+
+ FTS_FUNC_ENTER();
+
+ /* gesture not enable, return immediately */
+ if (fts_gesture_data.mode == 0) {
+ FTS_DEBUG("gesture is disabled");
+ FTS_FUNC_EXIT();
+ return -1;
+ }
+
+ fts_gesture_data.active = 0;
+ for (i = 0; i < 5; i++) {
+ fts_i2c_write_reg(client, FTS_REG_GESTURE_EN, 0x00);
+ msleep(1);
+ fts_i2c_read_reg(client, FTS_REG_GESTURE_EN, &state);
+ if (state == 0)
+ break;
+ }
+
+ if (i >= 5)
+ FTS_ERROR("[GESTURE]Clear gesture(resume) failed!\n");
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+#endif
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c
new file mode 100644
index 000000000000..75cd917eae2b
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_i2c.c
@@ -0,0 +1,209 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/************************************************************************
+*
+* File Name: focaltech_i2c.c
+*
+* Author: fupeipei
+*
+* Created: 2016-08-04
+*
+* Abstract: i2c communication with TP
+*
+* Version: v1.0
+*
+* Revision History:
+* v1.0:
+* First release. By fupeipei 2016-08-04
+************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static DEFINE_MUTEX(i2c_rw_access);
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+
+/************************************************************************
+* Name: fts_i2c_read
+* Brief: i2c read
+* Input: i2c info, write buf, write len, read buf, read len
+* Output: get data in the 3rd buf
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_read(struct i2c_client *client, char *writebuf, int writelen,
+ char *readbuf, int readlen)
+{
+ int ret = 0;
+
+ mutex_lock(&i2c_rw_access);
+
+ if (readlen > 0) {
+ if (writelen > 0) {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ if (ret < 0) {
+ FTS_ERROR("[IIC]: i2c_transfer(write)"
+ "error,ret=%d!!", ret);
+ }
+ } else {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = readlen,
+ .buf = readbuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0) {
+ FTS_ERROR("[IIC]: i2c_transfer(read)"
+ "error, ret=%d!!", ret);
+ }
+ }
+ }
+
+ mutex_unlock(&i2c_rw_access);
+ return ret;
+}
+
+/************************************************************************
+* Name: fts_i2c_write
+* Brief: i2c write
+* Input: i2c info, write buf, write len
+* Output: no
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen)
+{
+ int ret = 0;
+
+ mutex_lock(&i2c_rw_access);
+ if (writelen > 0) {
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = writelen,
+ .buf = writebuf,
+ },
+ };
+ ret = i2c_transfer(client->adapter, msgs, 1);
+ if (ret < 0) {
+ FTS_ERROR("%s: i2c_transfer(write) error, ret=%d",
+ __func__, ret);
+ }
+ }
+ mutex_unlock(&i2c_rw_access);
+
+ return ret;
+}
+
+/************************************************************************
+* Name: fts_i2c_write_reg
+* Brief: write register
+* Input: i2c info, reg address, reg value
+* Output: no
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_write_reg(struct i2c_client *client, u8 regaddr, u8 regvalue)
+{
+ u8 buf[2] = { 0 };
+
+ buf[0] = regaddr;
+ buf[1] = regvalue;
+ return fts_i2c_write(client, buf, sizeof(buf));
+}
+
+/************************************************************************
+* Name: fts_i2c_read_reg
+* Brief: read register
+* Input: i2c info, reg address, reg value
+* Output: get reg value
+* Return: fail <0
+***********************************************************************/
+int fts_i2c_read_reg(struct i2c_client *client, u8 regaddr, u8 *regvalue)
+{
+ return fts_i2c_read(client, &regaddr, 1, regvalue, 1);
+}
+
+/************************************************************************
+* Name: fts_i2c_init
+* Brief: fts i2c init
+* Input:
+* Output:
+* Return:
+***********************************************************************/
+int fts_i2c_init(void)
+{
+ FTS_FUNC_ENTER();
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+
+/************************************************************************
+* Name: fts_i2c_exit
+* Brief: fts i2c exit
+* Input:
+* Output:
+* Return:
+***********************************************************************/
+int fts_i2c_exit(void)
+{
+ FTS_FUNC_ENTER();
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c
new file mode 100644
index 000000000000..0fa561748f75
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_point_report_check.c
@@ -0,0 +1,151 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_point_report_check.c
+*
+* Author: WangTao
+*
+* Created: 2016-11-16
+*
+* Abstract: point report check function
+*
+* Version: v1.0
+*
+* Revision History:
+* v1.0:
+* First release. By WangTao 2016-11-16
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_POINT_REPORT_CHECK_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+#define POINT_REPORT_CHECK_WAIT_TIME 200
+
+/*****************************************************************************
+* Private enumerations, structures and unions using typedef
+*****************************************************************************/
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct delayed_work fts_point_report_check_work;
+static struct workqueue_struct *fts_point_report_check_workqueue;
+
+/*****************************************************************************
+* Global variable or extern global variabls/functions
+*****************************************************************************/
+
+/*****************************************************************************
+* Static function prototypes
+*****************************************************************************/
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+
+/*****************************************************************************
+* Name: fts_point_report_check_func
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_point_report_check_func(struct work_struct *work)
+{
+
+#if FTS_MT_PROTOCOL_B_EN
+ unsigned int finger_count = 0;
+
+ FTS_FUNC_ENTER();
+
+ for (finger_count = 0; finger_count < FTS_MAX_POINTS; finger_count++) {
+ input_mt_slot(fts_input_dev, finger_count);
+ input_mt_report_slot_state(fts_input_dev, MT_TOOL_FINGER,
+ false);
+ }
+#else
+ input_mt_sync(fts_input_dev);
+#endif
+ input_report_key(fts_input_dev, BTN_TOUCH, 0);
+ input_sync(fts_input_dev);
+
+ FTS_FUNC_EXIT();
+}
+
+void fts_point_report_check_queue_work(void)
+{
+
+ cancel_delayed_work(&fts_point_report_check_work);
+ queue_delayed_work(fts_point_report_check_workqueue,
+ &fts_point_report_check_work,
+ msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
+
+}
+
+/*****************************************************************************
+* Name: fts_point_report_check_init
+* Brief:
+* Input:
+* Output:
+* Return: < 0: Fail to create esd check queue
+*****************************************************************************/
+int fts_point_report_check_init(void)
+{
+ FTS_FUNC_ENTER();
+
+ INIT_DELAYED_WORK(&fts_point_report_check_work,
+ fts_point_report_check_func);
+ fts_point_report_check_workqueue =
+ create_workqueue("fts_point_report_check_func_wq");
+ if (fts_point_report_check_workqueue == NULL) {
+ FTS_ERROR("[POINT_REPORT]: Failed to create"
+ "fts_point_report_check_workqueue!!");
+ } else {
+ FTS_DEBUG("[POINT_REPORT]: Success to create"
+ "fts_point_report_check_workqueue!!");
+ }
+
+ FTS_FUNC_EXIT();
+
+ return 0;
+}
+
+/*****************************************************************************
+* Name: fts_point_report_check_exit
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+int fts_point_report_check_exit(void)
+{
+ FTS_FUNC_ENTER();
+
+ destroy_workqueue(fts_point_report_check_workqueue);
+
+ FTS_FUNC_EXIT();
+ return 0;
+}
+#endif /* FTS_POINT_REPORT_CHECK_EN */
diff --git a/drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c b/drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c
new file mode 100644
index 000000000000..4a62ee65eaa6
--- /dev/null
+++ b/drivers/input/touchscreen/focaltech_touch/focaltech_sensor.c
@@ -0,0 +1,311 @@
+/*
+ *
+ * FocalTech TouchScreen driver.
+ *
+ * Copyright (c) 2010-2016, FocalTech Systems, Ltd., all rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+/*****************************************************************************
+*
+* File Name: focaltech_esdcheck.c
+*
+* Author: Focaltech Driver Team
+*
+* Created: 2016-08-03
+*
+* Abstract: Sensor
+*
+* Version: v1.0
+*
+* Revision History:
+* v1.0:
+* First release. By luougojin 2016-08-03
+*****************************************************************************/
+
+/*****************************************************************************
+* Included header files
+*****************************************************************************/
+#include "focaltech_core.h"
+
+#if FTS_PSENSOR_EN
+/*****************************************************************************
+* Private constant and macro definitions using #define
+*****************************************************************************/
+/* psensor register address*/
+#define FTS_REG_PSENSOR_ENABLE 0xB0
+#define FTS_REG_PSENSOR_STATUS 0x01
+
+/* psensor register bits*/
+#define FTS_PSENSOR_ENABLE_MASK 0x01
+#define FTS_PSENSOR_STATUS_NEAR 0xC0
+#define FTS_PSENSOR_STATUS_FAR 0xE0
+#define FTS_PSENSOR_FAR_TO_NEAR 0
+#define FTS_PSENSOR_NEAR_TO_FAR 1
+#define FTS_PSENSOR_ORIGINAL_STATE_FAR 1
+#define FTS_PSENSOR_WAKEUP_TIMEOUT 500
+
+/*****************************************************************************
+* Static variables
+*****************************************************************************/
+static struct sensors_classdev __maybe_unused sensors_proximity_cdev = {
+ .name = "fts-proximity",
+ .vendor = "FocalTech",
+ .version = 1,
+ .handle = SENSORS_PROXIMITY_HANDLE,
+ .type = SENSOR_TYPE_PROXIMITY,
+ .max_range = "5.0",
+ .resolution = "5.0",
+ .sensor_power = "0.1",
+ .min_delay = 0,
+ .fifo_reserved_event_count = 0,
+ .fifo_max_event_count = 0,
+ .enabled = 0,
+ .delay_msec = 200,
+ .sensors_enable = NULL,
+ .sensors_poll_delay = NULL,
+};
+
+/*****************************************************************************
+* functions body
+*****************************************************************************/
+/*****************************************************************************
+* Name: fts_psensor_support_enabled
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static inline bool fts_psensor_support_enabled(void)
+{
+ /*return config_enabled(CONFIG_TOUCHSCREEN_FTS_PSENSOR); */
+ return FTS_PSENSOR_EN;
+}
+
+/*****************************************************************************
+* Name: fts_psensor_enable
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static void fts_psensor_enable(struct fts_ts_data *data, int enable)
+{
+ u8 state;
+ int ret = -1;
+
+ if (data->client == NULL)
+ return;
+
+ fts_i2c_read_reg(data->client, FTS_REG_PSENSOR_ENABLE, &state);
+ if (enable)
+ state |= FTS_PSENSOR_ENABLE_MASK;
+ else
+ state &= ~FTS_PSENSOR_ENABLE_MASK;
+
+ ret = fts_i2c_write_reg(data->client, FTS_REG_PSENSOR_ENABLE, state);
+ if (ret < 0)
+ FTS_ERROR("write psensor switch command failed");
+}
+
+/*****************************************************************************
+* Name: fts_psensor_enable_set
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_psensor_enable_set(struct sensors_classdev *sensors_cdev,
+ unsigned int enable)
+{
+ struct fts_psensor_platform_data *psensor_pdata =
+ container_of(sensors_cdev,
+ struct fts_psensor_platform_data, ps_cdev);
+ struct fts_ts_data *data = psensor_pdata->data;
+ struct input_dev *input_dev = data->psensor_pdata->input_psensor_dev;
+
+ mutex_lock(&input_dev->mutex);
+ fts_psensor_enable(data, enable);
+ psensor_pdata->tp_psensor_data = FTS_PSENSOR_ORIGINAL_STATE_FAR;
+ if (enable)
+ psensor_pdata->tp_psensor_opened = 1;
+ else
+ psensor_pdata->tp_psensor_opened = 0;
+ mutex_unlock(&input_dev->mutex);
+ return enable;
+}
+
+/*****************************************************************************
+* Name: fts_read_tp_psensor_data
+* Brief:
+* Input:
+* Output:
+* Return:
+*****************************************************************************/
+static int fts_read_tp_psensor_data(struct fts_ts_data *data)
+{
+ u8 psensor_status;
+ char tmp;
+ int ret = 1;
+
+ fts_i2c_read_reg(data->client, FTS_REG_PSENSOR_STATUS, &psensor_status);
+
+ tmp = data->psensor_pdata->tp_psensor_data;
+ if (psensor_status == FTS_PSENSOR_STATUS_NEAR)
+ data->psensor_pdata->tp_psensor_data = FTS_PSENSOR_FAR_TO_NEAR;
+ else if (psensor_status == FTS_PSENSOR_STATUS_FAR)
+ data->psensor_pdata->tp_psensor_data = FTS_PSENSOR_NEAR_TO_FAR;
+
+ if (tmp != data->psensor_pdata->tp_psensor_data) {
+ FTS_ERROR("%s sensor data changed", __func__);
+ ret = 0;
+ }
+ return ret;
+}
+
+int fts_sensor_read_data(struct fts_ts_data *data)
+{
+ int ret = 0;
+
+ if (fts_psensor_support_enabled()
+ && data->psensor_pdata->tp_psensor_opened) {
+ ret = fts_read_tp_psensor_data(data);
+ if (!ret) {
+ if (data->suspended) {
+ pm_wakeup_event(&data->client->dev,
+ FTS_PSENSOR_WAKEUP_TIMEOUT);
+ }
+ input_report_abs(data->psensor_pdata->input_psensor_dev,
+ ABS_DISTANCE,
+ data->psensor_pdata->tp_psensor_data);
+ input_sync(data->psensor_pdata->input_psensor_dev);
+ }
+ return 1;
+ }
+ return 0;
+}
+
+int fts_sensor_suspend(struct fts_ts_data *data)
+{
+ int ret = 0;
+
+ if (fts_psensor_support_enabled()
+ && device_may_wakeup(&data->client->dev)
+ && data->psensor_pdata->tp_psensor_opened) {
+ ret = enable_irq_wake(data->client->irq);
+ if (ret != 0)
+ FTS_ERROR("%s: set_irq_wake failed", __func__);
+ data->suspended = true;
+ return 1;
+ }
+
+ return 0;
+}
+
+int fts_sensor_resume(struct fts_ts_data *data)
+{
+ int ret = 0;
+
+ if (fts_psensor_support_enabled()
+ && device_may_wakeup(&data->client->dev)
+ && data->psensor_pdata->tp_psensor_opened) {
+ ret = disable_irq_wake(data->client->irq);
+ if (ret)
+ FTS_ERROR("%s: disable_irq_wake failed", __func__);
+ data->suspended = false;
+ return 1;
+ }
+
+ return 0;
+}
+
+int fts_sensor_init(struct fts_ts_data *data)
+{
+ struct fts_psensor_platform_data *psensor_pdata;
+ struct input_dev *psensor_input_dev;
+ int err;
+
+ if (fts_psensor_support_enabled()) {
+ device_init_wakeup(&data->client->dev, 1);
+ psensor_pdata =
+ devm_kzalloc(&data->client->dev,
+ sizeof(struct fts_psensor_platform_data),
+ GFP_KERNEL);
+ if (!psensor_pdata) {
+ FTS_ERROR("Failed to allocate memory");
+ goto irq_free;
+ }
+ data->psensor_pdata = psensor_pdata;
+
+ psensor_input_dev = input_allocate_device();
+ if (!psensor_input_dev) {
+ FTS_ERROR("Failed to allocate device");
+ goto free_psensor_pdata;
+ }
+
+ __set_bit(EV_ABS, psensor_input_dev->evbit);
+ input_set_abs_params(psensor_input_dev, ABS_DISTANCE, 0, 1, 0,
+ 0);
+ psensor_input_dev->name = "proximity";
+ psensor_input_dev->id.bustype = BUS_I2C;
+ psensor_input_dev->dev.parent = &data->client->dev;
+ data->psensor_pdata->input_psensor_dev = psensor_input_dev;
+
+ err = input_register_device(psensor_input_dev);
+ if (err) {
+ FTS_ERROR("Unable to register device, err=%d", err);
+ goto free_psensor_input_dev;
+ }
+
+ psensor_pdata->ps_cdev = sensors_proximity_cdev;
+ psensor_pdata->ps_cdev.sensors_enable = fts_psensor_enable_set;
+ psensor_pdata->data = data;
+
+ err =
+ sensors_classdev_register(&data->client->dev,
+ &psensor_pdata->ps_cdev);
+ if (err)
+ goto unregister_psensor_input_device;
+ }
+
+ return 0;
+unregister_psensor_input_device:
+ if (fts_psensor_support_enabled())
+ input_unregister_device(data->psensor_pdata->input_psensor_dev);
+free_psensor_input_dev:
+ if (fts_psensor_support_enabled())
+ input_free_device(data->psensor_pdata->input_psensor_dev);
+free_psensor_pdata:
+ if (fts_psensor_support_enabled()) {
+ devm_kfree(&data->client->dev, psensor_pdata);
+ data->psensor_pdata = NULL;
+ }
+irq_free:
+ if (fts_psensor_support_enabled())
+ device_init_wakeup(&data->client->dev, 0);
+ free_irq(data->client->irq, data);
+
+ return 1;
+}
+
+int fts_sensor_remove(struct fts_ts_data *data)
+{
+ if (fts_psensor_support_enabled()) {
+ device_init_wakeup(&data->client->dev, 0);
+ sensors_classdev_unregister(&data->psensor_pdata->ps_cdev);
+ input_unregister_device(data->psensor_pdata->input_psensor_dev);
+ devm_kfree(&data->client->dev, data->psensor_pdata);
+ data->psensor_pdata = NULL;
+ }
+ return 0;
+}
+#endif /* FTS_PSENSOR_EN */
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index 4f53d3c57e69..026c6404168c 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -911,7 +911,7 @@ retry_get_irq_gpio:
default:
if (ts->gpiod_int && ts->gpiod_rst) {
ts->reset_controller_at_probe = true;
- ts->load_cfg_from_disk = true;
+ ts->load_cfg_from_disk = false;
ts->irq_pin_access_method = IRQ_PIN_ACCESS_GPIO;
}
}
@@ -932,7 +932,7 @@ static void goodix_read_config(struct goodix_ts_data *ts)
int error;
error = goodix_i2c_read(ts->client, ts->chip->config_addr,
- ts->config, ts->chip->config_len);
+ ts->config, 9);
if (error) {
dev_warn(&ts->client->dev, "Error reading config: %d\n",
error);
@@ -1106,6 +1106,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
return error;
}
+ if (device_property_read_bool(ts->input_dev->dev.parent, "edge-failling-trigger"))
+ ts->int_trigger_type = GOODIX_INT_TRIGGER;
+
ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
error = goodix_request_irq(ts);
if (error) {
@@ -1355,9 +1358,11 @@ static int __maybe_unused goodix_resume(struct device *dev)
return error;
}
- error = goodix_send_cfg(ts, ts->config, ts->chip->config_len);
- if (error)
- return error;
+ if (ts->load_cfg_from_disk) {
+ error = goodix_send_cfg(ts, ts->config, ts->chip->config_len);
+ if (error)
+ return error;
+ }
}
error = goodix_request_irq(ts);
diff --git a/drivers/input/touchscreen/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index f15713aaebc2..7db941549e02 100644
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -3,7 +3,7 @@
* Driver for MAXI MAX11801 - A Resistive touch screen controller with
* i2c interface
*
- * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ * Copyright (C) 2011-2015 Freescale Semiconductor, Inc.
* Author: Zhang Jiejing <jiejing.zhang@freescale.com>
*
* Based on mcs5000_ts.c
@@ -34,6 +34,10 @@
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
/* Register Address define */
#define GENERNAL_STATUS_REG 0x00
@@ -49,13 +53,30 @@
#define AUX_MESURE_CONF_REG 0x0a
#define OP_MODE_CONF_REG 0x0b
+#define Panel_Setup_X (0x69 << 1)
+#define Panel_Setup_Y (0x6b << 1)
+
+#define XY_combined_measurement (0x70 << 1)
+#define X_measurement (0x78 << 1)
+#define Y_measurement (0x7a << 1)
+#define AUX_measurement (0x76 << 1)
+
/* FIFO is found only in max11800 and max11801 */
#define FIFO_RD_CMD (0x50 << 1)
#define MAX11801_FIFO_INT (1 << 2)
#define MAX11801_FIFO_OVERFLOW (1 << 3)
+#define MAX11801_EDGE_INT (1 << 1)
+
+#define FIFO_RD_X_MSB (0x52 << 1)
+#define FIFO_RD_X_LSB (0x53 << 1)
+#define FIFO_RD_Y_MSB (0x54 << 1)
+#define FIFO_RD_Y_LSB (0x55 << 1)
+#define FIFO_RD_AUX_MSB (0x5a << 1)
+#define FIFO_RD_AUX_LSB (0x5b << 1)
#define XY_BUFSIZE 4
#define XY_BUF_OFFSET 4
+#define AUX_BUFSIZE 2
#define MAX11801_MAX_X 0xfff
#define MAX11801_MAX_Y 0xfff
@@ -80,6 +101,64 @@ struct max11801_data {
struct input_dev *input_dev;
};
+static struct i2c_client *max11801_client;
+static unsigned int max11801_workmode;
+static u8 aux_buf[AUX_BUFSIZE];
+
+static int max11801_dcm_write_command(struct i2c_client *client, int command)
+{
+ return i2c_smbus_write_byte(client, command);
+}
+
+static u32 max11801_dcm_sample_aux(struct i2c_client *client)
+{
+ int ret;
+ int aux = 0;
+ u32 sample_data;
+
+ /* AUX_measurement */
+ max11801_dcm_write_command(client, AUX_measurement);
+ mdelay(5);
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_MSB,
+ 1, &aux_buf[0]);
+ if (ret < 1) {
+ dev_err(&client->dev, "FIFO_RD_AUX_MSB read fails\n");
+ return ret;
+ }
+ mdelay(5);
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_LSB,
+ 1, &aux_buf[1]);
+ if (ret < 1) {
+ dev_err(&client->dev, "FIFO_RD_AUX_LSB read fails\n");
+ return ret;
+ }
+
+ aux = (aux_buf[0] << 4) + (aux_buf[1] >> 4);
+ /*
+ * voltage = (9170*aux)/7371;
+ * voltage is (26.2*3150*aux)/(16.2*0xFFF)
+ * V(aux)=3150*sample/0xFFF,V(battery)=212*V(aux)/81
+ * sample_data = (14840*aux)/7371-1541;
+ */
+ sample_data = (14840 * aux) / 7371;
+
+ return sample_data;
+}
+
+u32 max11801_read_adc(void)
+{
+ u32 adc_data;
+
+ if (!max11801_client) {
+ pr_err("FAIL max11801_client not initialize\n");
+ return -1;
+ }
+ adc_data = max11801_dcm_sample_aux(max11801_client);
+
+ return adc_data;
+}
+EXPORT_SYMBOL_GPL(max11801_read_adc);
+
static u8 read_register(struct i2c_client *client, int addr)
{
/* XXX: The chip ignores LSB of register address */
@@ -100,21 +179,52 @@ static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
u8 buf[XY_BUFSIZE];
int x = -1;
int y = -1;
+ u8 command = FIFO_RD_X_MSB;
status = read_register(data->client, GENERNAL_STATUS_REG);
-
- if (status & (MAX11801_FIFO_INT | MAX11801_FIFO_OVERFLOW)) {
+ if ((!max11801_workmode && (status & (MAX11801_FIFO_INT |
+ MAX11801_FIFO_OVERFLOW))) || (max11801_workmode && (status &
+ MAX11801_EDGE_INT))) {
status = read_register(data->client, GENERNAL_STATUS_REG);
-
- ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
- XY_BUFSIZE, buf);
-
- /*
- * We should get 4 bytes buffer that contains X,Y
- * and event tag
- */
- if (ret < XY_BUFSIZE)
- goto out;
+ if (!max11801_workmode) {
+ /* ACM mode */
+ ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_CMD,
+ XY_BUFSIZE, buf);
+ /*
+ * We should get 4 bytes buffer that contains X,Y
+ * and event tag
+ */
+ if (ret < XY_BUFSIZE)
+ goto out;
+ } else {
+ /* DCM mode */
+ /* X = panel setup */
+ max11801_dcm_write_command(client, Panel_Setup_X);
+ /* X_measurement */
+ max11801_dcm_write_command(client, X_measurement);
+ for (i = 0; i < 2; i++) {
+ ret = i2c_smbus_read_i2c_block_data(client,
+ command, 1, &buf[i]);
+ if (ret < 1)
+ goto out;
+
+ command = FIFO_RD_X_LSB;
+ }
+
+ /* Y = panel setup */
+ max11801_dcm_write_command(client, Panel_Setup_Y);
+ /* Y_measurement */
+ max11801_dcm_write_command(client, Y_measurement);
+ command = FIFO_RD_Y_MSB;
+ for (i = 2; i < XY_BUFSIZE; i++) {
+ ret = i2c_smbus_read_i2c_block_data(client,
+ command, 1, &buf[i]);
+ if (ret < 1)
+ goto out;
+
+ command = FIFO_RD_Y_LSB;
+ }
+ }
for (i = 0; i < XY_BUFSIZE; i += XY_BUFSIZE / 2) {
if ((buf[i + 1] & MEASURE_TAG_MASK) == MEASURE_X_TAG)
@@ -132,6 +242,7 @@ static irqreturn_t max11801_ts_interrupt(int irq, void *dev_id)
case EVENT_INIT:
case EVENT_MIDDLE:
input_report_abs(data->input_dev, ABS_X, x);
+ y = MAX11801_MAX_Y - y; /* Calibration */
input_report_abs(data->input_dev, ABS_Y, y);
input_event(data->input_dev, EV_KEY, BTN_TOUCH, 1);
input_sync(data->input_dev);
@@ -154,7 +265,8 @@ static void max11801_ts_phy_init(struct max11801_data *data)
{
struct i2c_client *client = data->client;
- /* Average X,Y, take 16 samples, average eight media sample */
+ max11801_client = client;
+ /* Average X,Y, take 16 samples average eight media sample */
max11801_write_reg(client, MESURE_AVER_CONF_REG, 0xff);
/* X,Y panel setup time set to 20us */
max11801_write_reg(client, PANEL_SETUPTIME_CONF_REG, 0x11);
@@ -165,7 +277,22 @@ static void max11801_ts_phy_init(struct max11801_data *data)
/* Aperture X,Y set to +- 4LSB */
max11801_write_reg(client, APERTURE_CONF_REG, 0x33);
/* Enable Power, enable Automode, enable Aperture, enable Average X,Y */
- max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+ if (!max11801_workmode)
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x36);
+ else {
+ max11801_write_reg(client, OP_MODE_CONF_REG, 0x16);
+ /*
+ * Delay initial=1ms, Sampling time 2us
+ * Averaging sample depth 2
+ * samples, Resolution 12bit
+ */
+ max11801_write_reg(client, AUX_MESURE_CONF_REG, 0x76);
+ /*
+ * Use edge interrupt with
+ * direct conversion mode
+ */
+ max11801_write_reg(client, GENERNAL_CONF_REG, 0xf3);
+ }
}
static int max11801_ts_probe(struct i2c_client *client,
@@ -174,6 +301,7 @@ static int max11801_ts_probe(struct i2c_client *client,
struct max11801_data *data;
struct input_dev *input_dev;
int error;
+ struct device_node *of_node = client->dev.of_node;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
input_dev = devm_input_allocate_device(&client->dev);
@@ -195,6 +323,9 @@ static int max11801_ts_probe(struct i2c_client *client,
input_set_abs_params(input_dev, ABS_X, 0, MAX11801_MAX_X, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, MAX11801_MAX_Y, 0, 0);
+ if (of_property_read_u32(of_node, "work-mode", &max11801_workmode))
+ max11801_workmode = *(int *)(client->dev).platform_data;
+
max11801_ts_phy_init(data);
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
diff --git a/drivers/input/touchscreen/synaptics_dsx/Kconfig b/drivers/input/touchscreen/synaptics_dsx/Kconfig
new file mode 100644
index 000000000000..6410f7ec7cd1
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/Kconfig
@@ -0,0 +1,27 @@
+#
+# 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 I2C touchscreen
+ connected to your system
+
+ if unsure, say N.
+
+if TOUCHSCREEN_SYNAPTICS_DSX
+
+config TOUCHSCREEN_SYNAPTICS_DSX_I2C
+ tristate "Synaptics DSX core driver module"
+ depends on I2C
+ help
+ Say Y here if you have a Synaptics DSX I2C touchscreen
+ connected to your system
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_dsx_i2c.
+
+endif
diff --git a/drivers/input/touchscreen/synaptics_dsx/Makefile b/drivers/input/touchscreen/synaptics_dsx/Makefile
new file mode 100644
index 000000000000..b46f3df6d9d5
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/Makefile
@@ -0,0 +1,2 @@
+obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_I2C) += synaptics_dsx_i2c.o
+
diff --git a/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx.h
new file mode 100644
index 000000000000..5adeb65b6b3f
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx.h
@@ -0,0 +1,86 @@
+/*
+ * Synaptics DSX touchscreen driver
+ *
+ * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
+ *
+ * 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.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+
+#ifndef _SYNAPTICS_DSX_H_
+#define _SYNAPTICS_DSX_H_
+
+/*
+ * 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 int nbuttons;
+ unsigned int *map;
+};
+
+/*
+ * struct synaptics_dsx_platform_data - dsx platform 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
+ * @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_platform_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 char *regulator_name;
+ int (*gpio_config)(int gpio, bool configure, int dir, int state);
+ struct synaptics_dsx_cap_button_map *cap_button_map;
+};
+
+#endif
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 100644
index 000000000000..2704b4f0f100
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.c
@@ -0,0 +1,3623 @@
+/*
+ * Synaptics DSX touchscreen driver
+ *
+ * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
+ *
+ * Copyright (C) 2012 Alexandra Chin <alexandra.chin@tw.synaptics.com>
+ * Copyright (C) 2012 Scott Lin <scott.lin@tw.synaptics.com>
+ * Copyright 2018 NXP
+ *
+ * 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.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/stringify.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/dma-mapping.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+
+#include "synaptics_dsx_i2c.h"
+#include "synaptics_dsx.h"
+
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+#include <linux/input/mt.h>
+
+
+#define DRIVER_NAME "synaptics_dsx_i2c"
+#define INPUT_PHYS_NAME "synaptics_dsx_i2c/input0"
+
+#define TYPE_B_PROTOCOL
+
+#define UBL_I2C_ADDR 0x2c
+
+#define SENSOR_MAX_X 1080
+#define SENSOR_MAX_Y 1920
+
+#define WAKEUP_GESTURE false
+
+#define NO_0D_WHILE_2D
+#define REPORT_2D_W
+
+#define F12_DATA_15_WORKAROUND
+
+#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 attrify(propname) (&dev_attr_##propname.attr)
+
+#define EXP_FN_WORK_DELAY_MS 1000 /* ms */
+#define SYN_I2C_RETRY_TIMES 5
+#define MAX_F11_TOUCH_WIDTH 15
+
+#define CHECK_STATUS_TIMEOUT_MS 100
+#define DELAY_BOOT_READY 200
+#define DELAY_RESET_LOW 20
+#define DELAY_UI_READY 200
+
+#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)
+
+
+#define F11_CONTINUOUS_MODE 0x00
+#define F11_WAKEUP_GESTURE_MODE 0x04
+#define F12_CONTINUOUS_MODE 0x00
+#define F12_WAKEUP_GESTURE_MODE 0x02
+
+#define F12_UDG_DETECT 0x0f
+
+#ifdef USE_I2C_DMA
+#include <linux/dma-mapping.h>
+static unsigned char *wDMABuf_va;
+static dma_addr_t wDMABuf_pa;
+#endif
+
+static struct task_struct *thread;
+static DECLARE_WAIT_QUEUE_HEAD(waiter);
+static int tpd_flag;
+DEFINE_MUTEX(rmi4_report_mutex);
+static struct device *g_dev;
+
+/* for 0D button */
+static unsigned int cap_button_codes[] = {KEY_APPSELECT, KEY_HOMEPAGE, KEY_BACK};
+static struct synaptics_dsx_cap_button_map cap_button_map = {
+ .nbuttons = ARRAY_SIZE(cap_button_codes),
+ .map = cap_button_codes,
+};
+
+#ifdef CONFIG_OF_TOUCH
+int touch_irq;
+#endif
+
+#ifdef CONFIG_OF_TOUCH
+static irqreturn_t tpd_eint_handler(int irq, void *desc);
+#else
+static void tpd_eint_handler(void);
+#endif
+
+static int touch_event_handler(void *data);
+
+static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data,
+ unsigned short length);
+
+static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data,
+ unsigned short length);
+
+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);
+
+
+static int __maybe_unused synaptics_rmi4_suspend(struct device *dev);
+
+static int __maybe_unused 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);
+
+static ssize_t synaptics_rmi4_wake_gesture_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t synaptics_rmi4_wake_gesture_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_f11_query_0_5 {
+ union {
+ struct {
+ /* query 0 */
+ unsigned char f11_query0_b0__2:3;
+ unsigned char has_query_9:1;
+ unsigned char has_query_11:1;
+ unsigned char has_query_12:1;
+ unsigned char has_query_27:1;
+ unsigned char has_query_28:1;
+
+ /* query 1 */
+ unsigned char num_of_fingers:3;
+ unsigned char has_rel:1;
+ unsigned char has_abs:1;
+ unsigned char has_gestures:1;
+ unsigned char has_sensitibity_adjust:1;
+ unsigned char f11_query1_b7:1;
+
+ /* query 2 */
+ unsigned char num_of_x_electrodes;
+
+ /* query 3 */
+ unsigned char num_of_y_electrodes;
+
+ /* query 4 */
+ unsigned char max_electrodes:7;
+ unsigned char f11_query4_b7:1;
+
+ /* query 5 */
+ unsigned char abs_data_size:2;
+ unsigned char has_anchored_finger:1;
+ unsigned char has_adj_hyst:1;
+ unsigned char has_dribble:1;
+ unsigned char has_bending_correction:1;
+ unsigned char has_large_object_suppression:1;
+ unsigned char has_jitter_filter:1;
+ } __packed;
+ unsigned char data[6];
+ };
+};
+
+struct synaptics_rmi4_f11_query_7_8 {
+ union {
+ struct {
+ /* query 7 */
+ unsigned char has_single_tap:1;
+ unsigned char has_tap_and_hold:1;
+ unsigned char has_double_tap:1;
+ unsigned char has_early_tap:1;
+ unsigned char has_flick:1;
+ unsigned char has_press:1;
+ unsigned char has_pinch:1;
+ unsigned char has_chiral_scroll:1;
+
+ /* query 8 */
+ unsigned char has_palm_detect:1;
+ unsigned char has_rotate:1;
+ unsigned char has_touch_shapes:1;
+ unsigned char has_scroll_zones:1;
+ unsigned char individual_scroll_zones:1;
+ unsigned char has_multi_finger_scroll:1;
+ unsigned char has_multi_finger_scroll_edge_motion:1;
+ unsigned char has_multi_finger_scroll_inertia:1;
+ } __packed;
+ unsigned char data[2];
+ };
+};
+
+struct synaptics_rmi4_f11_query_9 {
+ union {
+ struct {
+ unsigned char has_pen:1;
+ unsigned char has_proximity:1;
+ unsigned char has_large_object_sensitivity:1;
+ unsigned char has_suppress_on_large_object_detect:1;
+ unsigned char has_two_pen_thresholds:1;
+ unsigned char has_contact_geometry:1;
+ unsigned char has_pen_hover_discrimination:1;
+ unsigned char has_pen_hover_and_edge_filters:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f11_query_12 {
+ union {
+ struct {
+ unsigned char has_small_object_detection:1;
+ unsigned char has_small_object_detection_tuning:1;
+ unsigned char has_8bit_w:1;
+ unsigned char has_2d_adjustable_mapping:1;
+ unsigned char has_general_information_2:1;
+ unsigned char has_physical_properties:1;
+ unsigned char has_finger_limit:1;
+ unsigned char has_linear_cofficient_2:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f11_query_27 {
+ union {
+ struct {
+ unsigned char f11_query27_b0:1;
+ unsigned char has_pen_position_correction:1;
+ unsigned char has_pen_jitter_filter_coefficient:1;
+ unsigned char has_group_decomposition:1;
+ unsigned char has_wakeup_gesture:1;
+ unsigned char has_small_finger_correction:1;
+ unsigned char has_data_37:1;
+ unsigned char f11_query27_b7:1;
+ } __packed;
+ unsigned char data[1];
+ };
+};
+
+struct synaptics_rmi4_f11_ctrl_6_9 {
+ union {
+ struct {
+ unsigned char sensor_max_x_pos_7_0;
+ unsigned char sensor_max_x_pos_11_8:4;
+ unsigned char f11_ctrl7_b4__7:4;
+ unsigned char sensor_max_y_pos_7_0;
+ unsigned char sensor_max_y_pos_11_8:4;
+ unsigned char f11_ctrl9_b4__7:4;
+ } __packed;
+ unsigned char data[4];
+ };
+};
+
+struct synaptics_rmi4_f11_data_1_5 {
+ union {
+ struct {
+ unsigned char x_position_11_4;
+ unsigned char y_position_11_4;
+ unsigned char x_position_3_0:4;
+ unsigned char y_position_3_0:4;
+ unsigned char wx:4;
+ unsigned char wy:4;
+ unsigned char z;
+ } __packed;
+ unsigned char data[5];
+ };
+};
+
+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 int max_count;
+ unsigned char valid_button_count;
+ unsigned char *button_data_buffer;
+ unsigned int *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[] = {
+ __ATTR(reset, 0660,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_f01_reset_store),
+ __ATTR(productinfo, 0444,
+ synaptics_rmi4_f01_productinfo_show,
+ synaptics_rmi4_store_error),
+ __ATTR(buildid, 0444,
+ synaptics_rmi4_f01_buildid_show,
+ synaptics_rmi4_store_error),
+ __ATTR(flashprog, 0444,
+ synaptics_rmi4_f01_flashprog_show,
+ synaptics_rmi4_store_error),
+ __ATTR(0dbutton, 0660,
+ synaptics_rmi4_0dbutton_show,
+ synaptics_rmi4_0dbutton_store),
+ __ATTR(suspend, 0660,
+ synaptics_rmi4_show_error,
+ synaptics_rmi4_suspend_store),
+ __ATTR(wake_gesture, 0660,
+ synaptics_rmi4_wake_gesture_show,
+ synaptics_rmi4_wake_gesture_store),
+};
+
+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(dev,
+ "%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_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ device_status.data,
+ sizeof(device_status.data));
+ if (retval < 0) {
+ dev_err(dev,
+ "%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_i2c_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_i2c_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;
+}
+
+
+static ssize_t synaptics_rmi4_wake_gesture_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->enable_wakeup_gesture);
+}
+
+static ssize_t synaptics_rmi4_wake_gesture_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;
+
+ input = input > 0 ? 1 : 0;
+
+ if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture)
+ rmi4_data->enable_wakeup_gesture = input;
+
+ return count;
+}
+
+static int tpd_set_page(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr)
+{
+ int retval = 0;
+ unsigned char retry;
+ unsigned char buf[PAGE_SELECT_LEN];
+ unsigned char page;
+ struct i2c_client *i2c = rmi4_data->i2c_client;
+
+ 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->i2c_client->dev,
+ "%s: I2C retry %d\n", __func__, retry + 1);
+ msleep(20);
+ if (retry == (SYN_I2C_RETRY_TIMES / 2)) {
+ if (i2c->addr == rmi4_data->i2c_addr)
+ i2c->addr = UBL_I2C_ADDR;
+ else
+ i2c->addr = rmi4_data->i2c_addr;
+ }
+ } else {
+ rmi4_data->current_page = page;
+ break;
+ }
+ }
+ } else {
+ retval = PAGE_SELECT_LEN;
+ }
+
+ return retval;
+}
+
+
+int tpd_i2c_read_data(struct synaptics_rmi4_data *rmi4_data, struct i2c_client *client,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+
+ unsigned char retry = 0;
+ unsigned char *pData = data;
+ unsigned char tmp_addr = (unsigned char)addr;
+ int retval = 0;
+ int left_len = length;
+
+ /* u16 old_flag; */
+ mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ retval = tpd_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ retval = -EIO;
+ goto exit;
+ }
+
+ retval = i2c_master_send(client, &tmp_addr, 1);
+ while (left_len > 0) {
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ if (left_len > 8)
+ retval = i2c_master_recv(client, pData, 8);
+ else
+ retval = i2c_master_recv(client, pData, left_len);
+ if (retval <= 0) {
+ dev_err(&client->dev, "%s: I2C retry %d\n", __func__, retry + 1);
+ msleep(20);
+ if (retry == (SYN_I2C_RETRY_TIMES / 2)) {
+ if (client->addr == rmi4_data->i2c_addr)
+ client->addr = UBL_I2C_ADDR;
+ else
+ client->addr = rmi4_data->i2c_addr;
+ }
+ left_len = length;
+ pData = data;
+ retval = i2c_master_send(client, &tmp_addr, 1);
+ continue;
+ } else {
+ break;
+ }
+ }
+ if (retry == SYN_I2C_RETRY_TIMES) {
+ retval = -EIO;
+ goto exit;
+ }
+ left_len -= 8;
+ pData += 8;
+ }
+exit:
+ mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ return retval;
+}
+EXPORT_SYMBOL(tpd_i2c_read_data);
+
+#ifdef USE_I2C_DMA
+int tpd_i2c_write_data_dma(struct synaptics_rmi4_data *rmi4_data, struct i2c_client *client,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval = 0;
+ unsigned char retry;
+ unsigned char *buf_va = NULL;
+ struct i2c_msg msg[1];
+
+ mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ msg[0].addr = rmi4_data->i2c_client->addr;
+ msg[0].flags = 0;
+ msg[0].len = length + 1;
+ msg[0].ext_flag = (rmi4_data->i2c_client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG),
+ msg[0].buf = (unsigned char *)(uintptr_t)wDMABuf_pa;
+ msg[0].timing = 400;
+
+ retval = tpd_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ retval = -EIO;
+ goto exit;
+ }
+
+ buf_va = wDMABuf_va;
+ buf_va[0] = addr & MASK_8BIT;
+ memcpy(&buf_va[1], &data[0], length);
+
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) {
+ retval = length;
+ break;
+ }
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: I2C retry %d\n",
+ __func__, retry + 1);
+ msleep(20);
+
+ if (retry == (SYN_I2C_RETRY_TIMES / 2)) {
+ if (rmi4_data->i2c_client->addr == rmi4_data->i2c_addr)
+ rmi4_data->i2c_client->addr = UBL_I2C_ADDR;
+ else
+ rmi4_data->i2c_client->addr = rmi4_data->i2c_addr;
+ msg[0].addr = rmi4_data->i2c_client->addr;
+ }
+ }
+
+ if (retry == SYN_I2C_RETRY_TIMES) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: I2C write over retry limit\n",
+ __func__);
+ retval = -EIO;
+ }
+
+exit:
+ mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ return retval;
+}
+EXPORT_SYMBOL(tpd_i2c_write_data_dma);
+
+#else
+int tpd_i2c_write_data(struct synaptics_rmi4_data *rmi4_data, struct i2c_client *client,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ int retval = 0;
+ u8 retry = 0;
+ u8 *buf;
+ int tmp_addr = addr;
+
+
+ mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ retval = tpd_set_page(rmi4_data, addr);
+ if (retval != PAGE_SELECT_LEN) {
+ pr_err("tpd_set_page fail, retval = %d\n", retval);
+ retval = -EIO;
+ goto exit;
+ }
+
+ buf = kzalloc(sizeof(unsigned char) * (length + 1), GFP_KERNEL);
+ *buf = tmp_addr;
+ memcpy(buf + 1, data, length);
+ for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) {
+ retval = i2c_master_send(client, buf, (length + 1));
+ if (retval <= 0) {
+ dev_err(&client->dev, "%s: I2C retry %d\n", __func__, retry + 1);
+ msleep(20);
+ continue;
+ } else {
+ break;
+ }
+ }
+ kfree(buf);
+
+exit:
+ mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex));
+
+ return retval;
+}
+EXPORT_SYMBOL(tpd_i2c_write_data);
+
+#endif
+
+ /**
+ * synaptics_rmi4_i2c_read()
+ *
+ * Called by various functions in this driver, and also exported to
+ * other expansion Function modules such as rmi_dev.
+ *
+ * This function reads data of an arbitrary length from the sensor,
+ * starting from an assigned register address of the sensor, via I2C
+ * with a retry mechanism.
+ */
+static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+ return tpd_i2c_read_data(rmi4_data, rmi4_data->i2c_client, addr, data, length);
+}
+
+ /**
+ * synaptics_rmi4_i2c_write()
+ *
+ * Called by various functions in this driver, and also exported to
+ * other expansion Function modules such as rmi_dev.
+ *
+ * This function writes data of an arbitrary length to the sensor,
+ * starting from an assigned register address of the sensor, via I2C with
+ * a retry mechanism.
+ */
+static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data,
+ unsigned short addr, unsigned char *data, unsigned short length)
+{
+#ifdef USE_I2C_DMA
+ return tpd_i2c_write_data_dma(rmi4_data, rmi4_data->i2c_client, addr, data, length);
+#else
+ return tpd_i2c_write_data(rmi4_data, rmi4_data->i2c_client, addr, data, length);
+#endif
+}
+
+ /**
+ * 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 finger_status_reg[3];
+ unsigned char detected_gestures;
+ unsigned short data_addr;
+ unsigned short data_offset;
+ int x;
+ int y;
+ int wx;
+ int wy;
+ //int temp;
+ struct synaptics_rmi4_f11_data_1_5 data;
+ struct synaptics_rmi4_f11_extra_data *extra_data;
+
+ /*
+ * 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;
+
+ extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra;
+
+ if (rmi4_data->sensor_sleep && rmi4_data->enable_wakeup_gesture) {
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_addr + extra_data->data38_offset,
+ &detected_gestures,
+ sizeof(detected_gestures));
+ if (retval < 0)
+ return 0;
+
+ if (detected_gestures) {
+ input_report_key(rmi4_data->input_dev, KEY_POWER, 1);
+ input_sync(rmi4_data->input_dev);
+ input_report_key(rmi4_data->input_dev, KEY_POWER, 0);
+ input_sync(rmi4_data->input_dev);
+ rmi4_data->sensor_sleep = false;
+ }
+
+ return 0;
+ }
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_addr,
+ finger_status_reg,
+ num_of_finger_status_regs);
+ if (retval < 0)
+ return 0;
+ mutex_lock(&rmi4_report_mutex);
+ 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 * sizeof(data.data));
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_offset,
+ data.data,
+ sizeof(data.data));
+ if (retval < 0) {
+ touch_count = 0;
+ goto exit;
+ }
+
+ x = (data.x_position_11_4 << 4) | data.x_position_3_0;
+ y = (data.y_position_11_4 << 4) | data.y_position_3_0;
+ if (rmi4_data->diagonal_rotation) {
+ x = rmi4_data->sensor_max_x - x;
+ y = rmi4_data->sensor_max_y - y;
+ }
+
+ wx = data.wx;
+ wy = data.wy;
+
+ 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->i2c_client->dev,
+ "%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);
+exit:
+ mutex_unlock(&(rmi4_report_mutex));
+ 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 char detected_gestures[F12_GESTURE_DETECTION_LEN]; // byte0 indicate gesture type, byte1~byte4 are gesture parameter
+ 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);
+
+
+ if (rmi4_data->sensor_sleep && rmi4_data->enable_wakeup_gesture) {
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ data_addr + extra_data->data4_offset,
+ detected_gestures,
+ sizeof(detected_gestures));
+ if (retval < 0)
+ return 0;
+
+ if (detected_gestures[0] && (detected_gestures[0] != F12_UDG_DETECT)) { // here is demo only, customer could decode gesture data and do whatever they want
+ input_report_key(rmi4_data->input_dev, KEY_POWER, 1);
+ input_sync(rmi4_data->input_dev);
+ input_report_key(rmi4_data->input_dev, KEY_POWER, 0);
+ input_sync(rmi4_data->input_dev);
+ rmi4_data->sensor_sleep = false;
+ }
+
+ return 0;
+ }
+
+
+ /* Determine the total number of fingers to process */
+ if (extra_data->data15_size) {
+ retval = synaptics_rmi4_i2c_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->i2c_client->dev,
+ "%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_i2c_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;
+ mutex_lock(&rmi4_report_mutex);
+ for (finger = 0; finger < fingers_to_process; finger++) {
+ finger_data = data + finger;
+ finger_status = finger_data->object_type_and_status & MASK_1BIT;
+
+#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) {
+#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);
+ if (rmi4_data->diagonal_rotation) {
+ x = rmi4_data->sensor_max_x - x;
+ y = rmi4_data->sensor_max_y - y;
+ }
+
+#ifdef REPORT_2D_W
+ wx = finger_data->wx;
+ wy = finger_data->wy;
+#endif
+
+ 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->i2c_client->dev,
+ "%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);
+ mutex_unlock(&rmi4_report_mutex);
+ 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_i2c_read(rmi4_data,
+ data_addr,
+ f1a->button_data_buffer,
+ f1a->button_bitmask_size);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read button data registers\n",
+ __func__);
+ return;
+ }
+
+ data = f1a->button_data_buffer;
+ mutex_lock(&rmi4_report_mutex);
+ 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->i2c_client->dev,
+ "%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);
+ mutex_unlock(&rmi4_report_mutex);
+ 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->i2c_client->dev,
+ "%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_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr,
+ data,
+ rmi4_data->num_of_intr_regs + 1);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%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->i2c_client->dev,
+ "%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.
+ */
+#ifdef CONFIG_OF_TOUCH
+static irqreturn_t tpd_eint_handler(int irq, void *desc)
+{
+ disable_irq_nosync(touch_irq);
+
+ tpd_flag = 1;
+ wake_up_interruptible(&waiter);
+ return IRQ_HANDLED;
+}
+#else
+static void tpd_eint_handler(void)
+{
+ tpd_flag = 1;
+ wake_up_interruptible(&waiter);
+}
+#endif
+
+static int touch_event_handler(void *data)
+{
+ struct synaptics_rmi4_data *rmi4_data = data;
+
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ wait_event_interruptible(waiter, tpd_flag != 0);
+
+ tpd_flag = 0;
+ set_current_state(TASK_RUNNING);
+
+ if (!rmi4_data->touch_stopped)
+ synaptics_rmi4_sensor_report(rmi4_data);
+#ifdef CONFIG_OF_TOUCH
+ enable_irq(touch_irq);
+#else
+ mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#endif
+
+ } while (1);
+
+ return 0;
+}
+
+ /**
+ * 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;
+
+ if (enable) {
+ /* set up irq */
+ if (!rmi4_data->irq_enabled) {
+#ifdef CONFIG_OF_TOUCH
+ enable_irq(touch_irq);
+#else
+ mt_eint_unmask(CUST_EINT_TOUCH_PANEL_NUM);
+#endif
+ rmi4_data->irq_enabled = true;
+
+ }
+
+ } else {
+ if (rmi4_data->irq_enabled) {
+#ifdef CONFIG_OF_TOUCH
+ disable_irq_nosync(touch_irq);
+#else
+ mt_eint_mask(CUST_EINT_TOUCH_PANEL_NUM);
+#endif
+ 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 function 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 offset;
+ unsigned char fingers_supported;
+ struct synaptics_rmi4_f11_extra_data *extra_data;
+ struct synaptics_rmi4_f11_query_0_5 query_0_5;
+ struct synaptics_rmi4_f11_query_7_8 query_7_8;
+ struct synaptics_rmi4_f11_query_9 query_9;
+ struct synaptics_rmi4_f11_query_12 query_12;
+ struct synaptics_rmi4_f11_query_27 query_27;
+ struct synaptics_rmi4_f11_ctrl_6_9 control_6_9;
+
+ fhandler->fn_number = fd->fn_number;
+ fhandler->num_of_data_sources = fd->intr_src_count;
+ fhandler->extra = kmalloc(sizeof(*extra_data), GFP_KERNEL);
+ if (!fhandler->extra) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for fhandle->extra\n",
+ __func__);
+ return -ENOMEM;
+ }
+ extra_data = (struct synaptics_rmi4_f11_extra_data *)fhandler->extra;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base,
+ query_0_5.data,
+ sizeof(query_0_5.data));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum number of fingers supported */
+ if (query_0_5.num_of_fingers <= 4)
+ fhandler->num_of_data_points = query_0_5.num_of_fingers + 1;
+ else if (query_0_5.num_of_fingers == 5)
+ fhandler->num_of_data_points = 10;
+
+ rmi4_data->num_of_fingers = fhandler->num_of_data_points;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.ctrl_base + 6,
+ control_6_9.data,
+ sizeof(control_6_9.data));
+ if (retval < 0)
+ return retval;
+
+ /* Maximum x and y */
+ rmi4_data->sensor_max_x = SENSOR_MAX_X;
+ rmi4_data->sensor_max_y = SENSOR_MAX_Y; //control_6_9.sensor_max_y_pos_7_0 |
+ //(control_6_9.sensor_max_y_pos_11_8 << 8);
+
+ /* It's recommended to parse max_x and max_y from contrel register, but this does not match MTK's mtk-tpd.c */
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%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);
+
+ fhandler->data = NULL;
+
+ offset = sizeof(query_0_5.data);
+
+ /* query 6 */
+ if (query_0_5.has_rel)
+ offset += 1;
+
+ /* queries 7 8 */
+ if (query_0_5.has_gestures) {
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base + offset,
+ query_7_8.data,
+ sizeof(query_7_8.data));
+ if (retval < 0)
+ return retval;
+
+ offset += sizeof(query_7_8.data);
+ }
+
+ /* query 9 */
+ if (query_0_5.has_query_9) {
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base + offset,
+ query_9.data,
+ sizeof(query_9.data));
+ if (retval < 0)
+ return retval;
+
+ offset += sizeof(query_9.data);
+ }
+
+ /* query 10 */
+ if (query_0_5.has_gestures && query_7_8.has_touch_shapes)
+ offset += 1;
+
+ /* query 11 */
+ if (query_0_5.has_query_11)
+ offset += 1;
+
+ /* query 12 */
+ if (query_0_5.has_query_12) {
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base + offset,
+ query_12.data,
+ sizeof(query_12.data));
+ if (retval < 0)
+ return retval;
+
+ offset += sizeof(query_12.data);
+ }
+
+ /* query 13 */
+ if (query_0_5.has_jitter_filter)
+ offset += 1;
+
+ /* query 14 */
+ if (query_0_5.has_query_12 && query_12.has_general_information_2)
+ offset += 1;
+
+ /* queries 15 16 17 18 19 20 21 22 23 24 25 26*/
+ if (query_0_5.has_query_12 && query_12.has_physical_properties)
+ offset += 12;
+
+ /* query 27 */
+ if (query_0_5.has_query_27) {
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base + offset,
+ query_27.data,
+ sizeof(query_27.data));
+ if (retval < 0)
+ return retval;
+
+ rmi4_data->f11_wakeup_gesture = query_27.has_wakeup_gesture;
+ }
+
+ if (!rmi4_data->f11_wakeup_gesture)
+ return retval;
+
+ /* data 0 */
+ fingers_supported = fhandler->num_of_data_points;
+ offset = (fingers_supported + 3) / 4;
+
+ /* data 1 2 3 4 5 */
+ offset += 5 * fingers_supported;
+
+ /* data 6 7 */
+ if (query_0_5.has_rel)
+ offset += 2 * fingers_supported;
+
+ /* data 8 */
+ if (query_0_5.has_gestures && query_7_8.data[0])
+ offset += 1;
+
+ /* data 9 */
+ if (query_0_5.has_gestures && (query_7_8.data[0] || query_7_8.data[1]))
+ offset += 1;
+
+ /* data 10 */
+ if (query_0_5.has_gestures &&
+ (query_7_8.has_pinch || query_7_8.has_flick))
+ offset += 1;
+
+ /* data 11 12 */
+ if (query_0_5.has_gestures &&
+ (query_7_8.has_flick || query_7_8.has_rotate))
+ offset += 2;
+
+ /* data 13 */
+ if (query_0_5.has_gestures && query_7_8.has_touch_shapes)
+ offset += (fingers_supported + 3) / 4;
+
+ /* data 14 15 */
+ if (query_0_5.has_gestures &&
+ (query_7_8.has_scroll_zones ||
+ query_7_8.has_multi_finger_scroll ||
+ query_7_8.has_chiral_scroll))
+ offset += 2;
+
+ /* data 16 17 */
+ if (query_0_5.has_gestures &&
+ (query_7_8.has_scroll_zones &&
+ query_7_8.individual_scroll_zones))
+ offset += 2;
+
+ /* data 18 19 20 21 22 23 24 25 26 27 */
+ if (query_0_5.has_query_9 && query_9.has_contact_geometry)
+ offset += 10 * fingers_supported;
+
+ /* data 28 */
+ if (query_0_5.has_bending_correction ||
+ query_0_5.has_large_object_suppression)
+ offset += 1;
+
+ /* data 29 30 31 */
+ if (query_0_5.has_query_9 && query_9.has_pen_hover_discrimination)
+ offset += 3;
+
+ /* data 32 */
+ if (query_0_5.has_query_12 &&
+ query_12.has_small_object_detection_tuning)
+ offset += 1;
+
+ /* data 33 34 */
+ if (query_0_5.has_query_27 && query_27.f11_query27_b0)
+ offset += 2;
+
+ /* data 35 */
+ if (query_0_5.has_query_12 && query_12.has_8bit_w)
+ offset += fingers_supported;
+
+ /* data 36 */
+ if (query_0_5.has_bending_correction)
+ offset += 1;
+
+ /* data 37 */
+ if (query_0_5.has_query_27 && query_27.has_data_37)
+ offset += 1;
+
+ /* data 38 */
+ if (query_0_5.has_query_27 && query_27.has_wakeup_gesture)
+ extra_data->data38_offset = offset;
+
+ 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_i2c_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 function 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_20_offset;
+ unsigned char ctrl_23_offset;
+ unsigned char ctrl_27_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);
+ if (!fhandler->extra) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for fhandler->extra\n",
+ __func__);
+ return -ENOMEM;
+ }
+ extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
+ size_of_2d_data = sizeof(struct synaptics_rmi4_f12_finger_data);
+
+ retval = synaptics_rmi4_i2c_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_20_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;
+
+ ctrl_23_offset = ctrl_20_offset +
+ query_5.ctrl20_is_present +
+ query_5.ctrl21_is_present +
+ query_5.ctrl22_is_present;
+ ctrl_27_offset = ctrl_23_offset +
+ query_5.ctrl23_is_present +
+ query_5.ctrl24_is_present +
+ query_5.ctrl25_is_present +
+ query_5.ctrl26_is_present;
+ ctrl_28_offset = ctrl_27_offset +
+ query_5.ctrl27_is_present;
+
+ retval = synaptics_rmi4_i2c_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_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base + 7,
+ &size_of_query8,
+ sizeof(size_of_query8));
+ if (retval < 0)
+ return retval;
+
+ retval = synaptics_rmi4_i2c_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_i2c_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 = SENSOR_MAX_X;
+ rmi4_data->sensor_max_y = SENSOR_MAX_Y;
+
+ /* It's recommended to parse max_x and max_y from contrel register, but this does not match MTK's mtk-tpd.c */
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%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);
+ rmi4_data->f12_wakeup_gesture = query_5.ctrl27_is_present;
+ if (rmi4_data->f12_wakeup_gesture) {
+ extra_data->ctrl20_offset = ctrl_20_offset;
+ extra_data->data4_offset = query_8.data0_is_present +
+ query_8.data1_is_present +
+ query_8.data2_is_present +
+ query_8.data3_is_present;
+ extra_data->ctrl27_offset = ctrl_27_offset;
+ }
+
+ 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);
+ if (!fhandler->data) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to alloc mem for fhandler->data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ 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->i2c_client->dev,
+ "%s: Failed to alloc mem for function handle\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ fhandler->data = (void *)f1a;
+ fhandler->extra = NULL;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.query_base,
+ f1a->button_query.data,
+ sizeof(f1a->button_query.data));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%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->i2c_client->dev,
+ "%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->i2c_client->dev,
+ "%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->i2c_client->dev,
+ "%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;
+
+ 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_i2c_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->i2c_client->dev,
+ "%s: Failed to read tx rx mapping\n",
+ __func__);
+ return retval;
+ }
+
+ rmi4_data->button_txrx_mapping = f1a->button_control.txrx_map;
+ }
+
+ if (cap_button_map.map) {
+ if (cap_button_map.nbuttons != f1a->max_count) {
+ f1a->valid_button_count = min(f1a->max_count,
+ 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] = 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_i2c_write(rmi4_data,
+ rmi4_data->f01_cmd_base_addr,
+ &command,
+ sizeof(command));
+ if (retval < 0)
+ return retval;
+
+ msleep(DELAY_UI_READY);
+
+ retval = synaptics_rmi4_i2c_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_i2c_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_i2c_read(rmi4_data,
+ rmi4_data->f01_data_base_addr + 1,
+ &intr_status,
+ sizeof(intr_status));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%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_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to set configured\n",
+ __func__);
+ return;
+ }
+
+ rmi4_data->no_sleep_setting = device_ctrl & NO_SLEEP_ON;
+ device_ctrl |= CONFIGURED;
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%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 function 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 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:
+ 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_i2c_read(rmi4_data,
+ pdt_entry_addr,
+ (unsigned char *)&rmi_fd,
+ sizeof(rmi_fd));
+ if (retval < 0)
+ return retval;
+
+ fhandler = NULL;
+
+ if (rmi_fd.fn_number == 0) {
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Reached end of PDT\n",
+ __func__);
+ break;
+ }
+
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%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;
+
+ retval = synaptics_rmi4_alloc_fh(&fhandler,
+ &rmi_fd, page_number);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%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) {
+ kfree(fhandler);
+ fhandler = NULL;
+ return retval;
+ }
+
+ retval = synaptics_rmi4_check_status(rmi4_data,
+ &was_in_bl_mode);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to check status\n",
+ __func__);
+ kfree(fhandler);
+ fhandler = NULL;
+ return retval;
+ }
+
+ if (was_in_bl_mode) {
+ kfree(fhandler);
+ fhandler = NULL;
+ goto rescan_pdt;
+ }
+
+ if (rmi4_data->flash_prog_mode) {
+ kfree(fhandler);
+ fhandler = NULL;
+ 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->i2c_client->dev,
+ "%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) {
+ kfree(fhandler);
+ fhandler = NULL;
+ 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->i2c_client->dev,
+ "%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) {
+ kfree(fhandler);
+ fhandler = NULL;
+ 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->i2c_client->dev,
+ "%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) {
+ kfree(fhandler);
+ fhandler = NULL;
+ return retval;
+ }
+
+ 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);
+ }
+ }
+ }
+
+flash_prog_mode:
+ rmi4_data->num_of_intr_regs = (intr_count + 7) / 8;
+ dev_dbg(&rmi4_data->i2c_client->dev,
+ "%s: Number of interrupt registers = %d\n",
+ __func__, rmi4_data->num_of_intr_regs);
+
+ retval = synaptics_rmi4_i2c_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->i2c_client->dev,
+ "%s: Non-Synaptics device found, manufacturer ID = %d\n",
+ __func__, rmi->manufacturer_id);
+ }
+
+ retval = synaptics_rmi4_i2c_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;
+ }
+ }
+ }
+
+ if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture)
+ rmi4_data->enable_wakeup_gesture = WAKEUP_GESTURE;
+ else
+ rmi4_data->enable_wakeup_gesture = false;
+
+ /* 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->i2c_client->dev,
+ "%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_i2c_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, 0);
+#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]);
+ }
+ }
+
+ if (rmi4_data->f11_wakeup_gesture || rmi4_data->f12_wakeup_gesture) {
+ set_bit(KEY_POWER, rmi4_data->input_dev->keybit);
+ input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_POWER);
+ }
+ return;
+}
+
+static int synaptics_rmi4_set_input_dev(struct synaptics_rmi4_data *rmi4_data)
+{
+ int retval;
+
+ rmi4_data->input_dev = input_allocate_device();
+ if (rmi4_data->input_dev == NULL) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%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->i2c_client->dev,
+ "%s: Failed to query device\n",
+ __func__);
+ goto err_query_device;
+ }
+
+ rmi4_data->input_dev->name = 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->id.bustype = BUS_I2C;
+ rmi4_data->input_dev->dev.parent = &rmi4_data->i2c_client->dev;
+ 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
+
+ synaptics_rmi4_set_params(rmi4_data);
+
+ retval = input_register_device(rmi4_data->input_dev);
+ if (retval) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%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_free_fingers(struct synaptics_rmi4_data *rmi4_data)
+{
+ unsigned char ii;
+ mutex_lock(&rmi4_report_mutex);
+
+#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);
+ mutex_unlock(&rmi4_report_mutex);
+ 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->i2c_client->dev,
+ "%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_i2c_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;
+ unsigned char command = 0x01;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+
+ mutex_lock(&(rmi4_data->rmi4_reset_mutex));
+
+ rmi4_data->touch_stopped = true;
+
+ synaptics_rmi4_irq_enable(rmi4_data, false);
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_cmd_base_addr,
+ &command,
+ sizeof(command));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to issue reset command, error = %d\n",
+ __func__, retval);
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+ return retval;
+ }
+
+ msleep(DELAY_UI_READY);
+
+ 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->i2c_client->dev,
+ "%s: Failed to query device\n",
+ __func__);
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+ return retval;
+ }
+
+ 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;
+
+ synaptics_rmi4_irq_enable(rmi4_data, true);
+
+ mutex_unlock(&(rmi4_data->rmi4_reset_mutex));
+
+ return 0;
+}
+
+
+static void synaptics_rmi4_sleep_enable(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ int retval;
+ unsigned char device_ctrl;
+ unsigned char no_sleep_setting = rmi4_data->no_sleep_setting;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to read device control\n",
+ __func__);
+ return;
+ }
+
+ device_ctrl = device_ctrl & ~MASK_3BIT;
+ if (enable)
+ device_ctrl = device_ctrl | NO_SLEEP_OFF | SENSOR_SLEEP;
+ else
+ device_ctrl = device_ctrl | no_sleep_setting | NORMAL_OPERATION;
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ rmi4_data->f01_ctrl_base_addr,
+ &device_ctrl,
+ sizeof(device_ctrl));
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to write device control\n",
+ __func__);
+ return;
+ }
+
+ rmi4_data->sensor_sleep = enable;
+
+ return;
+}
+
+
+
+
+/**
+ * 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);
+
+
+static ssize_t synaptics_rmi4_f34_configid_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%x\n", rmi4_data->config_id);
+}
+
+
+
+static ssize_t synaptics_rmi4_f01_product_id_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, "%s\n",
+ (rmi4_data->rmi4_mod_info.product_id_string));
+}
+
+
+static DEVICE_ATTR(tp_firmware_version, 0664, synaptics_rmi4_f34_configid_show, synaptics_rmi4_store_error);
+static DEVICE_ATTR(product_id, 0664, synaptics_rmi4_f01_product_id_show, synaptics_rmi4_store_error);
+
+static struct attribute *synaptics_rmi4_attrs[] = {
+ attrify(tp_firmware_version),
+ attrify(product_id),
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = synaptics_rmi4_attrs,
+};
+ /**
+ * 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 function 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 synaptics_rmi4_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ int retval, ret;
+ signed char attr_count;
+ struct synaptics_rmi4_data *rmi4_data;
+ struct device_node *np = client->dev.of_node;
+#if 0
+ int rst_gpio;
+#endif
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev,
+ "%s: SMBus byte data not supported\n",
+ __func__);
+ return -EIO;
+ }
+
+#ifdef USE_I2C_DMA
+ wDMABuf_va = (unsigned char *)dma_zalloc_coherent(&client->dev, WRITE_SIZE_LIMIT,
+ &wDMABuf_pa, GFP_KERNEL);
+ if (!wDMABuf_va) {
+ dev_err(&client->dev, "Allocate DMA I2C Buffer failed, exit\n");
+ return -ENOMEM;
+ }
+#endif
+
+ if (!np)
+ return -ENODEV;
+#if 0
+ rst_gpio = of_get_named_gpio(np, "rest-gpios", 0);
+ if (!gpio_is_valid(rst_gpio))
+ return -ENODEV;
+
+ ret = gpio_request(rst_gpio, "synaptics_rmi4_rest");
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "request rest gpio failed, cannot wake up controller: %d\n",
+ ret);
+ return ret;
+ }
+
+ gpio_direction_output(rst_gpio, 0);
+ gpio_set_value(rst_gpio, 1);
+ msleep(DELAY_BOOT_READY);
+ gpio_set_value(rst_gpio, 0);
+ msleep(DELAY_RESET_LOW);
+ gpio_set_value(rst_gpio, 1);
+ msleep(DELAY_UI_READY);
+#endif
+
+ rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL);
+ if (!rmi4_data) {
+ dev_err(&client->dev,
+ "%s: Failed to alloc mem for rmi4_data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ rmi4_data->i2c_client = client;
+ rmi4_data->current_page = MASK_8BIT;
+ rmi4_data->touch_stopped = false;
+ rmi4_data->sensor_sleep = false;
+ rmi4_data->irq_enabled = false;
+ rmi4_data->fingers_on_2d = false;
+
+ rmi4_data->i2c_read = synaptics_rmi4_i2c_read;
+ rmi4_data->i2c_write = synaptics_rmi4_i2c_write;
+ rmi4_data->irq_enable = synaptics_rmi4_irq_enable;
+ rmi4_data->reset_device = synaptics_rmi4_reset_device;
+ rmi4_data->sleep_enable = synaptics_rmi4_sleep_enable;
+
+ rmi4_data->i2c_addr = client->addr;
+ rmi4_data->diagonal_rotation = of_property_read_bool(np,
+ "synaptics,diagonal-rotation");
+
+ mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex));
+ mutex_init(&(rmi4_data->rmi4_reset_mutex));
+ mutex_init(&(rmi4_data->rmi4_exp_init_mutex));
+
+ i2c_set_clientdata(client, rmi4_data);
+ retval = synaptics_rmi4_set_input_dev(rmi4_data);
+ if (retval < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to set up input device\n",
+ __func__);
+ goto err_set_input_dev;
+ }
+
+ thread = kthread_run(touch_event_handler, rmi4_data, "synaptics_dsx");
+ if (IS_ERR(thread)) {
+ retval = PTR_ERR(thread);
+ pr_err(" %s: failed to create kernel thread: %d\n", __func__, retval);
+ }
+
+ touch_irq = client->irq;
+ ret = devm_request_irq(&client->dev, touch_irq, (irq_handler_t) tpd_eint_handler,
+ IRQF_TRIGGER_LOW, "synaptics_rmi4_touch", rmi4_data);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "%s: Failed to register attention interrupt\n",
+ __func__);
+ goto err_enable_irq;
+ }
+ rmi4_data->irq_enabled = true;
+
+ 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(&client->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ goto err_sysfs;
+ }
+ }
+
+ retval = sysfs_create_group(&client->dev.kobj, &attr_group);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to create sysfs attributes\n",
+ __func__);
+ goto err_sysfs;
+ }
+
+ g_dev = &rmi4_data->input_dev->dev;
+
+ return retval;
+
+err_sysfs:
+ sysfs_remove_group(&client->dev.kobj, &attr_group);
+ 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:
+
+ synaptics_rmi4_empty_fn_list(rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+
+err_set_input_dev:
+ 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 function 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 synaptics_rmi4_remove(struct i2c_client *client)
+{
+ unsigned char attr_count;
+ struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client);
+
+ 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);
+
+ synaptics_rmi4_empty_fn_list(rmi4_data);
+ input_unregister_device(rmi4_data->input_dev);
+ rmi4_data->input_dev = NULL;
+
+#ifdef USE_I2C_DMA
+ if (wDMABuf_va)
+ dma_free_coherent(&client->dev, WRITE_SIZE_LIMIT, wDMABuf_va, wDMABuf_pa);
+#endif
+
+ kfree(rmi4_data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static void synaptics_rmi4_f11_wg(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ int retval;
+ unsigned char reporting_control;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F11)
+ break;
+ }
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.ctrl_base,
+ &reporting_control,
+ sizeof(reporting_control));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to change reporting mode\n",
+ __func__);
+ return;
+ }
+
+ reporting_control = (reporting_control & ~MASK_3BIT);
+ if (enable)
+ reporting_control |= F11_WAKEUP_GESTURE_MODE;
+ else
+ reporting_control |= F11_CONTINUOUS_MODE;
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ fhandler->full_addr.ctrl_base,
+ &reporting_control,
+ sizeof(reporting_control));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to change reporting mode\n",
+ __func__);
+ return;
+ }
+
+ return;
+}
+
+static void synaptics_rmi4_f12_wg(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ int retval;
+ unsigned char offset;
+ unsigned char reporting_control[3];
+ struct synaptics_rmi4_f12_extra_data *extra_data;
+ struct synaptics_rmi4_fn *fhandler;
+ struct synaptics_rmi4_device_info *rmi;
+
+ rmi = &(rmi4_data->rmi4_mod_info);
+
+ list_for_each_entry(fhandler, &rmi->support_fn_list, link) {
+ if (fhandler->fn_number == SYNAPTICS_RMI4_F12)
+ break;
+ }
+
+ extra_data = (struct synaptics_rmi4_f12_extra_data *)fhandler->extra;
+ offset = extra_data->ctrl20_offset;
+
+ retval = synaptics_rmi4_i2c_read(rmi4_data,
+ fhandler->full_addr.ctrl_base + offset,
+ reporting_control,
+ sizeof(reporting_control));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to change reporting mode\n",
+ __func__);
+ return;
+ }
+
+ if (enable)
+ reporting_control[2] = F12_WAKEUP_GESTURE_MODE;
+ else
+ reporting_control[2] = F12_CONTINUOUS_MODE;
+
+ retval = synaptics_rmi4_i2c_write(rmi4_data,
+ fhandler->full_addr.ctrl_base + offset,
+ reporting_control,
+ sizeof(reporting_control));
+ if (retval < 0) {
+ dev_err(&(rmi4_data->input_dev->dev),
+ "%s: Failed to change reporting mode\n",
+ __func__);
+ return;
+ }
+
+ return;
+}
+
+static void synaptics_rmi4_wakeup_gesture(struct synaptics_rmi4_data *rmi4_data,
+ bool enable)
+{
+ if (rmi4_data->f11_wakeup_gesture)
+ synaptics_rmi4_f11_wg(rmi4_data, enable);
+ else if (rmi4_data->f12_wakeup_gesture)
+ synaptics_rmi4_f12_wg(rmi4_data, enable);
+
+ return;
+}
+
+ /**
+ * 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 __maybe_unused synaptics_rmi4_suspend(struct device *dev)
+{
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(g_dev);
+
+ if (rmi4_data->staying_awake)
+ return 0;
+
+ if (rmi4_data->enable_wakeup_gesture) {
+ synaptics_rmi4_wakeup_gesture(rmi4_data, true);
+ goto exit;
+ }
+
+ if (!rmi4_data->sensor_sleep) {
+ rmi4_data->touch_stopped = true;
+ synaptics_rmi4_sleep_enable(rmi4_data, true);
+ synaptics_rmi4_free_fingers(rmi4_data);
+ rmi4_data->irq_enabled = false;
+ free_irq(touch_irq, rmi4_data);
+ }
+
+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->suspend != NULL)
+ exp_fhandler->exp_fn->suspend(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+
+ rmi4_data->sensor_sleep = true;
+
+ 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 __maybe_unused synaptics_rmi4_resume(struct device *dev)
+{
+ int retval;
+ struct synaptics_rmi4_exp_fhandler *exp_fhandler;
+ struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(g_dev);
+
+ if (rmi4_data->staying_awake)
+ return 0;
+
+ if (rmi4_data->enable_wakeup_gesture) {
+ synaptics_rmi4_wakeup_gesture(rmi4_data, false);
+ goto exit;
+ }
+
+ retval = devm_request_irq(dev, touch_irq, (irq_handler_t) tpd_eint_handler,
+ IRQF_TRIGGER_LOW, "synaptics_rmi4_touch", rmi4_data);
+ rmi4_data->irq_enabled = true;
+
+ synaptics_rmi4_sleep_enable(rmi4_data, false);
+ retval = synaptics_rmi4_reinit_device(rmi4_data);
+ if (retval < 0) {
+ dev_err(&rmi4_data->i2c_client->dev,
+ "%s: Failed to reinit device\n",
+ __func__);
+ return 0;
+ }
+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->resume != NULL)
+ exp_fhandler->exp_fn->resume(rmi4_data);
+ }
+ mutex_unlock(&exp_data.mutex);
+
+
+ rmi4_data->sensor_sleep = false;
+ rmi4_data->touch_stopped = false;
+
+ return 0;
+}
+
+#endif
+
+static const struct i2c_device_id synaptics_rmi4_id_table[] = {
+ {"synaptics_dsx", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table);
+
+static SIMPLE_DEV_PM_OPS(synaptics_rmi4_pm_ops, synaptics_rmi4_suspend, synaptics_rmi4_resume);
+
+
+static const struct of_device_id synaptics_rmi4_dt_ids[] = {
+ { .compatible = "synaptics_dsx" },
+ { /* sentinel */},
+};
+MODULE_DEVICE_TABLE(of, synaptics_rmi4_dt_ids);
+
+static struct i2c_driver synaptics_rmi4_driver = {
+ .driver = {
+ .name = "synaptics_dsx",
+ .pm = &synaptics_rmi4_pm_ops,
+ .of_match_table = synaptics_rmi4_dt_ids,
+ },
+ .id_table = synaptics_rmi4_id_table,
+ .probe = synaptics_rmi4_probe,
+ .remove = synaptics_rmi4_remove,
+};
+
+module_i2c_driver(synaptics_rmi4_driver);
+
+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_i2c.h b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.h
new file mode 100644
index 000000000000..12d7bf4ca225
--- /dev/null
+++ b/drivers/input/touchscreen/synaptics_dsx/synaptics_dsx_i2c.h
@@ -0,0 +1,421 @@
+/*
+ * Synaptics DSX touchscreen driver
+ *
+ * Copyright (C) 2012-2015 Synaptics Incorporated. All rights reserved.
+ *
+ * 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.
+ *
+ * INFORMATION CONTAINED IN THIS DOCUMENT IS PROVIDED "AS-IS," AND SYNAPTICS
+ * EXPRESSLY DISCLAIMS ALL EXPRESS AND IMPLIED WARRANTIES, INCLUDING ANY
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE,
+ * AND ANY WARRANTIES OF NON-INFRINGEMENT OF ANY INTELLECTUAL PROPERTY RIGHTS.
+ * IN NO EVENT SHALL SYNAPTICS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, PUNITIVE, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OF THE INFORMATION CONTAINED IN THIS DOCUMENT, HOWEVER CAUSED
+ * AND BASED ON ANY THEORY OF LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, AND EVEN IF SYNAPTICS WAS ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE. IF A TRIBUNAL OF COMPETENT JURISDICTION DOES
+ * NOT PERMIT THE DISCLAIMER OF DIRECT DAMAGES OR ANY OTHER DAMAGES, SYNAPTICS'
+ * TOTAL CUMULATIVE LIABILITY TO ANY PARTY SHALL NOT EXCEED ONE HUNDRED U.S.
+ * DOLLARS.
+ */
+#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 0x2000
+#define SYNAPTICS_MTK_DRIVER_VERSION 0x6043
+
+#include <linux/version.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#ifdef CONFIG_OF
+#define CONFIG_OF_TOUCH
+#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 WRITE_SIZE_LIMIT 1024
+
+extern unsigned char g_log_enable;
+#define TP_DEBUG(fmt, arg...) do {\
+ if (g_log_enable)\
+ printk("%s %d " fmt, __func__, __LINE__, ##arg);\
+ } while (0)
+#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_F35 (0x35)
+#define SYNAPTICS_RMI4_F38 (0x38)
+#define SYNAPTICS_RMI4_F51 (0x51)
+#define SYNAPTICS_RMI4_F54 (0x54)
+#define SYNAPTICS_RMI4_F55 (0x55)
+#define SYNAPTICS_RMI4_FDB (0xdb)
+
+#define SYNAPTICS_RMI4_DATE_CODE_SIZE 3
+#define PRODUCT_INFO_SIZE 2
+#define PRODUCT_ID_SIZE 10
+#define 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 F12_GESTURE_DETECTION_LEN 5
+
+
+#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_GESTURE,
+ RMI_LAST,
+};
+
+/*
+ * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT entry
+ * @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_version: version of function
+ * @fn_number: function number
+ */
+struct synaptics_rmi4_fn_desc {
+ union {
+ struct {
+ 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:3;
+ unsigned char reserved_1:2;
+ unsigned char fn_version:2;
+ unsigned char reserved_2:1;
+ unsigned char fn_number;
+ } __packed;
+ unsigned char data[6];
+ };
+};
+
+/*
+ * 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 command registers
+ * @ctrl_base: 16-bit base address for control registers
+ * @data_base: 16-bit base address for data 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_f11_extra_data - extra data of F$11
+ * @data38_offset: offset to F11_2D_DATA38 register
+ */
+struct synaptics_rmi4_f11_extra_data {
+ unsigned char data38_offset;
+};
+
+/*
+ * struct synaptics_rmi4_f12_extra_data - extra data of F$12
+ * @data1_offset: offset to F12_2D_DATA01 register
+ * @data4_offset: offset to F12_2D_DATA04 register
+ * @data15_offset: offset to F12_2D_DATA15 register
+ * @data15_size: size of F12_2D_DATA15 register
+ * @data15_data: buffer for reading F12_2D_DATA15 register
+ * @ctrl20_offset: offset to F12_2D_CTRL20 register
+ */
+struct synaptics_rmi4_f12_extra_data {
+ unsigned char data1_offset;
+ unsigned char data4_offset;
+ unsigned char data15_offset;
+ unsigned char data15_size;
+ unsigned char data15_data[(F12_FINGERS_TO_SUPPORT + 7) / 8];
+ unsigned char ctrl20_offset;
+ unsigned char ctrl27_offset;
+};
+
+/*
+ * struct synaptics_rmi4_fn - RMI function handler
+ * @fn_number: function number
+ * @num_of_data_sources: number of data sources
+ * @num_of_data_points: maximum number of fingers supported
+ * @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
+ * @extra: pointer to extra data
+ */
+struct synaptics_rmi4_fn {
+ unsigned char fn_number;
+ unsigned char num_of_data_sources;
+ unsigned char num_of_data_points;
+ 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
+ * @product_info: product information
+ * @product_id_string: product ID
+ * @build_id: firmware build 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[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[PRODUCT_ID_SIZE + 1];
+ unsigned char build_id[BUILD_ID_SIZE];
+ struct list_head support_fn_list;
+};
+
+/*
+ * struct synaptics_rmi4_data - rmi4 device instance data
+ * @i2c_client: pointer to associated i2c client
+ * @input_dev: pointer to associated input device
+ * @board: constant pointer to platform 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 access
+ * @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
+ * @i2c_read: pointer to i2c read function
+ * @i2c_write: pointer to i2c write function
+ * @irq_enable: pointer to irq enable function
+ */
+struct synaptics_rmi4_data {
+ struct i2c_client *i2c_client;
+ struct input_dev *input_dev;
+ const struct synaptics_dsx_platform_data *board;
+ struct synaptics_rmi4_device_info rmi4_mod_info;
+ struct regulator *regulator;
+ struct mutex rmi4_reset_mutex;
+ struct mutex rmi4_io_ctrl_mutex;
+ struct mutex rmi4_exp_init_mutex;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ unsigned char i2c_addr;
+ 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;
+ unsigned int config_id;
+ int irq;
+ int sensor_max_x;
+ int sensor_max_y;
+ bool diagonal_rotation;
+ bool flash_prog_mode;
+ bool irq_enabled;
+ bool touch_stopped;
+ bool fingers_on_2d;
+ bool sensor_sleep;
+ bool stay_awake;
+ bool f11_wakeup_gesture;
+ bool f12_wakeup_gesture;
+ bool enable_wakeup_gesture;
+ bool staying_awake;
+ bool ext_afe_button;
+ unsigned char gesture_detection[F12_GESTURE_DETECTION_LEN];
+ int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr,
+ unsigned char *data, unsigned short length);
+ int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
+ int (*reset_device)(struct synaptics_rmi4_data *rmi4_data);
+ void (*sleep_enable)(struct synaptics_rmi4_data *rmi4_data,
+ bool enable);
+};
+
+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);
+};
+
+struct synaptics_rmi4_access_ptr {
+ 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);
+ int (*enable)(struct synaptics_rmi4_data *rmi4_data, bool enable);
+};
+
+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->i2c_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->i2c_write(rmi4_data, addr, data, len);
+}
+
+
+
+void synaptics_rmi4_new_function(struct synaptics_rmi4_exp_fn *exp_fn_module,
+ bool insert);
+
+int synaptics_fw_updater(const unsigned char *fw_data);
+
+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 int secure_memcpy(unsigned char *dest, unsigned int dest_size,
+ const unsigned char *src, unsigned int src_size,
+ unsigned int count)
+{
+ if (dest == NULL || src == NULL)
+ return -EINVAL;
+
+ if (count > dest_size || count > src_size)
+ return -EINVAL;
+
+ memcpy((void *)dest, (const void *)src, count);
+
+ return 0;
+}
+
+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/vtl/Makefile b/drivers/input/touchscreen/vtl/Makefile
new file mode 100644
index 000000000000..2ee7f7aefe43
--- /dev/null
+++ b/drivers/input/touchscreen/vtl/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the VTL touchscreen driver.
+#
+
+obj-y += vtl_ts.o
diff --git a/drivers/input/touchscreen/vtl/vtl_ts.c b/drivers/input/touchscreen/vtl/vtl_ts.c
new file mode 100644
index 000000000000..f7bef9e81158
--- /dev/null
+++ b/drivers/input/touchscreen/vtl/vtl_ts.c
@@ -0,0 +1,496 @@
+/*
+ * VTL CTP driver
+ *
+ * Copyright (C) 2013 VTL Corporation
+ * Copyright (C) 2016 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_qos.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#define FORCE_SINGLE_EVENT 1
+
+#include "vtl_ts.h"
+
+#define MIN_X 0x00
+#define MIN_Y 0x00
+#define MAX_X 1023
+#define MAX_Y 767
+#define MAX_AREA 0xff
+#define MAX_FINGERS 2
+
+
+/* Global or static variables */
+struct ts_driver g_driver;
+
+static struct ts_info g_ts = {
+ .driver = &g_driver,
+};
+static struct ts_info *pg_ts = &g_ts;
+
+static struct i2c_device_id vtl_ts_id[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, vtl_ts_id);
+
+
+static int vtl_ts_config(struct ts_info *ts)
+{
+ struct device *dev;
+
+ DEBUG();
+
+ dev = &ts->driver->client->dev;
+
+ /* ts config */
+ ts->config_info.touch_point_number = TOUCH_POINT_NUM;
+
+ pr_info("Configuring vtl\n");
+ ts->config_info.screen_max_x = SCREEN_MAX_X;
+ ts->config_info.screen_max_y = SCREEN_MAX_y;
+ return 0;
+}
+
+void vtl_ts_free_gpio(void)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ gpio_free(ts->config_info.irq_gpio_number);
+}
+
+void vtl_ts_hw_reset(void)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ gpio_set_value(ts->config_info.rst_gpio_number, 0);
+ mdelay(50);
+ gpio_set_value(ts->config_info.rst_gpio_number, 1);
+}
+
+static irqreturn_t vtl_ts_irq(int irq, void *dev)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ queue_work(ts->driver->workqueue, &ts->driver->event_work);
+
+ return IRQ_HANDLED;
+}
+
+static union ts_xy_data *vtl_read_xy_data(struct ts_info *ts)
+{
+ struct i2c_msg msgs;
+ int err;
+
+ DEBUG();
+
+ msgs.addr = ts->driver->client->addr;
+ msgs.flags = 0x01;
+ msgs.len = sizeof(ts->xy_data.buf);
+ msgs.buf = ts->xy_data.buf;
+
+ err = i2c_transfer(ts->driver->client->adapter, &msgs, 1);
+ if (err != 1) {
+ pr_err("___%s:i2c read err___\n", __func__);
+ return NULL;
+ }
+ return &ts->xy_data;
+}
+
+static void vtl_report_xy_coord(struct input_dev *input_dev,
+ union ts_xy_data *xy_data,
+ unsigned char touch_point_number)
+{
+ struct ts_info *ts;
+ int id;
+ int sync;
+ int x, y;
+ unsigned int press;
+ static unsigned int release;
+
+ ts = pg_ts;
+ DEBUG();
+
+ /* report points */
+ sync = 0; press = 0;
+ for (id = 0; id < touch_point_number; id++) {
+ if ((xy_data->point[id].xhi != 0xFF) &&
+ (xy_data->point[id].yhi != 0xFF) &&
+ ((xy_data->point[id].status == 1) ||
+ (xy_data->point[id].status == 2))) {
+ x = (xy_data->point[id].xhi<<4) |
+ (xy_data->point[id].xlo&0xF);
+ y = (xy_data->point[id].yhi<<4) |
+ (xy_data->point[id].ylo&0xF);
+
+ if (ts->config_info.exchange_x_y_flag)
+ swap(x, y);
+
+ if (ts->config_info.revert_x_flag)
+ x = ts->config_info.screen_max_x - x;
+
+ if (ts->config_info.revert_y_flag)
+ y = ts->config_info.screen_max_y - y;
+#ifndef FORCE_SINGLE_EVENT
+ input_mt_slot(input_dev, xy_data->point[id].id - 1);
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER, true);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, 30);
+ input_report_abs(input_dev, ABS_MT_WIDTH_MAJOR, 128);
+#else
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_report_abs(input_dev, ABS_PRESSURE, 1);
+#endif
+ sync = 1;
+ press |= 0x01 << (xy_data->point[id].id - 1);
+ }
+ }
+
+ release &= (release ^ press); /*release point flag */
+ for (id = 0; id < touch_point_number; id++) {
+ if (release & (0x01 << id)) {
+#ifndef FORCE_SINGLE_EVENT
+ input_mt_slot(input_dev, id);
+ input_mt_report_slot_state(input_dev,
+ MT_TOOL_FINGER, false);
+#else
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+#endif
+ sync = 1;
+ }
+
+ }
+ release = press;
+
+ if (sync)
+ input_sync(input_dev);
+}
+
+static void vtl_ts_workfunc(struct work_struct *work)
+{
+
+ union ts_xy_data *xy_data;
+ struct input_dev *input_dev;
+ unsigned char touch_point_number;
+
+ DEBUG();
+
+ input_dev = pg_ts->driver->input_dev;
+ touch_point_number = pg_ts->config_info.touch_point_number;
+
+ xy_data = vtl_read_xy_data(pg_ts);
+ if (xy_data != NULL)
+ vtl_report_xy_coord(input_dev, xy_data, touch_point_number);
+ else
+ pr_err("____xy_data error___\n");
+}
+
+#ifdef CONFIG_PM_SLEEP
+int vtl_ts_suspend(struct device *dev)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ disable_irq(ts->config_info.irq_number);
+ cancel_work_sync(&ts->driver->event_work);
+
+ return 0;
+}
+
+int vtl_ts_resume(struct device *dev)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ /* Hardware reset */
+ vtl_ts_hw_reset();
+ enable_irq(ts->config_info.irq_number);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void vtl_ts_early_suspend(struct early_suspend *handler)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ vtl_ts_suspend(ts->driver->client, PMSG_SUSPEND);
+}
+
+static void vtl_ts_early_resume(struct early_suspend *handler)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ vtl_ts_resume(ts->driver->client);
+}
+#endif
+
+int vtl_ts_remove(struct i2c_client *client)
+{
+ struct ts_info *ts;
+
+ ts = pg_ts;
+ DEBUG();
+
+ /* Driver clean up */
+
+ free_irq(ts->config_info.irq_number, ts);
+ vtl_ts_free_gpio();
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ts->driver->early_suspend);
+#endif
+
+ cancel_work_sync(&ts->driver->event_work);
+ destroy_workqueue(ts->driver->workqueue);
+
+ input_unregister_device(ts->driver->input_dev);
+ input_free_device(ts->driver->input_dev);
+
+ if (ts->driver->proc_entry != NULL)
+ remove_proc_entry(DRIVER_NAME, NULL);
+
+ return 0;
+}
+
+static int init_input_dev(struct ts_info *ts)
+{
+ struct input_dev *input_dev;
+ struct device *dev;
+ int err;
+
+ DEBUG();
+
+ dev = &ts->driver->client->dev;
+
+ /* allocate input device */
+ ts->driver->input_dev = devm_input_allocate_device(dev);
+ if (ts->driver->input_dev == NULL) {
+ dev_err(dev, "Unable to allocate input device for device %s\n",
+ DRIVER_NAME);
+ return -1;
+ }
+
+ input_dev = ts->driver->input_dev;
+
+ input_dev->name = "VTL for wld";
+ input_dev->phys = "I2C";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->id.vendor = 0xaaaa;
+ input_dev->id.product = 0x5555;
+ input_dev->id.version = 0x0001;
+ input_dev->dev.parent = dev;
+
+ /* config input device */
+ __set_bit(EV_SYN, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(EV_ABS, input_dev->evbit);
+
+#ifdef FORCE_SINGLE_EVENT
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_X, MIN_X, MAX_X, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, MIN_Y, MAX_Y, 0, 0);
+#else
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+
+ input_mt_init_slots(input_dev, TOUCH_POINT_NUM, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0,
+ ts->config_info.screen_max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0,
+ ts->config_info.screen_max_y, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
+#endif
+ /* register input device */
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(dev, "Unable to register input device for device %s\n",
+ DRIVER_NAME);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ct36x_test_tp(struct i2c_client *client)
+{
+ struct i2c_msg msgs;
+ char buf;
+
+ msgs.addr = 0x7F;
+ msgs.flags = 0x01;
+ msgs.len = 1;
+ msgs.buf = &buf;
+
+ if (i2c_transfer(client->adapter, &msgs, 1) != 1)
+ return -1;
+
+ return 0;
+}
+
+int vtl_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int err = -1;
+ struct ts_info *ts;
+ struct device *dev;
+
+ ts = pg_ts;
+ ts->driver->client = client;
+ dev = &ts->driver->client->dev;
+
+ /*Probing TouchScreen*/
+ pr_info("Probing vtl touchscreen, touchscreen node found\n");
+ if (ct36x_test_tp(client) < 0) {
+ pr_err("vtl tp not found\n");
+ goto ERR_TS_CONFIG;
+ }
+
+ /* Request platform resources (gpio/interrupt pins) */
+ err = vtl_ts_config(ts);
+ if (err) {
+ dev_err(dev, "VTL touch screen config Failed.\n");
+ goto ERR_TS_CONFIG;
+ }
+
+ /*Requestion GPIO*/
+ ts->config_info.rst_gpio_number = of_get_gpio(client->dev.of_node, 0);
+ if (gpio_is_valid(ts->config_info.rst_gpio_number)) {
+ err = devm_gpio_request(dev,
+ ts->config_info.rst_gpio_number, NULL);
+ if (err) {
+ dev_err(dev, "Unable to request GPIO %d\n",
+ ts->config_info.rst_gpio_number);
+ return err;
+ }
+ }
+
+ /* Check I2C Functionality */
+ err = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
+ if (!err) {
+ dev_err(dev, "Check I2C Functionality Failed.\n");
+ return -ENODEV;
+ }
+
+ err = devm_request_threaded_irq(dev, client->irq,
+ NULL, vtl_ts_irq,
+ IRQF_ONESHOT,
+ client->name, ts);
+ if (err) {
+ dev_err(&client->dev, "VTL Failed to register interrupt\n");
+
+ goto ERR_IRQ_REQ;
+ }
+
+ vtl_ts_hw_reset();
+
+ /*init input dev*/
+ err = init_input_dev(ts);
+ if (err) {
+
+ dev_err(dev, "init input dev failed.\n");
+ goto ERR_INIT_INPUT;
+ }
+
+ /* register early suspend */
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->driver->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->driver->early_suspend.suspend = vtl_ts_early_suspend;
+ ts->driver->early_suspend.resume = vtl_ts_early_resume;
+ register_early_suspend(&ts->driver->early_suspend);
+#endif
+ /* Create work queue */
+ INIT_WORK(&ts->driver->event_work, vtl_ts_workfunc);
+ ts->driver->workqueue = create_singlethread_workqueue(DRIVER_NAME);
+
+ return 0;
+
+ERR_IRQ_REQ:
+ cancel_work_sync(&ts->driver->event_work);
+ destroy_workqueue(ts->driver->workqueue);
+
+ERR_INIT_INPUT:
+ input_free_device(ts->driver->input_dev);
+ gpio_free(ts->config_info.rst_gpio_number);
+ERR_TS_CONFIG:
+
+ return err;
+}
+
+
+static SIMPLE_DEV_PM_OPS(vtl_ts_pm_ops, vtl_ts_suspend, vtl_ts_resume);
+
+static const struct of_device_id vtl_ts_dt_ids[] = {
+ { .compatible = "vtl,ct365", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vtl_ts_dt_ids);
+
+
+static struct i2c_driver vtl_ts_driver = {
+ .probe = vtl_ts_probe,
+ .remove = vtl_ts_remove,
+ .id_table = vtl_ts_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ .pm = &vtl_ts_pm_ops,
+ .of_match_table = of_match_ptr(vtl_ts_dt_ids),
+ },
+};
+
+module_i2c_driver(vtl_ts_driver);
+
+MODULE_AUTHOR("VTL");
+MODULE_DESCRIPTION("VTL TouchScreen driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/vtl/vtl_ts.h b/drivers/input/touchscreen/vtl/vtl_ts.h
new file mode 100644
index 000000000000..9f61565d6639
--- /dev/null
+++ b/drivers/input/touchscreen/vtl/vtl_ts.h
@@ -0,0 +1,181 @@
+/*
+ * VTL CTP driver
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc
+ *
+ * Using code from:
+ * - github.com/qdk0901/q98_source:drivers/input/touchscreen/vtl/vtl_ts.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef _TS_CORE_H_
+#define _TS_CORE_H_
+
+#include <linux/gpio.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+
+#ifdef TS_DEBUG
+#define DEBUG() pr_debug("___%s___\n", __func__)
+#else
+#define DEBUG()
+#endif
+
+
+/* platform define */
+#define COMMON 0x01 /* Samsung,Freescale,Amlogic,actions */
+#define ROCKCHIP 0X02
+#define ALLWINER 0X03
+#define MTK 0X04
+
+/* vtl touch IC define */
+#define CT36X 0x01
+#define CT360 0x02
+
+/* xy data protocol */
+#define OLD_PROTOCOL 0x01
+#define NEW_PROTOCOL 0x02
+
+
+/* vtl ts driver config */
+
+/*platform config*/
+#define PLATFORM COMMON
+
+/*vtl ts driver name*/
+#define DRIVER_NAME "vtl_ts"
+
+/*vtl chip ID*/
+#define CHIP_ID CT36X
+
+#define XY_DATA_PROTOCOL NEW_PROTOCOL
+
+
+/* maybe not use,please refer to the function
+ * vtl_ts_config() in the file "vtl_ts.c"
+ */
+#define SCREEN_MAX_X 1024
+#define SCREEN_MAX_y 600
+
+#define TS_IRQ_GPIO_NUM /* RK30_PIN4_PC2 */
+#define TS_RST_GPIO_NUM /* RK30_PIN4_PD0 */
+#define TS_I2C_SPEED 400000 /* for rockchip */
+
+
+/* priate define and declare */
+#if (CHIP_ID == CT360)
+#define TOUCH_POINT_NUM 1
+#elif (CHIP_ID == CT36X)
+#define TOUCH_POINT_NUM 1
+#endif
+
+
+#if (CHIP_ID == CT360)
+struct xy_data {
+#if (XY_DATA_PROTOCOL == OLD_PROTOCOL)
+ unsigned char status:4; /* Action information, 1:Down;
+ 2: Move; 3: Up */
+ unsigned char id:4; /* ID information, from 1 to
+ CFG_MAX_POINT_NUM */
+#endif
+ unsigned char xhi; /* X coordinate Hi */
+ unsigned char yhi; /* Y coordinate Hi */
+ unsigned char ylo:4; /* Y coordinate Lo */
+ unsigned char xlo:4; /* X coordinate Lo */
+#if (XY_DATA_PROTOCOL == NEW_PROTOCOL)
+ unsigned char status:4; /* Action information, 1: Down;
+ 2: Move; 3: Up */
+ unsigned char id:4; /* ID information, from 1 to
+ CFG_MAX_POINT_NUM */
+#endif
+};
+#else
+struct xy_data {
+#if (XY_DATA_PROTOCOL == OLD_PROTOCOL)
+ unsigned char status:3; /* Action information, 1: Down;
+ 2: Move; 3: Up */
+ unsigned char id:5; /* ID information, from 1 to
+ CFG_MAX_POINT_NUM */
+#endif
+ unsigned char xhi; /* X coordinate Hi */
+ unsigned char yhi; /* Y coordinate Hi */
+ unsigned char ylo:4; /* Y coordinate Lo */
+ unsigned char xlo:4; /* X coordinate Lo */
+#if (XY_DATA_PROTOCOL == NEW_PROTOCOL)
+ unsigned char status:3; /* Action information, 1: Down;
+ 2: Move; 3: Up */
+ unsigned char id:5; /* ID information, from 1 to
+ CFG_MAX_POINT_NUM */
+#endif
+ unsigned char area; /* Touch area */
+ unsigned char pressure; /* Touch Pressure */
+};
+#endif
+
+
+union ts_xy_data {
+ struct xy_data point[TOUCH_POINT_NUM];
+ unsigned char buf[TOUCH_POINT_NUM * sizeof(struct xy_data)];
+};
+
+
+struct ts_driver {
+
+ struct i2c_client *client;
+
+ /* input devices */
+ struct input_dev *input_dev;
+
+ struct proc_dir_entry *proc_entry;
+
+ /* Work thread settings */
+ struct work_struct event_work;
+ struct workqueue_struct *workqueue;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+struct ts_config_info {
+
+ unsigned int screen_max_x;
+ unsigned int screen_max_y;
+ unsigned int irq_gpio_number;
+ unsigned int irq_number;
+ unsigned int rst_gpio_number;
+ unsigned char touch_point_number;
+ unsigned char ctp_used;
+ unsigned char i2c_bus_number;
+ unsigned char revert_x_flag;
+ unsigned char revert_y_flag;
+ unsigned char exchange_x_y_flag;
+ int (*tp_enter_init)(void);
+ void (*tp_exit_init)(int state);
+};
+
+
+struct ts_chip_info {
+ unsigned char chip_id;
+};
+
+struct ts_info {
+
+ struct ts_driver *driver;
+ struct ts_config_info config_info;
+ struct ts_chip_info chip_info;
+ union ts_xy_data xy_data;
+};
+
+#endif