diff options
author | Dong Aisheng <aisheng.dong@nxp.com> | 2021-11-30 15:00:25 +0800 |
---|---|---|
committer | Dong Aisheng <aisheng.dong@nxp.com> | 2021-11-30 15:00:25 +0800 |
commit | 3f5da5c8a5d73adfa1049b15bd42a7a12e41089d (patch) | |
tree | a50c470d6e77401594327c153e69fdf707dad30f | |
parent | 76162b02776ed38b314033ace221b323651b1d83 (diff) | |
parent | bc5e079b61e085c7d7ba543d1216e253b43dfc2a (diff) |
Merge branch 'input/next' into next
* input/next: (29 commits)
LF-4558-2 input: touch: goodix: change the i2c data length to a small one
LF-4246 input: touchscreen: goodix: do not check FW cfg when it not support load fw
LF-4184-2 input: goodix: do not update touch device firmware
LF-2539-2 input: touch: goodix: force set the IRQ_TYPE_EDGE_FALLING for GT1151Q
MLK-24528 input: touch: synaptics_dsx: correct the irq handler define
...
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, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®_addr, 1, ®_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, ®addr, 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 |