Fingerprint. Driver porting.
Root cause:
N/A
How to fix:
N/A
RiskArea: Fingerprint
Change-Id: Id231c4ee49de5cd25fbdc452bfc4e806f4429597
diff --git a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
index 3dad2f2..957f6ac 100644
--- a/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953-pinctrl.dtsi
@@ -399,6 +399,62 @@
};
+//<2020/04/23-louisliu, Fingerprint. Driver porting.
+ fps {
+ fps_int_active: fps_int_active {
+ mux {
+ pins = "gpio48";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio48";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+
+ fps_int_suspend: fps_int_suspend {
+ mux {
+ pins = "gpio48";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio48";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+
+ fps_reset_active: fps_reset_active {
+ mux {
+ pins = "gpio140";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio140";
+ drive-strength = <8>;
+ bias-pull-up;
+ };
+ };
+
+ fps_reset_suspend1: fps_reset_suspend1 {
+ mux {
+ pins = "gpio140";
+ function = "gpio";
+ };
+
+ config {
+ pins = "gpio140";
+ drive-strength = <2>;
+ bias-pull-down;
+ };
+ };
+ };
+//>2020/04/23-louisliu
+
pmx_mdss: pmx_mdss {
/*[Arima_8901][Jialong] lcm driver porting begin*/
diff --git a/arch/arm64/boot/dts/qcom/msm8953.dtsi b/arch/arm64/boot/dts/qcom/msm8953.dtsi
index 1d78b6b..cd084ca 100644
--- a/arch/arm64/boot/dts/qcom/msm8953.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8953.dtsi
@@ -2319,3 +2319,16 @@
&gdsc_usb30 {
status = "okay";
};
+
+// //<2020/04/23-louisliu, Fingerprint. Driver porting.
+&soc {
+ elan {
+ compatible = "elan,elan_fp";
+ interrupt-parent = <&tlmm>;
+ interrupts = <48 0>;
+ elan,irq-gpio = <&tlmm 48 0>;
+ elan,rst-gpio = <&tlmm 140 0>;
+ elan,vdd-gpio = <&tlmm 90 0>;
+ };
+};
+// //>2020/04/23-louisliu
\ No newline at end of file
diff --git a/arch/arm64/configs/msm8953-perf_defconfig b/arch/arm64/configs/msm8953-perf_defconfig
index 78f1f97..221622c 100755
--- a/arch/arm64/configs/msm8953-perf_defconfig
+++ b/arch/arm64/configs/msm8953-perf_defconfig
@@ -684,3 +684,6 @@
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
CONFIG_CRYPTO_CRC32_ARM64=y
CONFIG_QMI_ENCDEC=y
+# //<2020/04/23-louisliu, Fingerprint. Driver porting.
+CONFIG_ELAN_FINGERPRINT=y
+# //>2020/04/23-louisliu
diff --git a/arch/arm64/configs/msm8953_defconfig b/arch/arm64/configs/msm8953_defconfig
index f30592c..ad12e19 100755
--- a/arch/arm64/configs/msm8953_defconfig
+++ b/arch/arm64/configs/msm8953_defconfig
@@ -748,3 +748,6 @@
CONFIG_CRYPTO_AES_ARM64_NEON_BLK=y
CONFIG_CRYPTO_CRC32_ARM64=y
CONFIG_QMI_ENCDEC=y
+# //<2020/04/23-louisliu, Fingerprint. Driver porting.
+CONFIG_ELAN_FINGERPRINT=y
+# //>2020/04/23-louisliu
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index de41b16..0d48666 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -200,6 +200,17 @@
---help---
Say Y here if you want to take action when some keys are pressed;
+# //<2020/04/23-louisliu, Fingerprint. Driver porting.
+config ELAN_FINGERPRINT
+ tristate "ELAN Fingerprint"
+ default n
+ ---help---
+ Fingerprint qualcomm driver enable/disable in the kernel.
+ Say Y here if you want to use qualcomm fingerprint driver,
+ fingerprint driver will support fingerprint function in TEE,
+ it supports ELNA's all fingerprint device.
+# //>2020/04/23-louisliu
+
comment "Input Device Drivers"
source "drivers/input/keyboard/Kconfig"
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 7ff1b70..50a5f94 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -34,3 +34,7 @@
obj-$(CONFIG_INPUT_KEYRESET) += keyreset.o
obj-$(CONFIG_INPUT_KEYCOMBO) += keycombo.o
obj-$(CONFIG_SMI130) += sensors/smi130/
+
+# //<2020/04/23-louisliu, Fingerprint. Driver porting.
+obj-$(CONFIG_ELAN_FINGERPRINT) +=fingerprint/
+# //>2020/04/23-louisliu
\ No newline at end of file
diff --git a/drivers/input/fingerprint/Makefile b/drivers/input/fingerprint/Makefile
new file mode 100644
index 0000000..4b4eb3a
--- /dev/null
+++ b/drivers/input/fingerprint/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_ELAN_FINGERPRINT)=elan_fp_qcom_tee.o
\ No newline at end of file
diff --git a/drivers/input/fingerprint/elan_fp_qcom_tee.c b/drivers/input/fingerprint/elan_fp_qcom_tee.c
new file mode 100644
index 0000000..34d3517
--- /dev/null
+++ b/drivers/input/fingerprint/elan_fp_qcom_tee.c
@@ -0,0 +1,697 @@
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/async.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/kthread.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/io.h>
+#include <linux/ioctl.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/syscalls.h>
+#include <linux/err.h>
+#include <linux/cdev.h>
+#include <linux/types.h>
+#include <linux/pm_wakeup.h> //#include <linux/wakelock.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/time.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/poll.h>
+#include <linux/of_gpio.h>
+#include "elan_fp_qcom_tee.h"
+
+#define VERSION_LOG "2.2.1"
+
+static int elan_debug = 1;
+#define ELAN_DEBUG(format, args ...) \
+do { \
+ if (elan_debug) \
+ printk("[ELAN] " format, ##args); \
+ } while(0)
+
+#define KEY_FP_INT KEY_POWER //KEY_WAKEUP // change by customer & framework support
+#define KEY_FP_INT2 KEY_1 // change by customer & framework support
+#define SET_SPI_OWNER (0)
+#if SET_SPI_OWNER
+#include <soc/qcom/scm.h>
+#endif
+
+#if defined(CONFIG_FB)
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#include <linux/signal.h>
+#endif
+
+static int factory_status = 0;
+static DEFINE_MUTEX(elan_factory_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(elan_poll_wq);
+static int elan_work_flag = 0;
+#if defined(CONFIG_FB)
+static struct notifier_block fb_notif;
+static int pid_fp = -1;
+static int display_status = -1;
+#endif
+//[Z20][fingerprint][Kent][17111801][begin]add power gpio for vdd
+#define VDD_POWER_GPIO 1
+//[Z20][fingerprint][Kent][17111801][end]add power gpio for vdd
+struct elan_data {
+ int int_gpio;
+ int irq;
+ int rst_gpio;
+ int irq_is_disable;
+ struct miscdevice elan_dev; /* char device for ioctl */
+ struct platform_device *pdev;
+ struct input_dev *input_dev;
+ spinlock_t irq_lock;
+ struct wakeup_source wake_lock; //struct wake_lock wake_lock;
+ struct wakeup_source hal_wake_lock; //struct wake_lock hal_wake_lock;
+//[Z20][fingerprint][Kent][17111802][begin]add power gpio for vdd
+#if VDD_POWER_GPIO
+ int vdd_gpio;
+#endif
+//[Z20][fingerprint][Kent][17111802][end]add power gpio for vdd
+};
+
+void elan_irq_enable(void *_fp)
+{
+ struct elan_data *fp = _fp;
+ unsigned long irqflags = 0;
+ ELAN_DEBUG("IRQ Enable = %d.\n", fp->irq);
+
+ spin_lock_irqsave(&fp->irq_lock, irqflags);
+ if (fp->irq_is_disable)
+ {
+ enable_irq(fp->irq);
+ fp->irq_is_disable = 0;
+ }
+ spin_unlock_irqrestore(&fp->irq_lock, irqflags);
+}
+
+void elan_irq_disable(void *_fp)
+{
+ struct elan_data *fp = _fp;
+ unsigned long irqflags;
+ ELAN_DEBUG("IRQ Disable = %d.\n", fp->irq);
+
+ spin_lock_irqsave(&fp->irq_lock, irqflags);
+ if (!fp->irq_is_disable)
+ {
+ fp->irq_is_disable = 1;
+ disable_irq_nosync(fp->irq);
+ }
+ spin_unlock_irqrestore(&fp->irq_lock, irqflags);
+}
+
+static ssize_t show_drv_version_value(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", VERSION_LOG);
+}
+static DEVICE_ATTR(drv_version, S_IRUGO, show_drv_version_value, NULL);
+
+static ssize_t elan_debug_value(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ if(elan_debug){
+ elan_debug=0;
+ } else {
+ elan_debug=1;
+ }
+ return sprintf(buf, "[ELAN] elan debug %d\n", elan_debug);
+}
+static DEVICE_ATTR(elan_debug, S_IRUGO, elan_debug_value, NULL);
+
+static struct attribute *elan_attributes[] = {
+ &dev_attr_drv_version.attr,
+ &dev_attr_elan_debug.attr,
+ NULL
+};
+
+static struct attribute_group elan_attr_group = {
+ .attrs = elan_attributes,
+};
+
+static void elan_reset(struct elan_data *fp)
+{
+ /* Developement platform */
+ gpio_set_value(fp->rst_gpio, 0);
+ mdelay(5);
+ gpio_set_value(fp->rst_gpio, 1);
+ mdelay(50);
+}
+
+#if defined(CONFIG_FB)
+
+static int send_sig_to_pid(int sig, pid_t pid)
+{
+ struct siginfo info;
+
+ info.si_signo = sig;
+ info.si_errno = 0;
+ info.si_code = SI_USER;
+ info.si_pid = get_current()->pid;
+ info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
+
+ return kill_proc_info(sig, &info, pid);
+}
+
+static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank ;
+
+ //[Z20][fingerprint][180123begin]void fb system crash
+ if (event != 0x10)
+ return 0;
+ //[Z20][fingerprint][180123end]void fb system crash
+
+//[Z20][fingerprint][180123begin]check the null pointer
+ if(evdata == NULL)
+ {
+ pr_err("%s data is NULL\n",__func__);
+ return 0;
+ }
+ blank = evdata->data;
+ if(blank == NULL)
+ {
+ pr_err("%s blank is NULL\n",__func__);
+ return 0;
+ }
+//[Z20][fingerprint][180123end]check the null pointer
+ ELAN_DEBUG("%s fb notifier callback event = %lu, evdata->data = %d\n",__func__, event, *blank);
+ if (evdata && evdata->data) {
+ if (event == 0x10) {
+ if (*blank == FB_BLANK_UNBLANK) {
+ display_status = 0;
+ if(pid_fp != -1)
+ send_sig_to_pid(SIGUSR2,pid_fp);
+ ELAN_DEBUG("Display On\n");
+ }
+ else if (*blank == FB_BLANK_POWERDOWN) {
+ display_status = 1;
+ if(pid_fp != -1)
+ send_sig_to_pid(SIGUSR2,pid_fp);
+ ELAN_DEBUG("Display Off\n");
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+static long elan_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct elan_data *fp = filp->private_data;
+ int keycode;
+ int wake_lock_arg;
+
+ ELAN_DEBUG("%s() : cmd = [%04X]\n", __func__, cmd);
+
+ switch(cmd)
+ {
+ case ID_IOCTL_RESET: //6
+ elan_reset(fp);
+ ELAN_DEBUG("[IOCTL] RESET\n");
+ break;
+
+ case ID_IOCTL_POLL_INIT: //20
+ elan_work_flag = 0;
+ ELAN_DEBUG("[IOCTL] POLL INIT\n");
+ break;
+
+ case ID_IOCTL_POLL_EXIT: //23
+ elan_work_flag = 1;
+ wake_up(&elan_poll_wq);
+ ELAN_DEBUG("[IOCTL] POLL EXIT\n");
+ break;
+
+ case ID_IOCTL_INPUT_KEYCODE: //22
+ keycode =(int __user)arg;
+ ELAN_DEBUG("[IOCTL] KEYCODE DOWN & UP, keycode = %d \n", keycode);
+ if (!keycode) {
+ ELAN_DEBUG("Keycode %d not defined, ignored\n", (int __user)arg);
+ break ;
+ }
+ input_report_key(fp->input_dev, keycode, 1); // Added for KEY Event
+ input_sync(fp->input_dev);
+ input_report_key(fp->input_dev, keycode, 0); // Added for KEY Event
+ input_sync(fp->input_dev);
+ break;
+
+ case ID_IOCTL_SET_KEYCODE: //24
+ keycode =(int __user)arg;
+ ELAN_DEBUG("[IOCTL] SET KEYCODE, keycode = %d \n", keycode);
+ if (!keycode) {
+ ELAN_DEBUG("Keycode %d not defined, ignored\n", (int __user)arg);
+ break ;
+ }
+ input_set_capability(fp->input_dev, EV_KEY, keycode);
+ set_bit(keycode, fp->input_dev->keybit);
+ break;
+
+ case ID_IOCTL_INPUT_KEYCODE_DOWN: //28
+ keycode =(int __user)arg;
+ ELAN_DEBUG("[IOCTL] KEYCODE DOWN, keycode = %d \n", keycode);
+ if(!keycode) {
+ ELAN_DEBUG("Keycode %d not defined, ignored\n", (int __user)arg);
+ break ;
+ }
+ input_report_key(fp->input_dev, keycode, 1);
+ input_sync(fp->input_dev);
+ break;
+
+ case ID_IOCTL_INPUT_KEYCODE_UP: //29
+ keycode =(int __user)arg;
+ ELAN_DEBUG("[IOCTL] KEYCODE UP, keycode = %d \n", keycode);
+ if(!keycode) {
+ ELAN_DEBUG("Keycode %d not defined, ignored\n", (int __user)arg);
+ break ;
+ }
+ input_report_key(fp->input_dev, keycode, 0);
+ input_sync(fp->input_dev);
+ break;
+
+ case ID_IOCTL_READ_FACTORY_STATUS: //26
+ mutex_lock(&elan_factory_mutex);
+ ELAN_DEBUG("[IOCTL] READ factory_status = %d\n", factory_status);
+ mutex_unlock(&elan_factory_mutex);
+ return factory_status;
+ break;
+
+ case ID_IOCTL_WRITE_FACTORY_STATUS: //27
+ mutex_lock(&elan_factory_mutex);
+ factory_status = (int __user)arg;
+ ELAN_DEBUG("[IOCTL] WRITE factory_status = %d\n", factory_status);
+ mutex_unlock(&elan_factory_mutex);
+ break;
+
+ case ID_IOCTL_INT_STATUS: //40
+ return gpio_get_value(fp->int_gpio);
+
+ case ID_IOCTL_WAKE_LOCK_UNLOCK: //41
+ wake_lock_arg = (int __user)arg;
+ if(!wake_lock_arg)
+ {
+ __pm_relax(&fp->hal_wake_lock); //wake_unlock(&fp->hal_wake_lock);
+ ELAN_DEBUG("[IOCTL] HAL WAKE UNLOCK = %d\n", wake_lock_arg);
+ }
+ else if(wake_lock_arg)
+ {
+ __pm_stay_awake(&fp->hal_wake_lock); //wake_lock(&fp->hal_wake_lock);
+ ELAN_DEBUG("[IOCTL] HAL WAKE LOCK = %d\n", wake_lock_arg);
+ }
+ else
+ ELAN_DEBUG("[IOCTL] ERROR WAKE LOCK ARGUMENT\n");
+ break;
+
+ case ID_IOCTL_EN_IRQ: //55
+ elan_irq_enable(fp);
+ ELAN_DEBUG("[IOCTL] ENABLE IRQ\n");
+ break;
+
+ case ID_IOCTL_DIS_IRQ: //66
+ elan_irq_disable(fp);
+ ELAN_DEBUG("[IOCTL] DISABLE IRQ\n");
+ break;
+
+ case ID_IOCTL_SET_IRQ_TYPE: //91
+ ELAN_DEBUG("[IOCTL] SET IRQ TYPE\n");
+ irq_set_irq_type(fp->irq, IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND | IRQF_ONESHOT);
+ break;
+
+ case ID_IOCTL_DISPLAY_STATUS: //93
+ ELAN_DEBUG("[IOCTL] DISPLAY_STATUS = %d\n", display_status);
+ return display_status;
+
+ case ID_IOCTL_DISPLAY_NOTIFY: //94
+ break;
+
+ case ID_IOCTL_SET_PID: //94
+ pid_fp = (int __user)arg;
+ ELAN_DEBUG("[IOCTL] ID_IOCTL_SET_PID = %d\n", pid_fp);
+ break;
+
+ default:
+ ELAN_DEBUG("INVALID COMMAND\n");
+ break;
+ }
+ return 0;
+}
+
+static unsigned int elan_poll(struct file *file, poll_table *wait)
+{
+ int mask=0;
+ ELAN_DEBUG("%s()\n",__func__);
+
+ wait_event_interruptible(elan_poll_wq, elan_work_flag > 0);
+ if(elan_work_flag > 0)
+ mask = elan_work_flag;
+
+ elan_work_flag = 0;
+ return mask;
+}
+
+static int elan_open(struct inode *inode, struct file *filp)
+{
+ struct elan_data *fp = container_of(filp->private_data, struct elan_data, elan_dev);
+ filp->private_data = fp;
+ ELAN_DEBUG("%s()\n", __func__);
+ return 0;
+}
+
+static int elan_close(struct inode *inode, struct file *filp)
+{
+ ELAN_DEBUG("%s()\n", __func__);
+ return 0;
+}
+
+static const struct file_operations elan_fops = {
+ .owner = THIS_MODULE,
+ .open = elan_open,
+ .unlocked_ioctl = elan_ioctl,
+ .poll = elan_poll,
+ .release = elan_close,
+};
+
+#if SET_SPI_OWNER
+static int set_pipe_ownership(void)
+{
+ const u32 TZ_BLSP_MODIFY_OWNERSHIP_ID = 3;
+ const u32 TZBSP_TZ_ID = 3;
+ int rc;
+ struct scm_desc desc = {
+ .arginfo = SCM_ARGS(2),
+ .args[0] = 3,
+ .args[1] = TZBSP_TZ_ID,
+ };
+
+ rc = scm_call2(SCM_SIP_FNID(SCM_SVC_TZ, TZ_BLSP_MODIFY_OWNERSHIP_ID), &desc);
+
+ if(rc || desc.ret[0])
+ {
+ ELAN_DEBUG("%s() FAIL\n", __func__);
+ return -EINVAL;
+ }
+ ELAN_DEBUG("%s() Success\n", __func__);
+ return 0;
+}
+#endif
+
+static irqreturn_t elan_irq_handler(int irq, void *dev_id)
+{
+ struct elan_data *fp = (struct elan_data *)dev_id;
+
+ ELAN_DEBUG("%s()\n", __func__);
+ __pm_wakeup_event(&fp->wake_lock, msecs_to_jiffies(1000)); //wake_lock_timeout(&fp->wake_lock, msecs_to_jiffies(1000));
+ if(fp == NULL)
+ return IRQ_NONE;
+ elan_work_flag = 1;
+ wake_up(&elan_poll_wq);
+
+ return IRQ_HANDLED;
+}
+
+static int elan_setup_cdev(struct elan_data *fp)
+{
+
+ fp->elan_dev.minor = MISC_DYNAMIC_MINOR;
+ fp->elan_dev.name = "elan_fp";
+ fp->elan_dev.fops = &elan_fops;
+ fp->elan_dev.mode = S_IFREG|S_IRWXUGO;
+ if (misc_register(&fp->elan_dev) < 0) {
+ ELAN_DEBUG("misc_register failed\n");
+ return -1;
+ }
+ else {
+ ELAN_DEBUG("misc_register finished\n");
+ }
+ return 0;
+}
+
+static int elan_sysfs_create(struct elan_data *sysfs)
+{
+ struct elan_data *fp = platform_get_drvdata(sysfs->pdev);
+ int ret = 0;
+
+ /* Register sysfs */
+ ret = sysfs_create_group(&fp->pdev->dev.kobj, &elan_attr_group);
+ if (ret) {
+ ELAN_DEBUG("create sysfs attributes failed, ret = %d\n", ret);
+ goto fail_un;
+ }
+ return 0;
+fail_un:
+ /* Remove sysfs */
+ sysfs_remove_group(&fp->pdev->dev.kobj, &elan_attr_group);
+
+ return ret;
+}
+
+static int elan_gpio_config(struct elan_data *fp)
+{
+ int ret = 0;
+
+ // Configure INT GPIO (Input)
+ ret = gpio_request(fp->int_gpio, "elan-irq");
+ if (ret < 0)
+ ELAN_DEBUG("interrupt pin request gpio failed, ret = %d\n", ret);
+ else {
+ gpio_direction_input(fp->int_gpio);
+ fp->irq = gpio_to_irq(fp->int_gpio);
+ if(fp->irq < 0) {
+ ELAN_DEBUG("gpio to irq failed, irq = %d\n", fp->irq);
+ ret = -1;
+ }
+ else
+ ELAN_DEBUG("gpio to irq success, irq = %d\n",fp->irq);
+ }
+
+ // Configure RST GPIO (Output)
+ ret = gpio_request(fp->rst_gpio, "elan-rst");
+ if (ret < 0) {
+ gpio_free(fp->int_gpio);
+ free_irq(fp->irq, fp);
+ ELAN_DEBUG("reset pin request gpio failed, ret = %d\n", ret);
+ }
+ else
+ gpio_direction_output(fp->rst_gpio, 1);
+//[Z20][fingerprint][Kent][17111803][begin]add power gpio for vdd
+#if VDD_POWER_GPIO
+ // Configure VDD GPIO (Output)
+ ret = gpio_request(fp->vdd_gpio, "elan-vdd");
+ if (ret < 0) {
+ gpio_free(fp->rst_gpio);
+ gpio_free(fp->int_gpio);
+ free_irq(fp->irq, fp);
+ ELAN_DEBUG("reset pin request gpio failed, ret = %d\n", ret);
+ }
+ else
+ gpio_direction_output(fp->vdd_gpio, 1);
+#endif
+//[Z20][fingerprint][Kent][17111803][end]add power gpio for vdd
+ return ret;
+}
+
+static int elan_dts_init(struct elan_data *fp, struct device_node *np)
+{
+ fp->rst_gpio = of_get_named_gpio(np, "elan,rst-gpio", 0);
+ ELAN_DEBUG("rst_gpio = %d\n", fp->rst_gpio);
+ if (fp->rst_gpio < 0)
+ return fp->rst_gpio;
+
+ fp->int_gpio = of_get_named_gpio(np, "elan,irq-gpio", 0);
+ ELAN_DEBUG("int_gpio = %d\n", fp->int_gpio);
+ if (fp->int_gpio < 0)
+ return fp->int_gpio;
+//[Z20][fingerprint][Kent][17111804][begin]add power gpio for vdd
+#if VDD_POWER_GPIO
+ fp->vdd_gpio = of_get_named_gpio(np, "elan,vdd-gpio", 0);
+ ELAN_DEBUG("vdd_gpio = %d\n", fp->vdd_gpio);
+ if (fp->vdd_gpio < 0)
+ return fp->vdd_gpio;
+#endif
+//[Z20][fingerprint][Kent][17111804][end]add power gpio for vdd
+ return 0;
+}
+
+static int elan_probe(struct platform_device *pdev)
+{
+ struct elan_data *fp = NULL;
+ struct input_dev *input_dev = NULL;
+ int ret = 0;
+
+ ELAN_DEBUG("%s(), version = %s\n", __func__, VERSION_LOG);
+
+ /* Allocate Device Data */
+ fp = devm_kzalloc(&pdev->dev, sizeof(struct elan_data), GFP_KERNEL);
+ if(!fp)
+ ELAN_DEBUG("kzmalloc elan data failed\n");
+
+ /* Init Input Device */
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ ELAN_DEBUG("alloc input_dev failed\n");
+
+ fp->pdev = pdev;
+
+ platform_set_drvdata(pdev, fp);
+
+ input_dev->name = "elan";
+ input_dev->id.bustype = BUS_SPI;
+ input_dev->dev.parent = &pdev->dev;
+ input_set_drvdata(input_dev, fp);
+
+ input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY);
+ input_set_capability(input_dev, EV_KEY, KEY_FP_INT); // change by customer, send key event to framework. KEY_xxx could be changed.
+ input_set_capability(input_dev, EV_KEY, KEY_FP_INT2); // change by customer, send key event to framework. KEY_xxx could be changed.
+
+ fp->input_dev = input_dev;
+
+ /* Init Sysfs */
+ ret = elan_sysfs_create(fp);
+ if(ret < 0)
+ ELAN_DEBUG("sysfs create failed, ret = %d\n", ret);
+
+ /* Init Char Device */
+ ret = elan_setup_cdev(fp);
+ if(ret < 0)
+ ELAN_DEBUG("setup device failed, ret = %d\n", ret);
+
+ /* Register Input Device */
+ ret = input_register_device(input_dev);
+ if(ret)
+ ELAN_DEBUG("register input device failed, ret = %d\n", ret);
+
+ ret = elan_dts_init(fp, pdev->dev.of_node);
+ if(ret < 0)
+ ELAN_DEBUG("device tree initial failed, ret = %d\n", ret);
+
+ ret = elan_gpio_config(fp);
+ if(ret < 0)
+ ELAN_DEBUG("gpio config failed, ret = %d\n", ret);
+
+ wakeup_source_init(&fp->wake_lock, "fp_wake_lock"); //wake_lock_init(&fp->wake_lock, WAKE_LOCK_SUSPEND, "fp_wake_lock");
+ wakeup_source_init(&fp->hal_wake_lock, "hal_fp_wake_lock"); //wake_lock_init(&fp->hal_wake_lock, WAKE_LOCK_SUSPEND, "hal_fp_wake_lock");
+
+ ret = request_irq(fp->irq, elan_irq_handler,
+ IRQF_NO_SUSPEND | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ pdev->dev.driver->name, fp);
+ if(ret)
+ ELAN_DEBUG("request irq failed, ret = %d\n", ret);
+
+ irq_set_irq_wake(fp->irq, 1);
+
+ spin_lock_init(&fp->irq_lock);
+
+#if defined(CONFIG_FB)
+ fb_notif.notifier_call = fb_notifier_callback;
+ fb_register_client(&fb_notif);
+#endif
+
+ /* Set Spi to TZ */
+#if SET_SPI_OWNER
+ ret = set_pipe_ownership();
+#endif
+
+ ELAN_DEBUG("%s() End\n", __func__);
+ return 0;
+}
+
+static int elan_remove(struct platform_device *pdev)
+{
+ struct elan_data *fp = platform_get_drvdata(pdev);
+
+ if (fp->irq)
+ free_irq(fp->irq, fp);
+//[Z20][fingerprint][Kent][17111805][begin]add power gpio for vdd
+#if VDD_POWER_GPIO
+ gpio_free(fp->vdd_gpio);
+#endif
+//[Z20][fingerprint][Kent][17111805][end]add power gpio for vdd
+ gpio_free(fp->int_gpio);
+ gpio_free(fp->rst_gpio);
+
+ misc_deregister(&fp->elan_dev);
+ input_free_device(fp->input_dev);
+
+#if defined(CONFIG_FB)
+ fb_unregister_client(&fb_notif);
+#endif
+
+ kfree(fp);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int elan_suspend(struct device *dev)
+{
+ ELAN_DEBUG("elan suspend!\n");
+ return 0;
+}
+
+static int elan_resume(struct device *dev)
+{
+ ELAN_DEBUG("elan resume!\n");
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+#ifdef CONFIG_OF
+static struct of_device_id elan_metallica_table[] = {
+ { .compatible = "elan,elan_fp",},
+ { },
+};
+#else
+#define elan_metallica_table NULL
+#endif
+
+static struct platform_driver elan_driver = {
+ .driver = {
+ .name = "elan",
+ .owner = THIS_MODULE,
+ .pm = &elan_pm_ops,
+ .of_match_table = elan_metallica_table,
+ },
+ .probe = elan_probe,
+ .remove = elan_remove,
+};
+
+static int __init elan_init(void)
+{
+ int ret = 0;
+ ELAN_DEBUG("%s() Start\n", __func__);
+
+ ret = platform_driver_register(&elan_driver);
+ if(ret < 0)
+ ELAN_DEBUG("%s FAIL !\n", __func__);
+
+ ELAN_DEBUG("%s() End\n", __func__);
+ return 0;
+}
+
+static void __exit elan_exist(void)
+{
+ platform_driver_unregister(&elan_driver);
+}
+
+module_init(elan_init);
+module_exit(elan_exist);
+
+MODULE_AUTHOR("Elan");
+MODULE_DESCRIPTION("ELAN SPI FingerPrint driver");
+MODULE_VERSION(VERSION_LOG);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/fingerprint/elan_fp_qcom_tee.h b/drivers/input/fingerprint/elan_fp_qcom_tee.h
new file mode 100644
index 0000000..1c6f9aa
--- /dev/null
+++ b/drivers/input/fingerprint/elan_fp_qcom_tee.h
@@ -0,0 +1,25 @@
+#ifndef _LINUX_ELAN_FP_H
+#define _LINUX_ELAN_FP_H
+
+#define FINGERPRINT_IOCTL 0x80
+#define ID_IOCTL_RESET _IOW(FINGERPRINT_IOCTL, 6, int)
+#define ID_IOCTL_POLL_INIT _IOW(FINGERPRINT_IOCTL, 20, int)
+#define ID_IOCTL_INPUT_KEYCODE _IOW(FINGERPRINT_IOCTL, 22, int)
+#define ID_IOCTL_POLL_EXIT _IOW(FINGERPRINT_IOCTL, 23, int)
+#define ID_IOCTL_SET_KEYCODE _IOW(FINGERPRINT_IOCTL, 24, int)
+#define ID_IOCTL_READ_FACTORY_STATUS _IOW(FINGERPRINT_IOCTL, 26, int)
+#define ID_IOCTL_WRITE_FACTORY_STATUS _IOW(FINGERPRINT_IOCTL, 27, int)
+#define ID_IOCTL_INPUT_KEYCODE_DOWN _IOW(FINGERPRINT_IOCTL, 28, int)
+#define ID_IOCTL_INPUT_KEYCODE_UP _IOW(FINGERPRINT_IOCTL, 29, int)
+#define ID_IOCTL_INT_STATUS _IOW(FINGERPRINT_IOCTL, 40, int)
+#define ID_IOCTL_WAKE_LOCK_UNLOCK _IOW(FINGERPRINT_IOCTL, 41, int)
+#define ID_IOCTL_EN_IRQ _IOW(FINGERPRINT_IOCTL, 55, int)
+#define ID_IOCTL_DIS_IRQ _IOW(FINGERPRINT_IOCTL, 66, int)
+#define ID_IOCTL_SET_IRQ_TYPE _IOW(FINGERPRINT_IOCTL, 91, int)
+#define ID_IOCTL_DISPLAY_STATUS _IOW(FINGERPRINT_IOCTL, 93, int)
+#define ID_IOCTL_DISPLAY_NOTIFY _IOW(FINGERPRINT_IOCTL, 94, int)
+#define ID_IOCTL_SET_PID _IOW(FINGERPRINT_IOCTL, 95, int)
+
+#define CUSTOMER_IOCTLID 0xD0 //For customer define
+
+#endif /* _LINUX_ELAN_FP_H */