axolotl: fingerprint: initial bringup

Device tree overlays and goodix kernel drivers.

Change-Id: Ie319dba7e2481ba830aaedc19a0f06597d031b90
Signed-off-by: Alexander Martinz <amartinz@shiftphones.com>
diff --git a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
index e1a3943..f605242 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-pinctrl.dtsi
@@ -2026,6 +2026,41 @@
 			};
 		};
 
+		fingerprint {
+			fingerprint_sleep: fingerprint_sleep {
+				/* active state */
+				mux {
+					/* GPIO 121 NFC Read Interrupt */
+					pins = "gpio121";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio121";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
+
+			};
+
+			fingerprint_active: fingerprint_active {
+				/* active state */
+				mux {
+					/* GPIO 121 NFC Read Interrupt */
+					pins = "gpio121";
+					function = "gpio";
+				};
+
+				config {
+					pins = "gpio121";
+					drive-strength = <2>; /* 2 MA */
+					bias-pull-up;
+				};
+
+			};
+
+		};
+
 		nfc {
 			nfc_int_active: nfc_int_active {
 				/* active state */
diff --git a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
index 438d7c5..4f909b3 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-qrd.dtsi
@@ -56,6 +56,20 @@
 };
 
 &soc {
+	goodix_fp {
+		compatible = "goodix,fingerprint";
+		interrupt-parent = <&tlmm>;
+		fp-gpio-irq = <&tlmm 121 0x00>;
+		fp-gpio-reset = <&tlmm 122 0x00>;
+		status = "okay";
+		pinctrl-names = "fingerprint_sleep", "fingerprint_active";
+		pinctrl-0 = <&fingerprint_sleep>;
+		pinctrl-1 = <&fingerprint_active>;
+	};
+};
+
+
+&soc {
 	gpio_keys {
 		compatible = "gpio-keys";
 		label = "gpio-keys";
diff --git a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
index 34beda4..f6fb7d4 100644
--- a/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845-regulator.dtsi
@@ -543,6 +543,7 @@
 			regulator-max-microvolt = <3104000>;
 			qcom,init-voltage = <2856000>;
 			qcom,init-mode = <RPMH_REGULATOR_MODE_LPM>;
+			regulator-always-on;
 		};
 	};
 
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 75443c3..9215e7b 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -2491,6 +2491,7 @@
 		qcom,pipe-attr-ee;
 	};
 
+/*
 	qcom,qbt1000 {
 		compatible = "qcom,qbt1000";
 		clock-names = "core", "iface";
@@ -2498,6 +2499,7 @@
 		qcom,ipc-gpio = <&tlmm 121 0>;
 		qcom,finger-detect-gpio = <&pm8998_gpios 5 0>;
 	};
+*/
 
 	qcom_seecom: qseecom@86d00000 {
 		compatible = "qcom,qseecom";
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index de41b16..e10d3b9 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -226,6 +226,8 @@
 
 source "drivers/input/gameport/Kconfig"
 
+source "drivers/input/fingerprint/Kconfig"
+
 endmenu
 
 endmenu
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 7ff1b70..5297736 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -34,3 +34,4 @@
 obj-$(CONFIG_INPUT_KEYRESET)	+= keyreset.o
 obj-$(CONFIG_INPUT_KEYCOMBO)	+= keycombo.o
 obj-$(CONFIG_SMI130)   += sensors/smi130/
+obj-$(CONFIG_GOODIX_FINGERPRINT) += fingerprint/
\ No newline at end of file
diff --git a/drivers/input/fingerprint/Kconfig b/drivers/input/fingerprint/Kconfig
new file mode 100755
index 0000000..977fd26
--- /dev/null
+++ b/drivers/input/fingerprint/Kconfig
@@ -0,0 +1,5 @@
+config GOODIX_FINGERPRINT
+	tristate "generic goodix fingerprint driver"
+	default n
+	help
+	add support for goodix fingerprint driver.
diff --git a/drivers/input/fingerprint/Makefile b/drivers/input/fingerprint/Makefile
new file mode 100755
index 0000000..26c678f
--- /dev/null
+++ b/drivers/input/fingerprint/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_GOODIX_FINGERPRINT) += gf_spi.o platform.o netlink.o
diff --git a/drivers/input/fingerprint/gf_spi.c b/drivers/input/fingerprint/gf_spi.c
new file mode 100755
index 0000000..5fad0ac
--- /dev/null
+++ b/drivers/input/fingerprint/gf_spi.c
@@ -0,0 +1,899 @@
+/*
+ * TEE driver for goodix fingerprint sensor
+ * Copyright (C) 2016 Goodix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#define pr_fmt(fmt)		KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/input.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/ktime.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/timer.h>
+#include <linux/notifier.h>
+#include <linux/fb.h>
+#include <linux/pm_qos.h>
+#include <linux/cpufreq.h>
+#include <linux/pinctrl/consumer.h>
+#include "gf_spi.h"
+
+#if defined(USE_SPI_BUS)
+#include <linux/spi/spi.h>
+#include <linux/spi/spidev.h>
+#elif defined(USE_PLATFORM_BUS)
+#include <linux/platform_device.h>
+#endif
+
+#define VER_MAJOR   1
+#define VER_MINOR   2
+#define PATCH_LEVEL 4
+#define EXTEND_VER  2
+
+#define WAKELOCK_HOLD_TIME 500 /* in ms */
+
+#define GF_SPIDEV_NAME     "goodix,fingerprint"
+/*device name after register in charater*/
+#define GF_DEV_NAME            "goodix_fp"
+#define	GF_INPUT_NAME	    "fp_input"	/*"goodix_fp" */
+
+#define	CHRD_DRIVER_NAME	"goodix_fp_spi"
+#define	CLASS_NAME		    "goodix_fp"
+
+#define N_SPI_MINORS		32	/* ... up to 256 */
+static int SPIDEV_MAJOR;
+
+static DECLARE_BITMAP(minors, N_SPI_MINORS);
+static LIST_HEAD(device_list);
+static DEFINE_MUTEX(device_list_lock);
+static struct wakeup_source fp_wakelock;
+static struct gf_dev gf;
+
+static struct gf_key_map maps[] = {
+	{ EV_KEY, GF_KEY_INPUT_HOME },
+	{ EV_KEY, GF_KEY_INPUT_MENU },
+	{ EV_KEY, GF_KEY_INPUT_BACK },
+	{ EV_KEY, GF_KEY_INPUT_POWER },
+	{ EV_KEY, GF_KEY_INPUT_CAMERA },
+#if defined(SUPPORT_NAV_EVENT)
+	{ EV_KEY, GF_NAV_INPUT_UP },
+	{ EV_KEY, GF_NAV_INPUT_DOWN },
+	{ EV_KEY, GF_NAV_INPUT_RIGHT },
+	{ EV_KEY, GF_NAV_INPUT_LEFT },
+	{ EV_KEY, GF_NAV_INPUT_CLICK },
+	{ EV_KEY, GF_NAV_INPUT_DOUBLE_CLICK },
+	{ EV_KEY, GF_NAV_INPUT_LONG_PRESS },
+	{ EV_KEY, GF_NAV_INPUT_HEAVY },
+#endif
+};
+
+static void gf_enable_irq(struct gf_dev *gf_dev)
+{
+	if (gf_dev->irq_enabled) {
+		pr_warn("IRQ has been enabled.\n");
+	} else {
+		enable_irq(gf_dev->irq);
+		gf_dev->irq_enabled = 1;
+	}
+}
+
+static void gf_disable_irq(struct gf_dev *gf_dev)
+{
+	if (gf_dev->irq_enabled) {
+		gf_dev->irq_enabled = 0;
+		disable_irq(gf_dev->irq);
+	} else {
+		pr_warn("IRQ has been disabled.\n");
+	}
+}
+
+#ifdef AP_CONTROL_CLK
+static long spi_clk_max_rate(struct clk *clk, unsigned long rate)
+{
+	long lowest_available, nearest_low, step_size, cur;
+	long step_direction = -1;
+	long guess = rate;
+	int max_steps = 10;
+
+	cur = clk_round_rate(clk, rate);
+	if (cur == rate)
+		return rate;
+
+	/* if we got here then: cur > rate */
+	lowest_available = clk_round_rate(clk, 0);
+	if (lowest_available > rate)
+		return -EINVAL;
+
+	step_size = (rate - lowest_available) >> 1;
+	nearest_low = lowest_available;
+
+	while (max_steps-- && step_size) {
+		guess += step_size * step_direction;
+		cur = clk_round_rate(clk, guess);
+
+		if ((cur < rate) && (cur > nearest_low))
+			nearest_low = cur;
+		/*
+		 * if we stepped too far, then start stepping in the other
+		 * direction with half the step size
+		 */
+		if (((cur > rate) && (step_direction > 0))
+				|| ((cur < rate) && (step_direction < 0))) {
+			step_direction = -step_direction;
+			step_size >>= 1;
+		}
+	}
+	return nearest_low;
+}
+
+static void spi_clock_set(struct gf_dev *gf_dev, int speed)
+{
+	long rate;
+	int rc;
+
+	rate = spi_clk_max_rate(gf_dev->core_clk, speed);
+	if (rate < 0) {
+		pr_info("%s: no match found for requested clock frequency:%d",
+				__func__, speed);
+		return;
+	}
+
+	rc = clk_set_rate(gf_dev->core_clk, rate);
+}
+
+static int gfspi_ioctl_clk_init(struct gf_dev *data)
+{
+	pr_debug("%s: enter\n", __func__);
+
+	data->clk_enabled = 0;
+	data->core_clk = clk_get(&data->spi->dev, "core_clk");
+	if (IS_ERR_OR_NULL(data->core_clk)) {
+		pr_err("%s: fail to get core_clk\n", __func__);
+		return -EPERM;
+	}
+	data->iface_clk = clk_get(&data->spi->dev, "iface_clk");
+	if (IS_ERR_OR_NULL(data->iface_clk)) {
+		pr_err("%s: fail to get iface_clk\n", __func__);
+		clk_put(data->core_clk);
+		data->core_clk = NULL;
+		return -ENOENT;
+	}
+	return 0;
+}
+
+static int gfspi_ioctl_clk_enable(struct gf_dev *data)
+{
+	int err;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (data->clk_enabled)
+		return 0;
+
+	err = clk_prepare_enable(data->core_clk);
+	if (err) {
+		pr_err("%s: fail to enable core_clk\n", __func__);
+		return -EPERM;
+	}
+
+	err = clk_prepare_enable(data->iface_clk);
+	if (err) {
+		pr_err("%s: fail to enable iface_clk\n", __func__);
+		clk_disable_unprepare(data->core_clk);
+		return -ENOENT;
+	}
+
+	data->clk_enabled = 1;
+
+	return 0;
+}
+
+static int gfspi_ioctl_clk_disable(struct gf_dev *data)
+{
+	pr_debug("%s: enter\n", __func__);
+
+	if (!data->clk_enabled)
+		return 0;
+
+	clk_disable_unprepare(data->core_clk);
+	clk_disable_unprepare(data->iface_clk);
+	data->clk_enabled = 0;
+
+	return 0;
+}
+
+static int gfspi_ioctl_clk_uninit(struct gf_dev *data)
+{
+	pr_debug("%s: enter\n", __func__);
+
+	if (data->clk_enabled)
+		gfspi_ioctl_clk_disable(data);
+
+	if (!IS_ERR_OR_NULL(data->core_clk)) {
+		clk_put(data->core_clk);
+		data->core_clk = NULL;
+	}
+
+	if (!IS_ERR_OR_NULL(data->iface_clk)) {
+		clk_put(data->iface_clk);
+		data->iface_clk = NULL;
+	}
+
+	return 0;
+}
+#endif
+
+static void nav_event_input(struct gf_dev *gf_dev, gf_nav_event_t nav_event)
+{
+	uint32_t nav_input = 0;
+
+	switch (nav_event) {
+	case GF_NAV_FINGER_DOWN:
+		pr_debug("%s nav finger down\n", __func__);
+		break;
+
+	case GF_NAV_FINGER_UP:
+		pr_debug("%s nav finger up\n", __func__);
+		break;
+
+	case GF_NAV_DOWN:
+		nav_input = GF_NAV_INPUT_DOWN;
+		pr_debug("%s nav down\n", __func__);
+		break;
+
+	case GF_NAV_UP:
+		nav_input = GF_NAV_INPUT_UP;
+		pr_debug("%s nav up\n", __func__);
+		break;
+
+	case GF_NAV_LEFT:
+		nav_input = GF_NAV_INPUT_LEFT;
+		pr_debug("%s nav left\n", __func__);
+		break;
+
+	case GF_NAV_RIGHT:
+		nav_input = GF_NAV_INPUT_RIGHT;
+		pr_debug("%s nav right\n", __func__);
+		break;
+
+	case GF_NAV_CLICK:
+		nav_input = GF_NAV_INPUT_CLICK;
+		pr_debug("%s nav click\n", __func__);
+		break;
+
+	case GF_NAV_HEAVY:
+		nav_input = GF_NAV_INPUT_HEAVY;
+		pr_debug("%s nav heavy\n", __func__);
+		break;
+
+	case GF_NAV_LONG_PRESS:
+		nav_input = GF_NAV_INPUT_LONG_PRESS;
+		pr_debug("%s nav long press\n", __func__);
+		break;
+
+	case GF_NAV_DOUBLE_CLICK:
+		nav_input = GF_NAV_INPUT_DOUBLE_CLICK;
+		pr_debug("%s nav double click\n", __func__);
+		break;
+
+	default:
+		pr_warn("%s unknown nav event: %d\n", __func__, nav_event);
+		break;
+	}
+
+	if ((nav_event != GF_NAV_FINGER_DOWN) &&
+			(nav_event != GF_NAV_FINGER_UP)) {
+		input_report_key(gf_dev->input, nav_input, 1);
+		input_sync(gf_dev->input);
+		input_report_key(gf_dev->input, nav_input, 0);
+		input_sync(gf_dev->input);
+	}
+}
+
+static irqreturn_t gf_irq(int irq, void *handle)
+{
+#if defined(GF_NETLINK_ENABLE)
+	char msg = GF_NET_EVENT_IRQ;
+	__pm_wakeup_event(&fp_wakelock, WAKELOCK_HOLD_TIME);
+	sendnlmsg(&msg);
+#elif defined(GF_FASYNC)
+	struct gf_dev *gf_dev = &gf;
+	if (gf_dev->async)
+		kill_fasync(&gf_dev->async, SIGIO, POLL_IN);
+#endif
+	return IRQ_HANDLED;
+}
+
+static void gf_kernel_key_input(struct gf_dev *gf_dev, struct gf_key *gf_key)
+{
+	uint32_t key_input = 0;
+
+	if (gf_key->key == GF_KEY_HOME) {
+		key_input = GF_KEY_INPUT_HOME;
+	} else if (gf_key->key == GF_KEY_POWER) {
+		key_input = GF_KEY_INPUT_POWER;
+	} else if (gf_key->key == GF_KEY_CAMERA) {
+		key_input = GF_KEY_INPUT_CAMERA;
+	} else {
+		/* add special key define */
+		key_input = gf_key->key;
+	}
+	pr_info("%s: received key event[%d], key=%d, value=%d\n",
+			__func__, key_input, gf_key->key, gf_key->value);
+
+	if ((GF_KEY_POWER == gf_key->key || GF_KEY_CAMERA == gf_key->key)
+			&& (gf_key->value == 1)) {
+		input_report_key(gf_dev->input, key_input, 1);
+		input_sync(gf_dev->input);
+		input_report_key(gf_dev->input, key_input, 0);
+		input_sync(gf_dev->input);
+	}
+
+	if (gf_key->key == GF_KEY_HOME) {
+		input_report_key(gf_dev->input, key_input, gf_key->value);
+		input_sync(gf_dev->input);
+	}
+}
+
+static long gf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct gf_dev *gf_dev = &gf;
+	struct gf_key gf_key;
+#if defined(SUPPORT_NAV_EVENT)
+	gf_nav_event_t nav_event = GF_NAV_NONE;
+#endif
+	int retval = 0;
+	u8 netlink_route = NETLINK_TEST;
+	struct gf_ioc_chip_info info;
+
+	if (_IOC_TYPE(cmd) != GF_IOC_MAGIC)
+		return -ENODEV;
+
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		retval = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
+	else if (_IOC_DIR(cmd) & _IOC_WRITE)
+		retval = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
+	if (retval)
+		return -EFAULT;
+
+	if (gf_dev->device_available == 0) {
+		if ((cmd == GF_IOC_ENABLE_POWER) || (cmd == GF_IOC_DISABLE_POWER)) {
+			pr_info("power cmd\n");
+		} else {
+			pr_info("Sensor is power off currently.\n");
+			return -ENODEV;
+		}
+	}
+
+	switch (cmd) {
+	case GF_IOC_INIT:
+		pr_debug("%s GF_IOC_INIT\n", __func__);
+		if (copy_to_user((void __user *)arg, (void *)&netlink_route, sizeof(u8))) {
+			retval = -EFAULT;
+			break;
+		}
+		break;
+	case GF_IOC_EXIT:
+		pr_debug("%s GF_IOC_EXIT\n", __func__);
+		break;
+	case GF_IOC_DISABLE_IRQ:
+		pr_debug("%s GF_IOC_DISABEL_IRQ\n", __func__);
+		gf_disable_irq(gf_dev);
+		break;
+	case GF_IOC_ENABLE_IRQ:
+		pr_debug("%s GF_IOC_ENABLE_IRQ\n", __func__);
+		gf_enable_irq(gf_dev);
+		break;
+	case GF_IOC_RESET:
+		pr_info("%s GF_IOC_RESET\n", __func__);
+		gf_hw_reset(gf_dev, 3);
+		break;
+	case GF_IOC_INPUT_KEY_EVENT:
+		break;	//lcy,disable return key value
+		if (copy_from_user(&gf_key, (void __user *)arg, sizeof(struct gf_key))) {
+			pr_info("Failed to copy input key event from user to kernel\n");
+			retval = -EFAULT;
+			break;
+		}
+
+		gf_kernel_key_input(gf_dev, &gf_key);
+		break;
+#if defined(SUPPORT_NAV_EVENT)
+	case GF_IOC_NAV_EVENT:
+		pr_debug("%s GF_IOC_NAV_EVENT\n", __func__);
+		if (copy_from_user(&nav_event, (void __user *)arg, sizeof(gf_nav_event_t))) {
+			pr_info("Failed to copy nav event from user to kernel\n");
+			retval = -EFAULT;
+			break;
+		}
+
+		nav_event_input(gf_dev, nav_event);
+		break;
+#endif
+
+	case GF_IOC_ENABLE_SPI_CLK:
+		pr_debug("%s GF_IOC_ENABLE_SPI_CLK\n", __func__);
+#ifdef AP_CONTROL_CLK
+		gfspi_ioctl_clk_enable(gf_dev);
+#else
+		pr_debug("Doesn't support control clock.\n");
+#endif
+		break;
+	case GF_IOC_DISABLE_SPI_CLK:
+		pr_debug("%s GF_IOC_DISABLE_SPI_CLK\n", __func__);
+#ifdef AP_CONTROL_CLK
+		gfspi_ioctl_clk_disable(gf_dev);
+#else
+		pr_debug("Doesn't support control clock\n");
+#endif
+		break;
+	case GF_IOC_ENABLE_POWER:
+		pr_debug("%s GF_IOC_ENABLE_POWER\n", __func__);
+		if (gf_dev->device_available == 1)
+			pr_info("Sensor has already powered-on.\n");
+		else
+			gf_power_on(gf_dev);
+		gf_dev->device_available = 1;
+		break;
+	case GF_IOC_DISABLE_POWER:
+		pr_debug("%s GF_IOC_DISABLE_POWER\n", __func__);
+		if (gf_dev->device_available == 0)
+			pr_info("Sensor has already powered-off.\n");
+		else
+			gf_power_off(gf_dev);
+		gf_dev->device_available = 0;
+		break;
+	case GF_IOC_ENTER_SLEEP_MODE:
+		pr_debug("%s GF_IOC_ENTER_SLEEP_MODE\n", __func__);
+		break;
+	case GF_IOC_GET_FW_INFO:
+		pr_debug("%s GF_IOC_GET_FW_INFO\n", __func__);
+		break;
+	case GF_IOC_REMOVE:
+		pr_debug("%s GF_IOC_REMOVE\n", __func__);
+		break;
+	case GF_IOC_CHIP_INFO:
+		pr_debug("%s GF_IOC_CHIP_INFO\n", __func__);
+		if (copy_from_user(&info, (void __user *)arg, sizeof(struct gf_ioc_chip_info))) {
+			retval = -EFAULT;
+			break;
+		}
+		pr_info("vendor_id : 0x%x\n", info.vendor_id);
+		pr_info("mode : 0x%x\n", info.mode);
+		pr_info("operation: 0x%x\n", info.operation);
+		break;
+	default:
+		pr_warn("unsupport cmd:0x%x\n", cmd);
+		break;
+	}
+
+	return retval;
+}
+
+#ifdef CONFIG_COMPAT
+static long gf_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	return gf_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
+}
+#endif /*CONFIG_COMPAT*/
+
+
+static int gf_open(struct inode *inode, struct file *filp)
+{
+	struct gf_dev *gf_dev;
+	int status = -ENXIO;
+
+	mutex_lock(&device_list_lock);
+
+	list_for_each_entry(gf_dev, &device_list, device_entry) {
+		if (gf_dev->devt == inode->i_rdev) {
+			pr_info("Found\n");
+			status = 0;
+			break;
+		}
+	}
+
+	if (status == 0) {
+		if (status == 0) {
+			gf_dev->users++;
+			filp->private_data = gf_dev;
+			nonseekable_open(inode, filp);
+			pr_info("Succeed to open device. irq = %d\n",
+					gf_dev->irq);
+			if (gf_dev->users == 1)
+				gf_enable_irq(gf_dev);
+			gf_hw_reset(gf_dev, 3);
+			pr_info("Did hw reset after opening device\n");
+			gf_dev->device_available = 1;
+		}
+	} else {
+		pr_info("No device for minor %d\n", iminor(inode));
+	}
+	mutex_unlock(&device_list_lock);
+	return status;
+}
+
+#ifdef GF_FASYNC
+static int gf_fasync(int fd, struct file *filp, int mode)
+{
+	struct gf_dev *gf_dev = filp->private_data;
+	int ret;
+
+	ret = fasync_helper(fd, filp, mode, &gf_dev->async);
+	pr_info("ret = %d\n", ret);
+	return ret;
+}
+#endif
+
+static int gf_release(struct inode *inode, struct file *filp)
+{
+	struct gf_dev *gf_dev;
+	int status = 0;
+
+	mutex_lock(&device_list_lock);
+	gf_dev = filp->private_data;
+	filp->private_data = NULL;
+
+	/*last close?? */
+	gf_dev->users--;
+	if (!gf_dev->users) {
+
+		pr_info("disble_irq. irq = %d\n", gf_dev->irq);
+		gf_disable_irq(gf_dev);
+		/*power off the sensor*/
+		gf_dev->device_available = 0;
+		gf_power_off(gf_dev);
+	}
+	mutex_unlock(&device_list_lock);
+	return status;
+}
+
+static const struct file_operations gf_fops = {
+	.owner = THIS_MODULE,
+	/* REVISIT switch to aio primitives, so that userspace
+	 * gets more complete API coverage.  It'll simplify things
+	 * too, except for the locking.
+	 */
+	.unlocked_ioctl = gf_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = gf_compat_ioctl,
+#endif /*CONFIG_COMPAT*/
+	.open = gf_open,
+	.release = gf_release,
+#ifdef GF_FASYNC
+	.fasync = gf_fasync,
+#endif
+};
+
+static int goodix_fb_state_chg_callback(struct notifier_block *nb,
+		unsigned long val, void *data)
+{
+	struct gf_dev *gf_dev;
+	struct fb_event *evdata = data;
+	unsigned int blank;
+	char msg = 0;
+
+	if (val != FB_EARLY_EVENT_BLANK)
+		return 0;
+	pr_info("[info] %s go to the goodix_fb_state_chg_callback value = %d\n",
+			__func__, (int)val);
+	gf_dev = container_of(nb, struct gf_dev, notifier);
+
+	if (evdata && evdata->data && val == FB_EARLY_EVENT_BLANK && gf_dev) {
+		blank = *(int *)(evdata->data);
+		switch (blank) {
+		case FB_BLANK_POWERDOWN:
+			if (gf_dev->device_available == 1) {
+				gf_dev->fb_black = 1;
+#if defined(GF_NETLINK_ENABLE)
+				msg = GF_NET_EVENT_FB_BLACK;
+				sendnlmsg(&msg);
+#elif defined(GF_FASYNC)
+				if (gf_dev->async)
+					kill_fasync(&gf_dev->async, SIGIO, POLL_IN);
+#endif
+			}
+			break;
+		case FB_BLANK_UNBLANK:
+			if (gf_dev->device_available == 1) {
+				gf_dev->fb_black = 0;
+#if defined(GF_NETLINK_ENABLE)
+				msg = GF_NET_EVENT_FB_UNBLACK;
+				sendnlmsg(&msg);
+#elif defined(GF_FASYNC)
+				if (gf_dev->async)
+					kill_fasync(&gf_dev->async, SIGIO, POLL_IN);
+#endif
+			}
+			break;
+		default:
+			pr_info("%s defalut\n", __func__);
+			break;
+		}
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block goodix_noti_block = {
+	.notifier_call = goodix_fb_state_chg_callback,
+};
+
+
+static struct class *gf_class;
+#if defined(USE_SPI_BUS)
+static int gf_probe(struct spi_device *spi)
+#elif defined(USE_PLATFORM_BUS)
+static int gf_probe(struct platform_device *pdev)
+#endif
+{
+	struct gf_dev *gf_dev = &gf;
+	int status = -EINVAL;
+	unsigned long minor;
+	int i;
+
+	/* Initialize the driver data */
+	INIT_LIST_HEAD(&gf_dev->device_entry);
+#if defined(USE_SPI_BUS)
+	gf_dev->spi = spi;
+#elif defined(USE_PLATFORM_BUS)
+	gf_dev->spi = pdev;
+#endif
+	gf_dev->irq_gpio = -EINVAL;
+	gf_dev->reset_gpio = -EINVAL;
+	gf_dev->pwr_gpio = -EINVAL;
+	gf_dev->device_available = 0;
+	gf_dev->fb_black = 0;
+
+	if (gf_parse_dts(gf_dev))
+		goto error_hw;
+
+	/* If we can allocate a minor number, hook up this device.
+	 * Reusing minors is fine so long as udev or mdev is working.
+	 */
+	mutex_lock(&device_list_lock);
+	minor = find_first_zero_bit(minors, N_SPI_MINORS);
+	if (minor < N_SPI_MINORS) {
+		struct device *dev;
+
+		gf_dev->devt = MKDEV(SPIDEV_MAJOR, minor);
+		dev = device_create(gf_class, &gf_dev->spi->dev, gf_dev->devt,
+				gf_dev, GF_DEV_NAME);
+		status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
+	} else {
+		dev_dbg(&gf_dev->spi->dev, "no minor number available!\n");
+		status = -ENODEV;
+		mutex_unlock(&device_list_lock);
+		goto error_hw;
+	}
+
+	if (status == 0) {
+		set_bit(minor, minors);
+		list_add(&gf_dev->device_entry, &device_list);
+	} else {
+		gf_dev->devt = 0;
+	}
+	mutex_unlock(&device_list_lock);
+
+	if (status == 0) {
+		/*input device subsystem */
+		gf_dev->input = input_allocate_device();
+		if (gf_dev->input == NULL) {
+			pr_err("%s, failed to allocate input device\n", __func__);
+			status = -ENOMEM;
+			goto error_dev;
+		}
+		for (i = 0; i < ARRAY_SIZE(maps); i++)
+			input_set_capability(gf_dev->input, maps[i].type, maps[i].code);
+
+		gf_dev->input->name = GF_INPUT_NAME;
+		status = input_register_device(gf_dev->input);
+		if (status) {
+			pr_err("failed to register input device\n");
+			goto error_input;
+		}
+	}
+#ifdef AP_CONTROL_CLK
+	pr_info("Get the clk resource.\n");
+	/* Enable spi clock */
+	if (gfspi_ioctl_clk_init(gf_dev))
+		goto gfspi_probe_clk_init_failed;
+
+	if (gfspi_ioctl_clk_enable(gf_dev))
+		goto gfspi_probe_clk_enable_failed;
+
+	spi_clock_set(gf_dev, 1000000);
+#endif
+
+	gf_dev->notifier = goodix_noti_block;
+	fb_register_client(&gf_dev->notifier);
+
+	gf_dev->irq = gf_irq_num(gf_dev);
+
+	wakeup_source_init(&fp_wakelock, "fp_wakelock");
+	status = request_threaded_irq(gf_dev->irq, NULL, gf_irq,
+			IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+			"gf", gf_dev);
+
+	if (status) {
+		pr_err("Failed to request IRQ: %d\n", gf_dev->irq);
+		goto err_irq;
+	}
+	enable_irq_wake(gf_dev->irq);
+	gf_dev->irq_enabled = 1;
+	gf_disable_irq(gf_dev);
+
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	pr_info("version V%d.%d.%02d.%02d\n", VER_MAJOR, VER_MINOR, PATCH_LEVEL, EXTEND_VER);
+
+	return status;
+
+err_irq:
+		input_unregister_device(gf_dev->input);
+#ifdef AP_CONTROL_CLK
+gfspi_probe_clk_enable_failed:
+	gfspi_ioctl_clk_uninit(gf_dev);
+gfspi_probe_clk_init_failed:
+#endif
+
+error_input:
+	if (gf_dev->input != NULL)
+		input_free_device(gf_dev->input);
+error_dev:
+	if (gf_dev->devt != 0) {
+		pr_info("Err: status = %d\n", status);
+		mutex_lock(&device_list_lock);
+		list_del(&gf_dev->device_entry);
+		device_destroy(gf_class, gf_dev->devt);
+		clear_bit(MINOR(gf_dev->devt), minors);
+		mutex_unlock(&device_list_lock);
+	}
+error_hw:
+	gf_cleanup(gf_dev);
+	gf_dev->device_available = 0;
+
+	return status;
+}
+
+#if defined(USE_SPI_BUS)
+static int gf_remove(struct spi_device *spi)
+#elif defined(USE_PLATFORM_BUS)
+static int gf_remove(struct platform_device *pdev)
+#endif
+{
+	struct gf_dev *gf_dev = &gf;
+
+	wakeup_source_trash(&fp_wakelock);
+
+	/* make sure ops on existing fds can abort cleanly */
+	if (gf_dev->irq)
+		free_irq(gf_dev->irq, gf_dev);
+
+	if (gf_dev->input != NULL)
+		input_unregister_device(gf_dev->input);
+	input_free_device(gf_dev->input);
+
+	/* prevent new opens */
+	mutex_lock(&device_list_lock);
+	list_del(&gf_dev->device_entry);
+	device_destroy(gf_class, gf_dev->devt);
+	clear_bit(MINOR(gf_dev->devt), minors);
+	if (gf_dev->users == 0)
+		gf_cleanup(gf_dev);
+
+	fb_unregister_client(&gf_dev->notifier);
+	mutex_unlock(&device_list_lock);
+
+	return 0;
+}
+
+static const struct of_device_id gx_match_table[] = {
+	{ .compatible = GF_SPIDEV_NAME },
+	{},
+};
+
+#if defined(USE_SPI_BUS)
+static struct spi_driver gf_driver = {
+#elif defined(USE_PLATFORM_BUS)
+static struct platform_driver gf_driver = {
+#endif
+	.driver = {
+		.name = GF_DEV_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = gx_match_table,
+	},
+	.probe = gf_probe,
+	.remove = gf_remove,
+};
+
+static int __init gf_init(void)
+{
+	int status;
+
+	/* Claim our 256 reserved device numbers.  Then register a class
+	 * that will key udev/mdev to add/remove /dev nodes.  Last, register
+	 * the driver which manages those device numbers.
+	 */
+
+	BUILD_BUG_ON(N_SPI_MINORS > 256);
+	status = register_chrdev(SPIDEV_MAJOR, CHRD_DRIVER_NAME, &gf_fops);
+	if (status < 0) {
+		pr_warn("Failed to register char device!\n");
+		return status;
+	}
+	SPIDEV_MAJOR = status;
+	gf_class = class_create(THIS_MODULE, CLASS_NAME);
+	if (IS_ERR(gf_class)) {
+		unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name);
+		pr_warn("Failed to create class.\n");
+		return PTR_ERR(gf_class);
+	}
+#if defined(USE_PLATFORM_BUS)
+	status = platform_driver_register(&gf_driver);
+#elif defined(USE_SPI_BUS)
+	status = spi_register_driver(&gf_driver);
+#endif
+	if (status < 0) {
+		class_destroy(gf_class);
+		unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name);
+		pr_warn("Failed to register SPI driver.\n");
+	}
+
+#ifdef GF_NETLINK_ENABLE
+	netlink_init();
+#endif
+	pr_info("status = 0x%x\n", status);
+	return 0;
+}
+module_init(gf_init);
+
+static void __exit gf_exit(void)
+{
+#ifdef GF_NETLINK_ENABLE
+	netlink_exit();
+#endif
+#if defined(USE_PLATFORM_BUS)
+	platform_driver_unregister(&gf_driver);
+#elif defined(USE_SPI_BUS)
+	spi_unregister_driver(&gf_driver);
+#endif
+	class_destroy(gf_class);
+	unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name);
+}
+module_exit(gf_exit);
+
+MODULE_AUTHOR("Jiangtao Yi, <yijiangtao@goodix.com>");
+MODULE_AUTHOR("Jandy Gou, <gouqingsong@goodix.com>");
+MODULE_DESCRIPTION("goodix fingerprint sensor device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/fingerprint/gf_spi.h b/drivers/input/fingerprint/gf_spi.h
new file mode 100755
index 0000000..f0342f2
--- /dev/null
+++ b/drivers/input/fingerprint/gf_spi.h
@@ -0,0 +1,171 @@
+/*
+ * driver definition for sensor driver
+ * Copyright (C) 2016 Goodix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#ifndef __GF_SPI_H
+#define __GF_SPI_H
+
+#include <linux/types.h>
+#include <linux/notifier.h>
+#include <linux/pinctrl/consumer.h>
+/**********************************************************/
+enum FP_MODE {
+	GF_IMAGE_MODE = 0,
+	GF_KEY_MODE,
+	GF_SLEEP_MODE,
+	GF_FF_MODE,
+	GF_DEBUG_MODE = 0x56
+};
+
+#define SUPPORT_NAV_EVENT
+
+#if defined(SUPPORT_NAV_EVENT)
+#define GF_NAV_INPUT_UP			KEY_UP
+#define GF_NAV_INPUT_DOWN		KEY_DOWN
+#define GF_NAV_INPUT_LEFT		KEY_LEFT
+#define GF_NAV_INPUT_RIGHT		KEY_RIGHT
+#define GF_NAV_INPUT_CLICK		KEY_VOLUMEDOWN
+#define GF_NAV_INPUT_DOUBLE_CLICK	KEY_VOLUMEUP
+#define GF_NAV_INPUT_LONG_PRESS		KEY_SEARCH
+#define GF_NAV_INPUT_HEAVY		KEY_CHAT
+#endif
+
+#define GF_KEY_INPUT_HOME		KEY_HOME
+#define GF_KEY_INPUT_MENU		KEY_MENU
+#define GF_KEY_INPUT_BACK		KEY_BACK
+#define GF_KEY_INPUT_POWER		KEY_POWER
+#define GF_KEY_INPUT_CAMERA		KEY_CAMERA
+
+#if defined(SUPPORT_NAV_EVENT)
+typedef enum gf_nav_event {
+	GF_NAV_NONE = 0,
+	GF_NAV_FINGER_UP,
+	GF_NAV_FINGER_DOWN,
+	GF_NAV_UP,
+	GF_NAV_DOWN,
+	GF_NAV_LEFT,
+	GF_NAV_RIGHT,
+	GF_NAV_CLICK,
+	GF_NAV_HEAVY,
+	GF_NAV_LONG_PRESS,
+	GF_NAV_DOUBLE_CLICK,
+} gf_nav_event_t;
+#endif
+
+typedef enum gf_key_event {
+	GF_KEY_NONE = 0,
+	GF_KEY_HOME,
+	GF_KEY_POWER,
+	GF_KEY_MENU,
+	GF_KEY_BACK,
+	GF_KEY_CAMERA,
+} gf_key_event_t;
+
+struct gf_key {
+	enum gf_key_event key;
+	uint32_t value;   /* key down = 1, key up = 0 */
+};
+
+struct gf_key_map {
+	unsigned int type;
+	unsigned int code;
+};
+
+struct gf_ioc_chip_info {
+	unsigned char vendor_id;
+	unsigned char mode;
+	unsigned char operation;
+	unsigned char reserved[5];
+};
+
+#define GF_IOC_MAGIC    'g'
+#define GF_IOC_INIT             _IOR(GF_IOC_MAGIC, 0, uint8_t)
+#define GF_IOC_EXIT             _IO(GF_IOC_MAGIC, 1)
+#define GF_IOC_RESET            _IO(GF_IOC_MAGIC, 2)
+#define GF_IOC_ENABLE_IRQ       _IO(GF_IOC_MAGIC, 3)
+#define GF_IOC_DISABLE_IRQ      _IO(GF_IOC_MAGIC, 4)
+#define GF_IOC_ENABLE_SPI_CLK   _IOW(GF_IOC_MAGIC, 5, uint32_t)
+#define GF_IOC_DISABLE_SPI_CLK  _IO(GF_IOC_MAGIC, 6)
+#define GF_IOC_ENABLE_POWER     _IO(GF_IOC_MAGIC, 7)
+#define GF_IOC_DISABLE_POWER    _IO(GF_IOC_MAGIC, 8)
+#define GF_IOC_INPUT_KEY_EVENT  _IOW(GF_IOC_MAGIC, 9, struct gf_key)
+#define GF_IOC_ENTER_SLEEP_MODE _IO(GF_IOC_MAGIC, 10)
+#define GF_IOC_GET_FW_INFO      _IOR(GF_IOC_MAGIC, 11, uint8_t)
+#define GF_IOC_REMOVE           _IO(GF_IOC_MAGIC, 12)
+#define GF_IOC_CHIP_INFO        _IOW(GF_IOC_MAGIC, 13, struct gf_ioc_chip_info)
+
+#if defined(SUPPORT_NAV_EVENT)
+#define GF_IOC_NAV_EVENT	_IOW(GF_IOC_MAGIC, 14, gf_nav_event_t)
+#define  GF_IOC_MAXNR    15  /* THIS MACRO IS NOT USED NOW... */
+#else
+#define  GF_IOC_MAXNR    14  /* THIS MACRO IS NOT USED NOW... */
+#endif
+
+/*#define AP_CONTROL_CLK       1*/
+#define  USE_PLATFORM_BUS     1
+/*#define  USE_SPI_BUS	1*/
+/* #define GF_FASYNC    1*/	/*If support fasync mechanism.*/
+#define GF_FASYNC 1
+#define GF_NETLINK_ENABLE 0
+#define GF_NET_EVENT_IRQ 1
+#define GF_NET_EVENT_FB_BLACK 2
+#define GF_NET_EVENT_FB_UNBLACK 3
+#define NETLINK_TEST 25
+
+struct gf_dev {
+	dev_t devt;
+	struct list_head device_entry;
+#if defined(USE_SPI_BUS)
+	struct spi_device *spi;
+#elif defined(USE_PLATFORM_BUS)
+	struct platform_device *spi;
+#endif
+	struct clk *core_clk;
+	struct clk *iface_clk;
+
+	struct input_dev *input;
+	/* buffer is NULL unless this device is open (users > 0) */
+	unsigned users;
+	signed irq_gpio;
+	signed reset_gpio;
+	signed pwr_gpio;
+	int irq;
+	int irq_enabled;
+	int clk_enabled;
+#ifdef GF_FASYNC
+	struct fasync_struct *async;
+#endif
+	struct notifier_block notifier;
+	char device_available;
+	char fb_black;
+	struct pinctrl *gf_pinctrl;
+};
+
+int gf_parse_dts(struct gf_dev *gf_dev);
+void gf_cleanup(struct gf_dev *gf_dev);
+
+int gf_power_on(struct gf_dev *gf_dev);
+int gf_power_off(struct gf_dev *gf_dev);
+
+int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms);
+int gf_irq_num(struct gf_dev *gf_dev);
+
+int sendnlmsg(char *msg);
+int netlink_init(void);
+void netlink_exit(void);
+#endif /*__GF_SPI_H*/
diff --git a/drivers/input/fingerprint/netlink.c b/drivers/input/fingerprint/netlink.c
new file mode 100755
index 0000000..6582399
--- /dev/null
+++ b/drivers/input/fingerprint/netlink.c
@@ -0,0 +1,117 @@
+/*
+ * netlink interface
+ * Copyright (C) 2016 Goodix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <net/sock.h>
+#include <net/netlink.h>
+#include "gf_spi.h"
+
+#define NETLINK_TEST 25
+#define MAX_MSGSIZE 32
+
+static int pid = -1;
+static struct sock *nl_sk;
+
+int sendnlmsg(char *msg)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	int len = NLMSG_SPACE(MAX_MSGSIZE);
+	int ret = 0;
+
+	if (!msg || !nl_sk || !pid)
+		return -ENODEV;
+
+	skb = alloc_skb(len, GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	nlh = nlmsg_put(skb, 0, 0, 0, MAX_MSGSIZE, 0);
+	if (!nlh) {
+		kfree_skb(skb);
+		return -EMSGSIZE;
+	}
+
+	NETLINK_CB(skb).portid = 0;
+	NETLINK_CB(skb).dst_group = 0;
+
+	memcpy(NLMSG_DATA(nlh), msg, sizeof(char));
+	pr_debug("send message: %d\n", *(char *)NLMSG_DATA(nlh));
+
+	ret = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
+	if (ret > 0)
+		ret = 0;
+
+	return ret;
+}
+
+static void nl_data_ready(struct sk_buff *__skb)
+{
+	struct sk_buff *skb;
+	struct nlmsghdr *nlh;
+	char str[100];
+
+	skb = skb_get(__skb);
+	if (skb->len >= NLMSG_SPACE(0)) {
+		nlh = nlmsg_hdr(skb);
+
+		memcpy(str, NLMSG_DATA(nlh), sizeof(str));
+		pid = nlh->nlmsg_pid;
+
+		kfree_skb(skb);
+	}
+
+}
+
+
+int netlink_init(void)
+{
+	struct netlink_kernel_cfg netlink_cfg;
+
+	memset(&netlink_cfg, 0, sizeof(struct netlink_kernel_cfg));
+
+	netlink_cfg.groups = 0;
+	netlink_cfg.flags = 0;
+	netlink_cfg.input = nl_data_ready;
+	netlink_cfg.cb_mutex = NULL;
+
+	nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST,
+			&netlink_cfg);
+
+	if (!nl_sk) {
+		pr_err("create netlink socket error\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+void netlink_exit(void)
+{
+	if (nl_sk != NULL) {
+		netlink_kernel_release(nl_sk);
+		nl_sk = NULL;
+	}
+
+	pr_info("self module exited\n");
+}
diff --git a/drivers/input/fingerprint/platform.c b/drivers/input/fingerprint/platform.c
new file mode 100755
index 0000000..a5547f0
--- /dev/null
+++ b/drivers/input/fingerprint/platform.c
@@ -0,0 +1,175 @@
+/*
+ * platform indepent driver interface
+ * Copyright (C) 2016 Goodix
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+
+#include "gf_spi.h"
+
+#if defined(USE_SPI_BUS)
+#include <linux/spi/spi.h>
+#include <linux/spi/spidev.h>
+#elif defined(USE_PLATFORM_BUS)
+#include <linux/platform_device.h>
+#endif
+
+/**
+ * Add pinctrl for fingerprint
+ */
+static int gf_pinctrl_configure(struct gf_dev *gf_dev, bool active)
+{
+	struct pinctrl_state *set_state;
+	int retval;
+
+	if (active) {
+		set_state =
+			pinctrl_lookup_state(gf_dev->gf_pinctrl, "fingerprint_active");
+		if (IS_ERR(set_state)) {
+			pr_err("Cannot get fingerprint active state \n");
+			return PTR_ERR(set_state);
+		}
+	} else {
+			set_state =
+			pinctrl_lookup_state(gf_dev->gf_pinctrl, "fingerprint_sleep");
+		if (IS_ERR(set_state)) {
+			pr_err("Cannot get fingerprint sleep state \n");
+			return PTR_ERR(set_state);
+		}
+	}
+
+	retval = pinctrl_select_state(gf_dev->gf_pinctrl, set_state);
+	if (retval) {
+		pr_err("Cannot set fingerprint active state \n");
+		return retval;
+	}
+
+	return 0;
+}
+
+int gf_parse_dts(struct gf_dev *gf_dev)
+{
+	int rc = 0;
+	struct device *dev = &gf_dev->spi->dev;
+	struct device_node *np = dev->of_node;
+
+	gf_dev->reset_gpio = of_get_named_gpio(np, "fp-gpio-reset", 0);
+	if (gf_dev->reset_gpio < 0) {
+		pr_err("Failed to get reset gpio!\n");
+		return gf_dev->reset_gpio;
+	}
+
+	rc = devm_gpio_request(dev, gf_dev->reset_gpio, "goodix_reset");
+	if (rc) {
+		pr_err("failed to request reset gpio, rc = %d\n", rc);
+		goto err_reset;
+	}
+	gpio_direction_output(gf_dev->reset_gpio, 1);
+	mdelay(10);
+	gpio_direction_output(gf_dev->reset_gpio, 0);
+	mdelay(10);
+	gpio_direction_output(gf_dev->reset_gpio, 1);
+
+	gf_dev->irq_gpio = of_get_named_gpio(np, "fp-gpio-irq", 0);
+	if (gf_dev->irq_gpio < 0) {
+		pr_err("Failed to get irq gpio!\n");
+		return gf_dev->irq_gpio;
+	}
+
+	rc = devm_gpio_request(dev, gf_dev->irq_gpio, "goodix_irq");
+	if (rc) {
+		pr_err("failed to request irq gpio, rc = %d\n", rc);
+		goto err_irq;
+	}
+	gpio_direction_input(gf_dev->irq_gpio);
+
+	gf_dev->gf_pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR(gf_dev->gf_pinctrl)) {
+		if (PTR_ERR(gf_dev->gf_pinctrl) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		gf_dev->gf_pinctrl = NULL;
+	}
+
+	gf_pinctrl_configure(gf_dev, true);
+
+err_irq:
+	devm_gpio_free(dev, gf_dev->reset_gpio);
+err_reset:
+	return rc;
+}
+
+void gf_cleanup(struct gf_dev *gf_dev)
+{
+	pr_info("[info] %s\n", __func__);
+
+	if (gpio_is_valid(gf_dev->irq_gpio)) {
+		gpio_free(gf_dev->irq_gpio);
+		pr_info("remove irq_gpio success\n");
+	}
+	if (gpio_is_valid(gf_dev->reset_gpio)) {
+		gpio_free(gf_dev->reset_gpio);
+		pr_info("remove reset_gpio success\n");
+	}
+}
+
+int gf_power_on(struct gf_dev *gf_dev)
+{
+	int rc = 0;
+
+	/* TODO: add your power control here */
+	return rc;
+}
+
+int gf_power_off(struct gf_dev *gf_dev)
+{
+	int rc = 0;
+
+	/* TODO: add your power control here */
+
+	return rc;
+}
+
+int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms)
+{
+	if (!gf_dev) {
+		pr_info("Input buff is NULL.\n");
+		return -ENODEV;
+	}
+	gpio_direction_output(gf_dev->reset_gpio, 1);
+	gpio_set_value(gf_dev->reset_gpio, 0);
+	mdelay(3);
+	gpio_set_value(gf_dev->reset_gpio, 1);
+	mdelay(delay_ms);
+	return 0;
+}
+
+int gf_irq_num(struct gf_dev *gf_dev)
+{
+	if (!gf_dev) {
+		pr_info("Input buff is NULL.\n");
+		return -ENODEV;
+	} else {
+		return gpio_to_irq(gf_dev->irq_gpio);
+	}
+}
+