From c2ac8879aec41b859cf247359ae280d6eb31d8c6 Mon Sep 17 00:00:00 2001 From: ShivamKumarJha Date: Tue, 30 Mar 2021 23:03:03 +0530 Subject: [PATCH] Add drivers/input/ modifications Signed-off-by: ShivamKumarJha --- drivers/input/Kconfig | 16 + drivers/input/Makefile | 1 + drivers/input/fingerprint/Kconfig | 20 + drivers/input/fingerprint/Makefile | 11 + drivers/input/fingerprint/fpc_fod/Kconfig | 11 + drivers/input/fingerprint/fpc_fod/Makefile | 1 + .../fpc_fod/fpc1020_platform_tee.c | 1150 +++++ drivers/input/fingerprint/fpc_tee/Kconfig | 10 + drivers/input/fingerprint/fpc_tee/Makefile | 1 + .../input/fingerprint/fpc_tee/fpc1020_tee.c | 1037 ++++ drivers/input/fingerprint/fs_tee/Kconfig | 11 + drivers/input/fingerprint/fs_tee/Makefile | 60 + drivers/input/fingerprint/fs_tee/sf_auto.h | 126 + drivers/input/fingerprint/fs_tee/sf_ctl.c | 1570 ++++++ drivers/input/fingerprint/fs_tee/sf_ctl.h | 241 + drivers/input/fingerprint/fs_tee/sf_def.h | 56 + drivers/input/fingerprint/fs_tee/sf_hw.c | 557 ++ drivers/input/fingerprint/fs_tee/sf_spi.h | 110 + drivers/input/fingerprint/fs_tee/sf_user.h | 99 + drivers/input/fingerprint/goodix_fod/Kconfig | 10 + drivers/input/fingerprint/goodix_fod/Makefile | 1 + drivers/input/fingerprint/goodix_fod/gf_spi.c | 1069 ++++ drivers/input/fingerprint/goodix_fod/gf_spi.h | 160 + .../input/fingerprint/goodix_fod/netlink.c | 100 + .../input/fingerprint/goodix_fod/platform.c | 142 + drivers/input/fingerprint/goodix_ta/Kconfig | 10 + drivers/input/fingerprint/goodix_ta/Makefile | 1 + drivers/input/fingerprint/goodix_ta/gf_spi.c | 1069 ++++ drivers/input/fingerprint/goodix_ta/gf_spi.h | 160 + drivers/input/fingerprint/goodix_ta/netlink.c | 100 + .../input/fingerprint/goodix_ta/platform.c | 142 + drivers/input/input.c | 6 +- drivers/input/misc/Kconfig | 13 + drivers/input/misc/Makefile | 3 +- drivers/input/misc/akm09970.c | 981 ++++ drivers/input/misc/aw8624_haptic/Kconfig | 13 + drivers/input/misc/aw8624_haptic/Makefile | 2 + drivers/input/misc/aw8624_haptic/aw8624.c | 4460 +++++++++++++++++ drivers/input/misc/aw8624_haptic/aw8624.h | 455 ++ .../input/misc/aw8624_haptic/aw8624_config.h | 118 + drivers/input/misc/aw8624_haptic/aw8624_reg.h | 540 ++ drivers/input/misc/drv260x_input.c | 1500 ++++++ drivers/input/misc/qpnp-power-on.c | 300 +- 43 files changed, 16439 insertions(+), 4 deletions(-) create mode 100644 drivers/input/fingerprint/Kconfig create mode 100644 drivers/input/fingerprint/Makefile create mode 100644 drivers/input/fingerprint/fpc_fod/Kconfig create mode 100644 drivers/input/fingerprint/fpc_fod/Makefile create mode 100644 drivers/input/fingerprint/fpc_fod/fpc1020_platform_tee.c create mode 100644 drivers/input/fingerprint/fpc_tee/Kconfig create mode 100644 drivers/input/fingerprint/fpc_tee/Makefile create mode 100644 drivers/input/fingerprint/fpc_tee/fpc1020_tee.c create mode 100644 drivers/input/fingerprint/fs_tee/Kconfig create mode 100644 drivers/input/fingerprint/fs_tee/Makefile create mode 100644 drivers/input/fingerprint/fs_tee/sf_auto.h create mode 100644 drivers/input/fingerprint/fs_tee/sf_ctl.c create mode 100644 drivers/input/fingerprint/fs_tee/sf_ctl.h create mode 100644 drivers/input/fingerprint/fs_tee/sf_def.h create mode 100644 drivers/input/fingerprint/fs_tee/sf_hw.c create mode 100644 drivers/input/fingerprint/fs_tee/sf_spi.h create mode 100644 drivers/input/fingerprint/fs_tee/sf_user.h create mode 100644 drivers/input/fingerprint/goodix_fod/Kconfig create mode 100644 drivers/input/fingerprint/goodix_fod/Makefile create mode 100644 drivers/input/fingerprint/goodix_fod/gf_spi.c create mode 100644 drivers/input/fingerprint/goodix_fod/gf_spi.h create mode 100644 drivers/input/fingerprint/goodix_fod/netlink.c create mode 100644 drivers/input/fingerprint/goodix_fod/platform.c create mode 100644 drivers/input/fingerprint/goodix_ta/Kconfig create mode 100644 drivers/input/fingerprint/goodix_ta/Makefile create mode 100644 drivers/input/fingerprint/goodix_ta/gf_spi.c create mode 100644 drivers/input/fingerprint/goodix_ta/gf_spi.h create mode 100644 drivers/input/fingerprint/goodix_ta/netlink.c create mode 100644 drivers/input/fingerprint/goodix_ta/platform.c create mode 100644 drivers/input/misc/akm09970.c create mode 100644 drivers/input/misc/aw8624_haptic/Kconfig create mode 100644 drivers/input/misc/aw8624_haptic/Makefile create mode 100644 drivers/input/misc/aw8624_haptic/aw8624.c create mode 100644 drivers/input/misc/aw8624_haptic/aw8624.h create mode 100644 drivers/input/misc/aw8624_haptic/aw8624_config.h create mode 100644 drivers/input/misc/aw8624_haptic/aw8624_reg.h create mode 100644 drivers/input/misc/drv260x_input.c diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 1b748ad70af3..ff65c6cc5846 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -197,6 +197,20 @@ config INPUT_KEYCOMBO ---help--- Say Y here if you want to take action when some keys are pressed; +config LAST_TOUCH_EVENTS + bool "Last touch events" + default n + depends on INPUT + ---help--- + Say Y here if you want to get last touch events. + +config TOUCH_COUNT_DUMP + bool "Touch cout dump" + default n + depends on LAST_TOUCH_EVENTS + ---help--- + Say Y here if you want to get last touch events. + comment "Input Device Drivers" source "drivers/input/keyboard/Kconfig" @@ -215,6 +229,8 @@ source "drivers/input/sensors/smi130/Kconfig" source "drivers/input/rmi4/Kconfig" +source "drivers/input/fingerprint/Kconfig" + endif menu "Hardware I/O ports" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 7133c3bc1b04..235a0294971c 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_INPUT_MOUSE) += mouse/ obj-$(CONFIG_INPUT_JOYSTICK) += joystick/ obj-$(CONFIG_INPUT_TABLET) += tablet/ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ +obj-$(CONFIG_INPUT_FINGERPRINT) += fingerprint/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o diff --git a/drivers/input/fingerprint/Kconfig b/drivers/input/fingerprint/Kconfig new file mode 100644 index 000000000000..cb763a543939 --- /dev/null +++ b/drivers/input/fingerprint/Kconfig @@ -0,0 +1,20 @@ +# +# Fingerprint driver configuration +# +menuconfig INPUT_FINGERPRINT + bool "Fingerprints" + help + Say Y here, and a list of supported fingerprints will be displayed. + This option doesn't affect the kernel. + + If unsure, say Y. + +if INPUT_FINGERPRINT + +source "drivers/input/fingerprint/fpc_tee/Kconfig" +source "drivers/input/fingerprint/fpc_fod/Kconfig" +source "drivers/input/fingerprint/goodix_ta/Kconfig" +source "drivers/input/fingerprint/goodix_fod/Kconfig" +source "drivers/input/fingerprint/fs_tee/Kconfig" + +endif diff --git a/drivers/input/fingerprint/Makefile b/drivers/input/fingerprint/Makefile new file mode 100644 index 000000000000..5f06b0169eac --- /dev/null +++ b/drivers/input/fingerprint/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the fingerprint drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_FINGERPRINT_GOODIX_TA) += goodix_ta/ +obj-$(CONFIG_FINGERPRINT_FPC_FOD) += fpc_fod/ +obj-$(CONFIG_FINGERPRINT_FPC_TEE) += fpc_tee/ +obj-$(CONFIG_FINGERPRINT_GOODIX_FOD) += goodix_fod/ +obj-$(CONFIG_FINGERPRINT_FS_TEE) += fs_tee/ diff --git a/drivers/input/fingerprint/fpc_fod/Kconfig b/drivers/input/fingerprint/fpc_fod/Kconfig new file mode 100644 index 000000000000..d8e33b7c9aa5 --- /dev/null +++ b/drivers/input/fingerprint/fpc_fod/Kconfig @@ -0,0 +1,11 @@ +# +# FPC sensors +# + +config FINGERPRINT_FPC_FOD + tristate "FPC1020 fingerprint sensor IRQ support" + depends on INPUT_FINGERPRINT + help + If you say yes here you get IRQ support for the FPC1020 family + of fingerprint sensors from Fingerprint Cards (FPC). + diff --git a/drivers/input/fingerprint/fpc_fod/Makefile b/drivers/input/fingerprint/fpc_fod/Makefile new file mode 100644 index 000000000000..e5c2dd98a69d --- /dev/null +++ b/drivers/input/fingerprint/fpc_fod/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_FPC_FOD) += fpc1020_platform_tee.o diff --git a/drivers/input/fingerprint/fpc_fod/fpc1020_platform_tee.c b/drivers/input/fingerprint/fpc_fod/fpc1020_platform_tee.c new file mode 100644 index 000000000000..e90f0ced07ac --- /dev/null +++ b/drivers/input/fingerprint/fpc_fod/fpc1020_platform_tee.c @@ -0,0 +1,1150 @@ +/* + * FPC1020 Fingerprint sensor device driver + * + * This driver will control the platform resources that the FPC fingerprint + * sensor needs to operate. The major things are probing the sensor to check + * that it is actually connected and let the Kernel know this and with that also + * enabling and disabling of regulators, controlling GPIOs such as sensor reset + * line, sensor IRQ line. + * + * The driver will expose most of its available functionality in sysfs which + * enables dynamic control of these features from eg. a user space process. + * + * The sensor's IRQ events will be pushed to Kernel's event handling system and + * are exposed in the drivers event node. + * + * This driver will NOT send any commands to the sensor it only controls the + * electrical parts. + * + * + * Copyright (c) 2015 Fingerprint Cards AB + * Copyright (C) 2021 XiaoMi, 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPC_TTW_HOLD_TIME 1000 + +#define RESET_LOW_SLEEP_MIN_US 5000 +#define RESET_LOW_SLEEP_MAX_US (RESET_LOW_SLEEP_MIN_US + 100) +#define RESET_HIGH_SLEEP1_MIN_US 100 +#define RESET_HIGH_SLEEP1_MAX_US (RESET_HIGH_SLEEP1_MIN_US + 100) +#define RESET_HIGH_SLEEP2_MIN_US 5000 +#define RESET_HIGH_SLEEP2_MAX_US (RESET_HIGH_SLEEP2_MIN_US + 100) +#define PWR_ON_SLEEP_MIN_US 100 +#define PWR_ON_SLEEP_MAX_US (PWR_ON_SLEEP_MIN_US + 900) + +//for optical +#define OPTICAL_PWR_ON_SLEEP_MIN_US 1000 +#define OPTICAL_PWR_ON_SLEEP_MAX_US (OPTICAL_PWR_ON_SLEEP_MIN_US + 100) + +#define OPTICAL_RST_SLEEP_MIN_US 1000 +#define OPTICAL_RST_SLEEP_MAX_US (OPTICAL_RST_SLEEP_MIN_US + 100) + +#define NUM_PARAMS_REG_ENABLE_SET 2 + +#define RELEASE_WAKELOCK_W_V "release_wakelock_with_verification" +#define RELEASE_WAKELOCK "release_wakelock" +#define START_IRQS_RECEIVED_CNT "start_irqs_received_counter" + +static const char * const pctl_names[] = { + "fpc1020_reset_reset", + "fpc1020_reset_active", + "fpc1020_irq_active", + "fpc1020_vdd1v2_default", + "fpc1020_vdd1v8_default", +}; + +struct vreg_config { + char *name; + unsigned long vmin; + unsigned long vmax; + int ua_load; + int gpio; +}; + +static struct vreg_config vreg_conf[] = { + { "vdd_spi", 1200000UL, 1200000UL, 6000, 0,}, + { "vdd_io", 1800000UL, 1800000UL, 6000, 0,}, + { "vdd_ana", 2800000UL, 2800000UL, 6000, 0,}, +}; + +struct fpc1020_data { + struct device *dev; + struct pinctrl *fingerprint_pinctrl; + struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)]; + struct regulator *vreg[ARRAY_SIZE(vreg_conf)]; + struct wakeup_source ttw_wl; + struct mutex lock; /* To set/get exported values in sysfs */ + int irq_gpio; + int rst_gpio; + + int vdd1v2_gpio; + int vdd1v8_gpio; + int vdd2v8_gpio; + bool gpios_requested; + + int nbr_irqs_received; + int nbr_irqs_received_counter_start; + bool prepared; +#ifdef CONFIG_FPC_COMPAT + bool compatible_enabled; +#endif + atomic_t wakeup_enabled; /* Used both in ISR and non-ISR */ +}; + +struct fpc_hotzone_setting { + /*hotzone setting: + left,right,top,bottom + */ + uint32_t left; + uint32_t right; + uint32_t top; + uint32_t bottom; + /*indicate the hotzone will reset*/ + uint32_t update; +}; +typedef struct fpc_hotzone_setting fpc_hotzone_setting_t; +static struct fpc1020_data *fpc_data_bak; +volatile long unsigned int finger_irq_value; + + +/** hotzone_value_set + *this function will set the hotzone postion in kernel driver + */ +static struct fpc_hotzone_setting hotzone_setting; + +/*define the hoztone with MACRO */ +#define TOUCHSCREEN_BOUNDARY_LOW_X hotzone_setting.left +#define TOUCHSCREEN_BOUNDARY_LOW_Y hotzone_setting.top +#define TOUCHSCREEN_BOUNDARY_HIGH_X hotzone_setting.right +#define TOUCHSCREEN_BOUNDARY_HIGH_Y hotzone_setting.bottom + +/*the pressure, size thresh*/ +#define TOUCHSCREEN_PRESSURE_THRESH 0x30 +#define TOUCHSCREEN_AREA_THRESH 0x11f + +#define HOTZONE_IN 1 +#define HOTZONE_OUT 0 +volatile long unsigned int simulate_irq_value; +static int request_vreg_gpio(struct fpc1020_data *fpc1020, bool enable); + +static int vreg_setup(struct fpc1020_data *fpc1020, const char *name, bool enable); +static int device_prepare(struct fpc1020_data *fpc1020, bool enable); +static irqreturn_t fpc1020_simulate_irq(struct device *dev); + +#ifdef CONFIG_FPC_COMPAT +static irqreturn_t fpc1020_irq_handler(int irq, void *handle); +#endif +static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, + const char *label, int *gpio); +static int hw_reset(struct fpc1020_data *fpc1020); + + +static int request_vreg_gpio(struct fpc1020_data *fpc1020, bool enable) +{ + + int rc=0; + struct device *dev = fpc1020->dev; + dev_err(dev, "fpc %s: enter!\n", __func__); + + mutex_lock(&fpc1020->lock); + + if (enable && !fpc1020->gpios_requested) { + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_vdd1v2", &fpc1020->vdd1v2_gpio); + if (rc) { + dev_err(dev, "fpc vdd1v2 gpio request failed\n"); + goto release; + } else { + vreg_conf[0].gpio = fpc1020->vdd1v2_gpio; + dev_info(dev, "fpc vdd1v2 gpio applied at %d\n", fpc1020->vdd1v2_gpio); + } + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_vdd1v8", &fpc1020->vdd1v8_gpio); + if (rc) { + dev_err(dev, "fpc vdd1v2 gpio request failed\n"); + goto release; + } else { + vreg_conf[1].gpio = fpc1020->vdd1v8_gpio; + dev_info(dev, "fpc vdd1v8 gpio applied at %d\n", fpc1020->vdd1v8_gpio); + } + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_vdd2v8", &fpc1020->vdd2v8_gpio); + if (rc) { + dev_err(dev, "fpc vdd2v8 gpio request failed\n"); + goto release; + } else { + vreg_conf[2].gpio = fpc1020->vdd2v8_gpio; + dev_info(dev, "fpc vdd2v8 gpio applied at %d\n", fpc1020->vdd2v8_gpio); + } + + fpc1020->gpios_requested = true; + dev_dbg(dev, "vreg gpio requested successfully!\n"); + goto exit; + } else if (!enable && fpc1020->gpios_requested) { +release: + if (gpio_is_valid(fpc1020->vdd1v2_gpio)) { + devm_gpio_free(dev, fpc1020->vdd1v2_gpio); + fpc1020->vdd1v2_gpio = -1; + } + + if (gpio_is_valid(fpc1020->vdd1v8_gpio)) { + devm_gpio_free(dev, fpc1020->vdd1v8_gpio); + fpc1020->vdd1v8_gpio = -1; + } + + if (gpio_is_valid(fpc1020->vdd2v8_gpio)) { + devm_gpio_free(dev, fpc1020->vdd2v8_gpio); + fpc1020->vdd2v8_gpio = -1; + } + + vreg_conf[0].gpio = 0; + vreg_conf[1].gpio = 0; + vreg_conf[2].gpio = 0; + fpc1020->gpios_requested = false; + } else { + dev_info(dev, "%s: enable: %d, gpios_requested: %d ???\n", + __func__, enable, fpc1020->gpios_requested); + } + +exit: + mutex_unlock(&fpc1020->lock); + dev_dbg(dev, "fpc %s: exit!\n", __func__); + return rc; +} + +static int vreg_setup(struct fpc1020_data *fpc1020, const char *name, + bool enable) +{ + size_t i; + int rc = 0; + int gpio = 0; + struct device *dev = fpc1020->dev; + + dev_err(dev, "fpc %s: enter!\n", __func__); + + for (i = 0; i < ARRAY_SIZE(vreg_conf); i++) { + const char *n = vreg_conf[i].name; + + if (!memcmp(n, name, strlen(n))) + goto found; + } + + dev_err(dev, "fpc %s: Regulator %s not found\n", __func__, name); + + return -EINVAL; + +found: + gpio = vreg_conf[i].gpio; + if (enable) { + if (gpio_is_valid(gpio)) { + rc = gpio_direction_output(gpio, 1); + if (rc) { + dev_err(dev, "fpc %s: fail to set gpio %d !\n", __func__, gpio); + return rc; + } + } else { + dev_err(dev, "fpc %s: unable to get gpio %d!\n", __func__, gpio); + return -EINVAL; + } + } else { + if (gpio_is_valid(gpio)) { + rc = gpio_direction_output(gpio, 0); + if (rc) { + dev_err(dev, "fpc %s: fail to clear gpio %d!\n", __func__, gpio); + return rc; + } + } else { + dev_err(dev, "fpc %s: unable to get gpio %d!\n", __func__, gpio); + return -EINVAL; + } + } + return rc; +} + + +/* + * sysfs node for release GPIO. + * + */ + +static ssize_t request_vreg_gpio_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!memcmp(buf, "enable", strlen("enable"))) + rc = request_vreg_gpio(fpc1020, true); + else if (!memcmp(buf, "disable", strlen("disable"))) + rc = request_vreg_gpio(fpc1020, false); + else + return -EINVAL; + + return rc ? rc : count; +} +static DEVICE_ATTR(request_vreg, 0200, NULL, request_vreg_gpio_set); + + + +/* + * sysfs node for controlling clocks. + * + * This is disabled in platform variant of this driver but kept for + * backwards compatibility. Only prints a debug print that it is + * disabled. + */ +static ssize_t clk_enable_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + dev_dbg(dev, "clk_enable sysfs node not enabled in platform driver\n"); + return count; +} +static DEVICE_ATTR(clk_enable, 0200, NULL, clk_enable_set); + +/* + * Will try to select the set of pins (GPIOS) defined in a pin control node of + * the device tree named @p name. + * + * The node can contain several eg. GPIOs that is controlled when selecting it. + * The node may activate or deactivate the pins it contains, the action is + * defined in the device tree node itself and not here. The states used + * internally is fetched at probe time. + * + * @see pctl_names + * @see fpc1020_probe + */ +static int select_pin_ctl(struct fpc1020_data *fpc1020, const char *name) +{ + size_t i; + int rc; + struct device *dev = fpc1020->dev; + + for (i = 0; i < ARRAY_SIZE(pctl_names); i++) { + const char *n = pctl_names[i]; + + if (!memcmp(n, name, strlen(n))) { + rc = pinctrl_select_state(fpc1020->fingerprint_pinctrl, + fpc1020->pinctrl_state[i]); + if (rc) + dev_err(dev, "fpc %s: cannot select '%s'\n", __func__, name); + else + dev_dbg(dev, "fpc %s: selected '%s'\n", __func__, name); + goto exit; + } + } + + rc = -EINVAL; + dev_err(dev, "%s:'%s' not found gpio\n", __func__, name); + +exit: + return rc; +} + +static ssize_t pinctl_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + int rc; + + mutex_lock(&fpc1020->lock); + rc = select_pin_ctl(fpc1020, buf); + mutex_unlock(&fpc1020->lock); + + return rc ? rc : count; +} +static DEVICE_ATTR(pinctl_set, 0200, NULL, pinctl_set); + +static ssize_t regulator_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + char op; + char name[16]; + int rc; + bool enable; + + if (sscanf(buf, "%15[^,],%c", name, &op) != NUM_PARAMS_REG_ENABLE_SET) + return -EINVAL; + if (op == 'e') + enable = true; + else if (op == 'd') + enable = false; + else + return -EINVAL; + + mutex_lock(&fpc1020->lock); + rc = vreg_setup(fpc1020, name, enable); + mutex_unlock(&fpc1020->lock); + + return rc ? rc : count; +} +static DEVICE_ATTR(regulator_enable, 0200, NULL, regulator_enable_set); + +static int hw_reset(struct fpc1020_data *fpc1020) +{ + //int irq_gpio; + int rc; + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_active"); + if (rc) + goto exit; + + usleep_range(OPTICAL_RST_SLEEP_MIN_US, OPTICAL_PWR_ON_SLEEP_MAX_US); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + if (rc) + goto exit; + + usleep_range(OPTICAL_RST_SLEEP_MIN_US, OPTICAL_PWR_ON_SLEEP_MAX_US); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_active"); + if (rc) + goto exit; + + usleep_range(OPTICAL_RST_SLEEP_MIN_US, OPTICAL_PWR_ON_SLEEP_MAX_US); + //irq_gpio = gpio_get_value(fpc1020->irq_gpio); + + //dev_info("fpc1020.irq_gpio is %d\n", fpc1020->irq_gpio); + //dev_info("fpc IRQ after reset %d\n", irq_gpio); + +exit: + return rc; +} + +static ssize_t hw_reset_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc = -EINVAL; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!memcmp(buf, "reset", strlen("reset"))) { + mutex_lock(&fpc1020->lock); + rc = hw_reset(fpc1020); + mutex_unlock(&fpc1020->lock); + } else { + return rc; + } + return rc ? rc : count; +} +static DEVICE_ATTR(hw_reset, 0200, NULL, hw_reset_set); + +/* + * Will setup GPIOs, and regulators to correctly initialize the touch sensor to + * be ready for work. + * + * In the correct order according to the sensor spec this function will + * enable/disable regulators, and reset line, all to set the sensor in a + * correct power on or off state "electrical" wise. + * + * @see device_prepare_set + * @note This function will not send any commands to the sensor it will only + * control it "electrically". + */ +static int device_prepare(struct fpc1020_data *fpc1020, bool enable) +{ + int rc = 0; + struct device *dev = fpc1020->dev; + + mutex_lock(&fpc1020->lock); + + dev_err(dev, "fpc device prepare enter.\n"); + + if (enable && !fpc1020->prepared) { + fpc1020->prepared = true; + select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + + //1.2V + rc = vreg_setup(fpc1020, "vdd_spi", true); + if (rc) + goto exit; + + usleep_range(OPTICAL_PWR_ON_SLEEP_MIN_US, OPTICAL_PWR_ON_SLEEP_MAX_US); + + //1.8V + rc = vreg_setup(fpc1020, "vdd_io", true); + if (rc) + goto exit_1; + + usleep_range(OPTICAL_PWR_ON_SLEEP_MIN_US, OPTICAL_PWR_ON_SLEEP_MAX_US); + + //2.8V + rc = vreg_setup(fpc1020, "vdd_ana", true); + if (rc) + goto exit_2; + + usleep_range(OPTICAL_PWR_ON_SLEEP_MIN_US * 2, OPTICAL_PWR_ON_SLEEP_MAX_US * 2); + + /* As we can't control chip select here the other part of the + * sensor driver eg. the TEE driver needs to do a _SOFT_ reset + * on the sensor after power up to be sure that the sensor is + * in a good state after power up. Okeyed by ASIC. */ + + (void)select_pin_ctl(fpc1020, "fpc1020_reset_active"); + } else if (!enable && fpc1020->prepared) { + rc = 0; + (void)select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + + usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US); + + (void)vreg_setup(fpc1020, "vdd_ana", false); +exit_2: + (void)vreg_setup(fpc1020, "vdd_io", false); +exit_1: + (void)vreg_setup(fpc1020, "vdd_spi", false); +exit: + fpc1020->prepared = false; + } + + mutex_unlock(&fpc1020->lock); + + return rc; +} + +/* + * sysfs node to enable/disable (power up/power down) the touch sensor + * + * @see device_prepare + */ +static ssize_t device_prepare_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!memcmp(buf, "enable", strlen("enable"))) + rc = device_prepare(fpc1020, true); + else if (!memcmp(buf, "disable", strlen("disable"))) + rc = device_prepare(fpc1020, false); + else + return -EINVAL; + + return rc ? rc : count; +} +static DEVICE_ATTR(device_prepare, 0200, NULL, device_prepare_set); + +/** + * sysfs node for controlling whether the driver is allowed + * to wake up the platform on interrupt. + */ +static ssize_t wakeup_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + ssize_t ret = count; + + mutex_lock(&fpc1020->lock); + if (!memcmp(buf, "enable", strlen("enable"))) + atomic_set(&fpc1020->wakeup_enabled, 1); + else if (!memcmp(buf, "disable", strlen("disable"))) + atomic_set(&fpc1020->wakeup_enabled, 0); + else + ret = -EINVAL; + mutex_unlock(&fpc1020->lock); + + return ret; +} +static DEVICE_ATTR(wakeup_enable, 0200, NULL, wakeup_enable_set); + + +/* + * sysfs node for controlling the wakelock. + */ +static ssize_t handle_wakelock_cmd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + ssize_t ret = count; + + mutex_lock(&fpc1020->lock); + if (!memcmp(buf, RELEASE_WAKELOCK_W_V, + min(count, strlen(RELEASE_WAKELOCK_W_V)))) { + if (fpc1020->nbr_irqs_received_counter_start == + fpc1020->nbr_irqs_received) { + __pm_relax(&fpc1020->ttw_wl); + } else { + dev_dbg(dev, "Ignore releasing of wakelock %d != %d", + fpc1020->nbr_irqs_received_counter_start, + fpc1020->nbr_irqs_received); + } + } else if (!memcmp(buf, RELEASE_WAKELOCK, min(count, + strlen(RELEASE_WAKELOCK)))) { + __pm_relax(&fpc1020->ttw_wl); + } else if (!memcmp(buf, START_IRQS_RECEIVED_CNT, + min(count, strlen(START_IRQS_RECEIVED_CNT)))) { + fpc1020->nbr_irqs_received_counter_start = + fpc1020->nbr_irqs_received; + } else + ret = -EINVAL; + mutex_unlock(&fpc1020->lock); + + return ret; +} +static DEVICE_ATTR(handle_wakelock, 0200, NULL, handle_wakelock_cmd); + +/* + * sysf node to check the interrupt status of the sensor, the interrupt + * handler should perform sysf_notify to allow userland to poll the node. + */ +static ssize_t irq_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + int irq = gpio_get_value(fpc1020->irq_gpio); + + return scnprintf(buf, PAGE_SIZE, "%i\n", irq); +} + +/* + * writing to the irq node will just drop a printk message + * and return success, used for latency measurement. + */ +static ssize_t irq_ack(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + dev_dbg(fpc1020->dev, "%s\n", __func__); + + return count; +} +static DEVICE_ATTR(irq, 0600 | 0200, irq_get, irq_ack); + +/** + * sysf node to check the interrupt status of the sensor, the interrupt + * handler should perform sysf_notify to allow userland to poll the node. + */ +static ssize_t simulate_irq_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int irq = simulate_irq_value; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + dev_info(fpc1020->dev, "%s irq = %d\n", __func__, irq); + clear_bit(0, &simulate_irq_value); + + return scnprintf(buf, PAGE_SIZE, "%i\n", irq); +} + +/* + * writing to the irq node will just drop a printk message + * and return success, used for latency measurement. + */ +static ssize_t simulate_irq_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + dev_info(fpc1020->dev, "%s, buf = %s\n", __func__, buf); + + set_bit(0, &simulate_irq_value); + fpc1020_simulate_irq(dev); + + return count; +} +static DEVICE_ATTR(simulate_irq, S_IRUSR | S_IWUSR, simulate_irq_get, simulate_irq_set); + +#ifdef CONFIG_FPC_COMPAT +static ssize_t compatible_all_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + int i; + int irqf; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + dev_err(dev, "compatible all enter %d\n", fpc1020->compatible_enabled); + if (!strncmp (buf, "enable", strlen ("enable")) && fpc1020->compatible_enabled != 1) { + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_irq", + &fpc1020->irq_gpio); + if (rc) + goto exit; + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_rst", + &fpc1020->rst_gpio); + dev_err(dev, "fpc request reset result = %d\n", rc); + if (rc) + goto exit; + fpc1020->fingerprint_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(fpc1020->fingerprint_pinctrl)) { + if (PTR_ERR(fpc1020->fingerprint_pinctrl) == -EPROBE_DEFER) { + dev_info(dev, "pinctrl not ready\n"); + rc = -EPROBE_DEFER; + goto exit; + } + dev_err(dev, "Target does not use pinctrl\n"); + fpc1020->fingerprint_pinctrl = NULL; + rc = -EINVAL; + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(fpc1020->pinctrl_state); i++) { + const char *n = pctl_names[i]; + struct pinctrl_state *state = + pinctrl_lookup_state(fpc1020->fingerprint_pinctrl, n); + if (IS_ERR(state)) { + dev_err(dev, "cannot find '%s'\n", n); + rc = -EINVAL; + goto exit; + } + dev_info(dev, "found pin control %s\n", n); + fpc1020->pinctrl_state[i] = state; + } + rc = select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + if (rc) + goto exit; + rc = select_pin_ctl(fpc1020, "fpc1020_irq_active"); + if (rc) + goto exit; + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + if (of_property_read_bool(dev->of_node, "fpc,enable-wakeup")) { + irqf |= IRQF_NO_SUSPEND; + device_init_wakeup(dev, 1); + } + rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio), + NULL, fpc1020_irq_handler, irqf, + dev_name(dev), fpc1020); + if (rc) { + dev_err(dev, "could not request irq %d\n", + gpio_to_irq(fpc1020->irq_gpio)); + goto exit; + } + dev_dbg(dev, "requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio)); + + /* Request that the interrupt should be wakeable */ + enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio)); + fpc1020->compatible_enabled = 1; + if (of_property_read_bool(dev->of_node, "fpc,enable-on-boot")) { + dev_info(dev, "Enabling hardware\n"); + (void)device_prepare(fpc1020, true); + } + } else if (!strncmp (buf, "disable", strlen ("disable")) && fpc1020->compatible_enabled != 0) { + if (gpio_is_valid(fpc1020->irq_gpio)) { + devm_gpio_free(dev, fpc1020->irq_gpio); + dev_info(dev, "remove irq_gpio success\n"); + } + if (gpio_is_valid(fpc1020->rst_gpio)) { + devm_gpio_free(dev, fpc1020->rst_gpio); + dev_info(dev, "remove rst_gpio success\n"); + } + devm_free_irq(dev, gpio_to_irq(fpc1020->irq_gpio), fpc1020); + fpc1020->compatible_enabled = 0; + } + hw_reset(fpc1020); + return count; +exit: + return -EINVAL; +} +static DEVICE_ATTR(compatible_all, S_IWUSR, NULL, compatible_all_set); +#endif + +static ssize_t hotzone_value_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "hotzone: wakeup_enable:%d,left:%d,right:%d,top:%d,bottom:%d,update:%d\n", + atomic_read(&fpc_data_bak->wakeup_enabled), + hotzone_setting.left, + hotzone_setting.right, + hotzone_setting.top, + hotzone_setting.bottom, + hotzone_setting.update); +} + +static ssize_t hotzone_value_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpc1020_data *fpc = dev_get_drvdata(dev); + + memcpy(&hotzone_setting, (uint8_t *)buf, count); + dev_info(fpc->dev, "%s, hotzone:left:%d, right:%d, top:%d, bottom:%d, update:%d\r\n", __func__, + hotzone_setting.left, + hotzone_setting.right, + hotzone_setting.top, + hotzone_setting.bottom, + hotzone_setting.update); + return count; +} +static DEVICE_ATTR(hotzone_config, S_IRUSR | S_IWUSR, hotzone_value_get, hotzone_value_set); + +/* finger_irq_flag + * this flag will indicate the finger not in the hotzone + * 0 --finger not int hotzone( finger_up) (as the default value) + * 1--finger in the hotzone (finger_down) + * the return value, following with Hal define + */ + +void fpc_hotzone_finger_irq_set(uint8_t flag) +{ + finger_irq_value = flag; +} + +uint8_t fpc_hotzone_finger_irq_get(void) +{ + //printk("%s, finger_irq_flag:%d", __func__, finger_irq_flag); + return finger_irq_value; +} + +static ssize_t fpc_finger_irq_status_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + uint8_t status = fpc_hotzone_finger_irq_get(); + dev_info(dev, "%s status= %d\r\n", __func__, status); + + return scnprintf(buf, PAGE_SIZE, "%i\n", status); + +} +static DEVICE_ATTR(finger_irq, S_IRUSR, fpc_finger_irq_status_get, NULL); + +/*report finger detect irq to fingerprints HAL layer*/ +int fpc_finger_irq_handler(uint8_t value) +{ + printk("%s fingerprints hotzone finger irq\r\n", __func__); + fpc_hotzone_finger_irq_set(value); + sysfs_notify(&fpc_data_bak->dev->kobj, NULL, "finger_irq"); + return 0; + +} +EXPORT_SYMBOL(fpc_finger_irq_handler); + +/*this function will detect the touch position whether in the hotzone +*/ +int fpc_hotzone_in(int x, int y, int p, int major) +{ + /*first check the wakeup irq status, + * if true, finger detect irq to fingerprints HAL layer + */ + if (atomic_read(&fpc_data_bak->wakeup_enabled) != true) { + //printk("%s wakeup disabled\r\n", __func__); + return 0; + } + + /*judge the touch event*/ + if ((x >= TOUCHSCREEN_BOUNDARY_LOW_X) && (x <= TOUCHSCREEN_BOUNDARY_HIGH_X) && + (y >= TOUCHSCREEN_BOUNDARY_LOW_Y) && (y <= TOUCHSCREEN_BOUNDARY_HIGH_Y) && + (p >= TOUCHSCREEN_PRESSURE_THRESH) && (major >= TOUCHSCREEN_AREA_THRESH)) { + /*ignore the pressue and area, some touch screen not support*/ + //if (p >= TOUCHSCREEN_PRESSURE_THRESH && major >= TOUCHSCREEN_AREA_THRESH) + fpc_finger_irq_handler(HOTZONE_IN); + ///printk("%s shot the hotzone\r\n", __func__); + return 1; + } + + //printk("%s miss the hotzone\r\n", __func__); + return 0; +} +EXPORT_SYMBOL(fpc_hotzone_in); + +int fpc_hotzone_out(void) +{ + fpc_finger_irq_handler(HOTZONE_OUT); + return 0; +} +EXPORT_SYMBOL(fpc_hotzone_out); + + +/* + * this function used for report the hotzone touch event + * @paramter input: + * 1--finger on in the hotzone + * 0--finger up form the hotzone + * this function called in the touch driver when report hotzone event + * (with xiaomi design, when send event.keycode=0x152, call this function + * the status = key_value(1 or 0)) + */ +int fpc_hotzone_event_report(int status) +{ + if (status == 1) { + /* + * only when the sensor wake up (that's means need capture images form sensor) + * should reprot finger down event. + */ + if (atomic_read(&fpc_data_bak->wakeup_enabled) != true) { + return 0; + } + + fpc_finger_irq_handler(HOTZONE_IN); + } else { + fpc_finger_irq_handler(HOTZONE_OUT); + } + return 0; +} +EXPORT_SYMBOL(fpc_hotzone_event_report); +static struct attribute *attributes[] = { + &dev_attr_request_vreg.attr, + &dev_attr_pinctl_set.attr, + &dev_attr_device_prepare.attr, + &dev_attr_regulator_enable.attr, + &dev_attr_hw_reset.attr, + &dev_attr_wakeup_enable.attr, + &dev_attr_handle_wakelock.attr, + &dev_attr_clk_enable.attr, + &dev_attr_irq.attr, + &dev_attr_simulate_irq.attr, +#ifdef CONFIG_FPC_COMPAT + &dev_attr_compatible_all.attr, +#endif + &dev_attr_finger_irq.attr, + &dev_attr_hotzone_config.attr, + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +static irqreturn_t fpc1020_simulate_irq(struct device *dev) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_simulate_irq.attr.name); + + return IRQ_HANDLED; +} +#ifdef CONFIG_FPC_COMPAT +static irqreturn_t fpc1020_irq_handler(int irq, void *handle) +{ + struct fpc1020_data *fpc1020 = handle; + + dev_dbg(fpc1020->dev, "%s\n", __func__); + mutex_lock(&fpc1020->lock); + if (atomic_read(&fpc1020->wakeup_enabled)) { + fpc1020->nbr_irqs_received++; + __pm_wakeup_event(&fpc1020->ttw_wl, + msecs_to_jiffies(FPC_TTW_HOLD_TIME)); + } + mutex_unlock(&fpc1020->lock); + + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); + + return IRQ_HANDLED; +} +#endif +static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, + const char *label, int *gpio) +{ + struct device *dev = fpc1020->dev; + struct device_node *np = dev->of_node; + int rc; + + rc = of_get_named_gpio(np, label, 0); + + if (rc < 0) { + dev_err(dev, "failed to get '%s'\n", label); + return rc; + } + *gpio = rc; + + rc = devm_gpio_request(dev, *gpio, label); + if (rc) { + dev_err(dev, "failed to request gpio %d\n", *gpio); + return rc; + } + dev_dbg(dev, "%s %d\n", label, *gpio); + + return 0; +} + +static int fpc1020_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc = 0; +#ifndef CONFIG_FPC_COMPAT + size_t i; + int irqf; +#endif + + struct device_node *np = dev->of_node; + struct fpc1020_data *fpc1020 = devm_kzalloc(dev, sizeof(*fpc1020), + GFP_KERNEL); + + dev_info(dev, "fpc probe start! \n"); + + if (!fpc1020) { + dev_err(dev, + "failed to allocate memory for struct fpc1020_data\n"); + rc = -ENOMEM; + goto exit; + } + + fpc1020->dev = dev; + fpc1020->vdd1v2_gpio = -1; + fpc1020->vdd1v8_gpio = -1; + fpc1020->vdd2v8_gpio = -1; + fpc1020->gpios_requested = false; + platform_set_drvdata(pdev, fpc1020); + + if (!np) { + dev_err(dev, "fpc %s: no of node found\n", __func__); + rc = -EINVAL; + goto exit; + } +#ifndef CONFIG_FPC_COMPAT + + fpc1020->fingerprint_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(fpc1020->fingerprint_pinctrl)) { + if (PTR_ERR(fpc1020->fingerprint_pinctrl) == -EPROBE_DEFER) { + dev_info(dev, "pinctrl not ready\n"); + rc = -EPROBE_DEFER; + goto exit; + } + dev_err(dev, "Target does not use pinctrl\n"); + fpc1020->fingerprint_pinctrl = NULL; + rc = -EINVAL; + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(pctl_names); i++) { + const char *n = pctl_names[i]; + struct pinctrl_state *state = + pinctrl_lookup_state(fpc1020->fingerprint_pinctrl, n); + if (IS_ERR(state)) { + dev_err(dev, "cannot find '%s'\n", n); + rc = -EINVAL; + goto exit; + } + dev_info(dev, "found pin control %s\n", n); + fpc1020->pinctrl_state[i] = state; + } + + + atomic_set(&fpc1020->wakeup_enabled, 0); + + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + if (of_property_read_bool(dev->of_node, "fpc,enable-wakeup")) { + irqf |= IRQF_NO_SUSPEND; + device_init_wakeup(dev, 1); + } + + mutex_init(&fpc1020->lock); + + rc = sysfs_create_group(&dev->kobj, &attribute_group); + if (rc) { + dev_err(dev, "fpc %s: could not create sysfs!\n", __func__); + goto exit; + } + + if (of_property_read_bool(dev->of_node, "fpc,enable-on-boot")) { + dev_info(dev, "fpc Enabling hardware\n"); + (void)device_prepare(fpc1020, true); + + fpc_data_bak = fpc1020; + rc = hw_reset(fpc1020); + if (rc) { + dev_err(dev, "fpc hardware reset failed\n"); + goto exit; + } + } +#else + mutex_init(&fpc1020->lock); + wake_lock_init(&fpc1020->ttw_wl, WAKE_LOCK_SUSPEND, "fpc_ttw_wl"); + + rc = sysfs_create_group(&dev->kobj, &attribute_group); + if (rc) { + dev_err(dev, "could not create sysfs\n"); + goto exit; + } + + mutex_lock(&fpc1020->lock); + /*initial power control gpio pin */ + rc = select_pin_ctl(fpc1020, "fpc1020_vdd1v2_default"); + if (rc) { + dev_err(dev, "fpc initial power 1v2 gpio failed!\n"); + goto exit; + } + + rc = select_pin_ctl(fpc1020, "fpc1020_vdd1v8_default"); + if (rc) { + dev_err(dev, "fpc initial power 1v8 gpio failed!\n"); + goto exit; + } + + mutex_unlock(&fpc1020->lock); + +#endif + dev_info(dev, "fpc %s: ok\n", __func__); + return rc; + +exit: + dev_err(dev, "fpc %s: failed!\n", __func__); + return rc; +} + +static int fpc1020_remove(struct platform_device *pdev) +{ + struct fpc1020_data *fpc1020 = platform_get_drvdata(pdev); + + sysfs_remove_group(&pdev->dev.kobj, &attribute_group); + mutex_destroy(&fpc1020->lock); + wakeup_source_trash(&fpc1020->ttw_wl); + (void)vreg_setup(fpc1020, "vdd_ana", false); + (void)vreg_setup(fpc1020, "vdd_io", false); + (void)vreg_setup(fpc1020, "vdd_spi", false); + dev_info(&pdev->dev, "%s\n", __func__); + + return 0; +} + +static struct of_device_id fpc1020_of_match[] = { + { .compatible = "fpc,fpc1020", }, + {} +}; +MODULE_DEVICE_TABLE(of, fpc1020_of_match); + +static struct platform_driver fpc1020_driver = { + .driver = { + .name = "fpc1020", + .owner = THIS_MODULE, + .of_match_table = fpc1020_of_match, + }, + .probe = fpc1020_probe, + .remove = fpc1020_remove, +}; + +static int __init fpc1020_init(void) +{ + int rc = platform_driver_register(&fpc1020_driver); + + if (!rc) + pr_info("%s OK\n", __func__); + else + pr_err("%s %d\n", __func__, rc); + + return rc; +} + +static void __exit fpc1020_exit(void) +{ + pr_info("%s\n", __func__); + platform_driver_unregister(&fpc1020_driver); +} + +module_init(fpc1020_init); +module_exit(fpc1020_exit); + + +MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/fingerprint/fpc_tee/Kconfig b/drivers/input/fingerprint/fpc_tee/Kconfig new file mode 100644 index 000000000000..e2cf8f94c21e --- /dev/null +++ b/drivers/input/fingerprint/fpc_tee/Kconfig @@ -0,0 +1,10 @@ +config FINGERPRINT_FPC_TEE + tristate "Finger print card fpc" + depends on INPUT_FINGERPRINT + help + Say Y here to enable support for retrieving self-test reports. + + If unsure, say N. + + To compile this driver as a module, choose M here. + diff --git a/drivers/input/fingerprint/fpc_tee/Makefile b/drivers/input/fingerprint/fpc_tee/Makefile new file mode 100644 index 000000000000..ac8f1bdeb071 --- /dev/null +++ b/drivers/input/fingerprint/fpc_tee/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FINGERPRINT_FPC_TEE) += fpc1020_tee.o diff --git a/drivers/input/fingerprint/fpc_tee/fpc1020_tee.c b/drivers/input/fingerprint/fpc_tee/fpc1020_tee.c new file mode 100644 index 000000000000..4ec0608771c2 --- /dev/null +++ b/drivers/input/fingerprint/fpc_tee/fpc1020_tee.c @@ -0,0 +1,1037 @@ +/* + * FPC1020 Fingerprint sensor device driver + * + * This driver will control the platform resources that the FPC fingerprint + * sensor needs to operate. The major things are probing the sensor to check + * that it is actually connected and let the Kernel know this and with that also + * enabling and disabling of regulators, controlling GPIOs such as sensor reset + * line, sensor IRQ line. + * + * The driver will expose most of its available functionality in sysfs which + * enables dynamic control of these features from eg. a user space process. + * + * The sensor's IRQ events will be pushed to Kernel's event handling system and + * are exposed in the drivers event node. + * + * This driver will NOT send any commands to the sensor it only controls the + * electrical parts. + * + * + * Copyright (c) 2015 Fingerprint Cards AB + * Copyright (C) 2021 XiaoMi, 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. + */ +/* +#define FPC_DRM_INTERFACE_WA +*/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FPC_GPIO_NO_DEFAULT -1 +#define FPC_TTW_HOLD_TIME 2000 +#define FP_UNLOCK_REJECTION_TIMEOUT (FPC_TTW_HOLD_TIME - 500) + +#define RESET_LOW_SLEEP_MIN_US 5000 +#define RESET_LOW_SLEEP_MAX_US (RESET_LOW_SLEEP_MIN_US + 100) +#define RESET_HIGH_SLEEP1_MIN_US 100 +#define RESET_HIGH_SLEEP1_MAX_US (RESET_HIGH_SLEEP1_MIN_US + 100) +#define RESET_HIGH_SLEEP2_MIN_US 5000 +#define RESET_HIGH_SLEEP2_MAX_US (RESET_HIGH_SLEEP2_MIN_US + 100) +#define PWR_ON_SLEEP_MIN_US 100 +#define PWR_ON_SLEEP_MAX_US (PWR_ON_SLEEP_MIN_US + 900) + +#define NUM_PARAMS_REG_ENABLE_SET 2 + +#define RELEASE_WAKELOCK_W_V "release_wakelock_with_verification" +#define RELEASE_WAKELOCK "release_wakelock" +#define START_IRQS_RECEIVED_CNT "start_irqs_received_counter" + +#define HWMON_CONPONENT_NAME "fingerprint" + +static const char * const pctl_names[] = { + "fpc1020_reset_reset", + "fpc1020_reset_active", +}; + +struct vreg_config { + char *name; + unsigned long vmin; + unsigned long vmax; + int ua_load; + int gpio; +}; + +static struct vreg_config vreg_conf[] = { + { "vdd_ana", 1800000UL, 1800000UL, 6000, FPC_GPIO_NO_DEFAULT}, + /*{ "vcc_spi", 1800000UL, 1800000UL, 10, },*/ + /*{ "vdd_io", 1800000UL, 1800000UL, 6000, },*/ +}; + +struct fpc1020_data { + struct device *dev; + + struct pinctrl *fingerprint_pinctrl; + struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)]; + //struct regulator *vreg[ARRAY_SIZE(vreg_conf)]; + + struct wakeup_source ttw_wl; + struct wakeup_source screen_wl; + int irq_gpio; + int rst_gpio; + + /*GPIO for voltage control*/ + int vdd1v8_gpio; + + int nbr_irqs_received; + int nbr_irqs_received_counter_start; + + struct mutex lock; /* To set/get exported values in sysfs */ + bool prepared; + bool irq_requested; + + atomic_t wakeup_enabled; /* Used both in ISR and non-ISR */ + int irqf; + struct notifier_block fb_notifier; + bool fb_black; + bool wait_finger_down; + struct work_struct work; +}; + + +static int reset_gpio_res(struct fpc1020_data *fpc1020); + +/* + * request/release GPIO for voltage control. + * + */ +static int request_vreg_gpio(struct fpc1020_data *fpc1020, bool enable); +static int irq_setup(struct fpc1020_data * fpc1020, bool enable); +static int vreg_setup(struct fpc1020_data *fpc1020, const char *name, bool enable); +static int device_prepare(struct fpc1020_data *fpc1020, bool enable); +static irqreturn_t fpc1020_irq_handler(int irq, void *handle); +static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, + const char *label, int *gpio); +static int hw_reset(struct fpc1020_data *fpc1020); + + +static int reset_gpio_res(struct fpc1020_data *fpc1020) +{ + int rc = 0; + struct device * dev; + + if(!fpc1020){ + rc = -ENOMEM; + printk("fpc %s: failed to allocate memory for struct fpc1020_data!\n", __func__); + return rc; + } + + dev = fpc1020->dev; + dev_info(dev, "fpc %s --->: enter!\n", __func__); + + fpc1020->vdd1v8_gpio = FPC_GPIO_NO_DEFAULT; + fpc1020->irq_gpio = FPC_GPIO_NO_DEFAULT; + dev_info(dev, "fpc %s <---: exit!\n", __func__); + + return rc; +} + + +static int request_vreg_gpio(struct fpc1020_data *fpc1020, bool enable) +{ + + int rc; + struct device *dev = fpc1020->dev; + dev_info(dev, "fpc %s --->: enter!\n", __func__); + + mutex_lock(&fpc1020->lock); + + /* if gpio has been occupied, release */ + if (gpio_is_valid(fpc1020->vdd1v8_gpio)){ + + dev_info(dev, "fpc vdd1v8 gpio is occupied at %d, release first!\n", fpc1020->vdd1v8_gpio); + devm_gpio_free(dev, fpc1020->vdd1v8_gpio); + } + + if(gpio_is_valid(fpc1020->irq_gpio)){ + + dev_info(dev, "fpc irq gpio is occupied at %d, release first!\n", fpc1020->irq_gpio); + devm_gpio_free(dev, fpc1020->irq_gpio); + } + + if (enable) { + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_vdd1v8", &fpc1020->vdd1v8_gpio); + if (rc) { + dev_err(dev, "fpc vdd1v8 gpio request failed!\n"); + goto release_vreg_gpio; + } else { + vreg_conf[0].gpio = fpc1020->vdd1v8_gpio; + dev_info(dev, "fpc vdd1v8 gpio applied at %d\n", fpc1020->vdd1v8_gpio); + } + + dev_info(dev, "fpc vreg gpio requested successfully!\n"); + + rc = fpc1020_request_named_gpio(fpc1020, "fpc,gpio_irq", + &fpc1020->irq_gpio); + if (rc) { + pr_err("fpc irq gpio request failed!\n"); + goto release_irq_gpio; + } + else{ + dev_info(dev, "fpc irq gpio applied at %d\n", fpc1020->irq_gpio); + } + dev_info(dev, "fpc irq gpio requested successfully!\n"); + + + if(fpc1020->irq_requested){ + devm_free_irq(dev, gpio_to_irq(fpc1020->irq_gpio), fpc1020); + fpc1020->irq_requested = false; + dev_info(dev, "fpc irq has been requested already, free firstly!\n"); + } + + rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio), + NULL, fpc1020_irq_handler, fpc1020->irqf, + dev_name(dev), fpc1020); + + if (rc) { + pr_err("fpc could not request irq %d\n", + gpio_to_irq(fpc1020->irq_gpio)); + + goto release_irq_gpio; + } + + fpc1020->irq_requested = true; + dev_info(dev, "fpc requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio)); + + goto exit; + } else { + devm_free_irq(dev, gpio_to_irq(fpc1020->irq_gpio), fpc1020); + dev_info(dev, "fpc irq free successfully!\n"); + fpc1020->irq_requested = false; +release_irq_gpio: + + if(gpio_is_valid(fpc1020->irq_gpio)){ + devm_gpio_free(dev, fpc1020->irq_gpio); + dev_info(dev, "fpc irq gpio released successfully!\n"); + } +release_vreg_gpio: + if (gpio_is_valid(fpc1020->vdd1v8_gpio)) + devm_gpio_free(dev, fpc1020->vdd1v8_gpio); + vreg_conf[0].gpio = FPC_GPIO_NO_DEFAULT; + dev_info(dev, "fpc vreg gpio released successfully!\n"); + } + +exit: mutex_unlock(&fpc1020->lock); + dev_info(dev, "fpc %s <---: exit!\n", __func__); + return rc; +} + +static int irq_setup(struct fpc1020_data *fpc1020,bool enable){ + + int rc = 0; + + struct device *dev = fpc1020->dev; + if(enable){ + dev_info(dev, "fpc %s --->: enter, for enable irq!\n", __func__); + }else{ + dev_info(dev, "fpc %s --->: enter, for disable irq!\n", __func__); + } + + if (gpio_is_valid(fpc1020->irq_gpio)){ + + dev_info(dev, "fpc %s irq_gpio is validate!\n", __func__); + + if(enable){ + enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio)); + dev_info(dev, "fpc irq_gpio is enable. \n"); + }else{ + disable_irq(gpio_to_irq(fpc1020->irq_gpio)); + dev_info(dev, "fpc irq_gpio is disable. \n"); + } + + } + else{ + dev_info(dev, "fpc %s irq_gpio is invalidate!\n", __func__); + rc = -EINVAL; + } + dev_info(dev, "fpc %s <---: exit!\n", __func__); + return rc; +} + +static int vreg_setup(struct fpc1020_data *fpc1020, const char *name, + bool enable) +{ + size_t i; + int rc = 0; + int gpio = 0; + struct device *dev = fpc1020->dev; + + if(enable){ + dev_info(dev, "fpc %s --->: enter, for power on at %s.\n", __func__, name); + }else{ + dev_info(dev, "fpc %s --->: enter, for power off at %s.\n", __func__, name); + } + + for (i = 0; i < ARRAY_SIZE(vreg_conf); i++) { + const char *n = vreg_conf[i].name; + + if (!memcmp(n, name, strlen(n))) + goto found; + } + + dev_err(dev, "fpc %s: Regulator %s not found\n", __func__, name); + + return -EINVAL; + +found: + gpio = vreg_conf[i].gpio; + if (enable) { + if (gpio_is_valid(gpio)) { + rc = gpio_direction_output(gpio, 1); + if (rc) { + dev_err(dev, "fpc %s: fail to set gpio %d !\n", __func__, gpio); + return rc; + } + } else { + dev_err(dev, "fpc %s: unable to get gpio %d!\n", __func__, gpio); + return -EINVAL; + } + } else { + if (gpio_is_valid(gpio)) { + rc = gpio_direction_output(gpio, 0); + if (rc) { + dev_err(dev, "fpc %s: fail to clear gpio %d!\n", __func__, gpio); + return rc; + } + } else { + dev_err(dev, "fpc %s: unable to get gpio %d!\n", __func__, gpio); + return -EINVAL; + } + } + + dev_info(dev, "fpc %s <---: exit!\n", __func__); + return rc; +} + + +/* + * sysfs node for release GPIO. + * + */ + +static ssize_t request_vreg_gpio_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!memcmp(buf, "enable", strlen("enable"))) + rc = request_vreg_gpio(fpc1020, true); + else if (!memcmp(buf, "disable", strlen("disable"))) + rc = request_vreg_gpio(fpc1020, false); + else + return -EINVAL; + + return rc ? rc : count; +} +static DEVICE_ATTR(request_vreg, S_IWUSR, NULL, request_vreg_gpio_set); + + + +/* + * sysfs node for controlling clocks. + * + * This is disabled in platform variant of this driver but kept for + * backwards compatibility. Only prints a debug print that it is + * disabled. + */ +static ssize_t clk_enable_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + dev_dbg(dev, + "clk_enable sysfs node not enabled in platform driver\n"); + + return count; +} +static DEVICE_ATTR(clk_enable, S_IWUSR, NULL, clk_enable_set); + +/** + * Will try to select the set of pins (GPIOS) defined in a pin control node of + * the device tree named @p name. + * + * The node can contain several eg. GPIOs that is controlled when selecting it. + * The node may activate or deactivate the pins it contains, the action is + * defined in the device tree node itself and not here. The states used + * internally is fetched at probe time. + * + * @see pctl_names + * @see fpc1020_probe + */ +static int select_pin_ctl(struct fpc1020_data *fpc1020, const char *name) +{ + size_t i; + int rc; + struct device *dev = fpc1020->dev; + dev_dbg(dev, "fpc %s --->: enter! \n", __func__); + + for (i = 0; i < ARRAY_SIZE(pctl_names); i++) { + const char *n = pctl_names[i]; + + if (!memcmp(n, name, strlen(n))) { + rc = pinctrl_select_state(fpc1020->fingerprint_pinctrl, + fpc1020->pinctrl_state[i]); + if (rc) + dev_err(dev, "fpc %s: cannot select '%s'\n", __func__, name); + else + dev_dbg(dev, "fpc %s: selected '%s'\n", __func__, name); + goto exit; + } + } + + rc = -EINVAL; + dev_err(dev, "%s:'%s' not found gpio\n", __func__, name); + +exit: + return rc; +} + +static ssize_t pinctl_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + int rc; + + mutex_lock(&fpc1020->lock); + rc = select_pin_ctl(fpc1020, buf); + mutex_unlock(&fpc1020->lock); + + return rc ? rc : count; +} +static DEVICE_ATTR(pinctl_set, S_IWUSR, NULL, pinctl_set); + +static ssize_t regulator_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + char op; + char name[16]; + int rc; + bool enable; + + if (NUM_PARAMS_REG_ENABLE_SET != sscanf(buf, "%15[^,],%c", name, &op)) + return -EINVAL; + if (op == 'e') + enable = true; + else if (op == 'd') + enable = false; + else + return -EINVAL; + + mutex_lock(&fpc1020->lock); + rc = vreg_setup(fpc1020, name, enable); + mutex_unlock(&fpc1020->lock); + + return rc ? rc : count; +} +static DEVICE_ATTR(regulator_enable, S_IWUSR, NULL, regulator_enable_set); + +static int hw_reset(struct fpc1020_data *fpc1020) +{ + int irq_gpio; + int rc; + struct device *dev = fpc1020->dev; + + dev_dbg(dev, "fpc %s --->: enter! \n", __func__); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_active"); + if (rc) + goto exit; + usleep_range(RESET_HIGH_SLEEP1_MIN_US, RESET_HIGH_SLEEP1_MAX_US); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + if (rc) + goto exit; + usleep_range(RESET_LOW_SLEEP_MIN_US, RESET_LOW_SLEEP_MAX_US); + + rc = select_pin_ctl(fpc1020, "fpc1020_reset_active"); + if (rc) + goto exit; + usleep_range(RESET_HIGH_SLEEP2_MIN_US, RESET_HIGH_SLEEP2_MAX_US); + + irq_gpio = gpio_get_value(fpc1020->irq_gpio); + dev_info(dev, "IRQ after reset %d\n", irq_gpio); + +exit: + dev_dbg(dev, "fpc %s <---: exit! \n", __func__); + return rc; +} + +static ssize_t hw_reset_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!strncmp(buf, "reset", strlen("reset"))) { + mutex_lock(&fpc1020->lock); + rc = hw_reset(fpc1020); + mutex_unlock(&fpc1020->lock); + } else { + return -EINVAL; + } + + return rc ? rc : count; +} +static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, hw_reset_set); + +/** + * Will setup GPIOs, and regulators to correctly initialize the touch sensor to + * be ready for work. + * + * In the correct order according to the sensor spec this function will + * enable/disable regulators, and reset line, all to set the sensor in a + * correct power on or off state "electrical" wise. + * + * @see device_prepare_set + * @note This function will not send any commands to the sensor it will only + * control it "electrically". + */ +static int device_prepare(struct fpc1020_data *fpc1020, bool enable) +{ + int rc = 0; + + struct device *dev = fpc1020->dev; + + if(enable){ + dev_info(dev, "fpc %s --->: enter, for enable power! \n", __func__); + } + else{ + dev_info(dev, "fpc %s --->: enter, for disable power! \n", __func__); + } + + mutex_lock(&fpc1020->lock); + + if (enable && !fpc1020->prepared) { + + rc = irq_setup(fpc1020, true); + + if(rc){ + dev_dbg(dev, "fpc irq setup failed。 \n"); + } + + select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + + rc = vreg_setup(fpc1020, "vdd_ana", true); + if (rc){ + dev_dbg(dev, "fpc power on failed。 \n"); + goto exit; + } + + usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US); + + /* As we can't control chip select here the other part of the + * sensor driver eg. the TEE driver needs to do a _SOFT_ reset + * on the sensor after power up to be sure that the sensor is + * in a good state after power up. Okeyed by ASIC. */ + + (void)select_pin_ctl(fpc1020, "fpc1020_reset_active"); + hw_reset(fpc1020); + + fpc1020->prepared = true; + dev_dbg(dev, "fpc power on success. \n"); + } else if (!enable && fpc1020->prepared) { + + rc = irq_setup(fpc1020, false); + + if(rc){ + dev_dbg(dev, "fpc irq setup failed。 \n"); + } + + (void)select_pin_ctl(fpc1020, "fpc1020_reset_reset"); + + usleep_range(PWR_ON_SLEEP_MIN_US, PWR_ON_SLEEP_MAX_US); + + rc = vreg_setup(fpc1020, "vdd_ana", false); + if(rc){ + dev_dbg(dev, "fpc vreg power off failed. \n"); + goto exit; + } + +exit: + fpc1020->prepared = false; + } else { + rc = 0; + } + mutex_unlock(&fpc1020->lock); + + dev_dbg(dev, "fpc %s <---: exit! \n", __func__); + return rc; +} + +/** + * sysfs node to enable/disable (power up/power down) the touch sensor + * + * @see device_prepare + */ +static ssize_t device_prepare_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!strncmp(buf, "enable", strlen("enable"))) + rc = device_prepare(fpc1020, true); + else if (!strncmp(buf, "disable", strlen("disable"))) + rc = device_prepare(fpc1020, false); + else + return -EINVAL; + + return rc ? rc : count; +} +static DEVICE_ATTR(device_prepare, S_IWUSR, NULL, device_prepare_set); + +/** + * sysfs node for controlling whether the driver is allowed + * to wake up the platform on interrupt. + */ +static ssize_t wakeup_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + ssize_t ret = count; + + mutex_lock(&fpc1020->lock); +/* + if (!strncmp(buf, "enable", strlen("enable"))) + atomic_set(&fpc1020->wakeup_enabled, 1); + else if (!strncmp(buf, "disable", strlen("disable"))) + atomic_set(&fpc1020->wakeup_enabled, 0); + else + ret = -EINVAL; +*/ + mutex_unlock(&fpc1020->lock); + + return ret; +} +static DEVICE_ATTR(wakeup_enable, S_IWUSR, NULL, wakeup_enable_set); + +/** + * sysfs node for controlling the wakelock. + */ +static ssize_t handle_wakelock_cmd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + ssize_t ret = count; + + mutex_lock(&fpc1020->lock); + if (!strncmp(buf, RELEASE_WAKELOCK_W_V, + min(count, strlen(RELEASE_WAKELOCK_W_V)))) { + if (fpc1020->nbr_irqs_received_counter_start == + fpc1020->nbr_irqs_received) { + __pm_relax(&fpc1020->ttw_wl); + } else { + dev_dbg(dev, "Ignore releasing of wakelock %d != %d", + fpc1020->nbr_irqs_received_counter_start, + fpc1020->nbr_irqs_received); + } + } else if (!strncmp(buf, RELEASE_WAKELOCK, min(count, + strlen(RELEASE_WAKELOCK)))) { + __pm_relax(&fpc1020->ttw_wl); + } else if (!strncmp(buf, START_IRQS_RECEIVED_CNT, + min(count, strlen(START_IRQS_RECEIVED_CNT)))) { + fpc1020->nbr_irqs_received_counter_start = + fpc1020->nbr_irqs_received; + } else + ret = -EINVAL; + mutex_unlock(&fpc1020->lock); + + return ret; +} +static DEVICE_ATTR(handle_wakelock, S_IWUSR, NULL, handle_wakelock_cmd); + +/** + * sysf node to check the interrupt status of the sensor, the interrupt + * handler should perform sysf_notify to allow userland to poll the node. + */ +static ssize_t irq_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + int irq = gpio_get_value(fpc1020->irq_gpio); + + return scnprintf(buf, PAGE_SIZE, "%i\n", irq); +} + +/** + * writing to the irq node will just drop a printk message + * and return success, used for latency measurement. + */ +static ssize_t irq_ack(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + dev_dbg(fpc1020->dev, "%s\n", __func__); + + return count; +} +static DEVICE_ATTR(irq, S_IRUSR | S_IWUSR, irq_get, irq_ack); + +static ssize_t fingerdown_wait_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + dev_info(fpc1020->dev, "%s -> %s\n", __func__, buf); + if (!strncmp(buf, "enable", strlen("enable")) && fpc1020->prepared) + fpc1020->wait_finger_down = true; + else if (!strncmp(buf, "disable", strlen("disable")) && fpc1020->prepared) + fpc1020->wait_finger_down = false; + else + return -EINVAL; + + return count; +} +static DEVICE_ATTR(fingerdown_wait, S_IWUSR, NULL, fingerdown_wait_set); + +static ssize_t vendor_update(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + //rc = add_hw_component_info(HWMON_CONPONENT_NAME, "ic_vendor", (char *)buf); + //rc = add_hw_component_info(HWMON_CONPONENT_NAME, "reserve", "0xFFFF"); + return rc ? rc : count; +} +static DEVICE_ATTR(vendor, S_IWUSR, NULL, vendor_update); + +static ssize_t irq_enable_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + if (!strncmp(buf, "1", strlen("1"))) { + mutex_lock(&fpc1020->lock); + enable_irq(gpio_to_irq(fpc1020->irq_gpio)); + mutex_unlock(&fpc1020->lock); + pr_debug("fpc enable irq\n"); + } else if (!strncmp(buf, "0", strlen("0"))) { + mutex_lock(&fpc1020->lock); + disable_irq(gpio_to_irq(fpc1020->irq_gpio)); + mutex_unlock(&fpc1020->lock); + pr_debug("fpc disable irq\n"); + } + + return rc ? rc : count; +} +static DEVICE_ATTR(irq_enable, S_IWUSR | S_IRUSR | S_IRGRP | S_IWGRP , NULL, irq_enable_set); + +static struct attribute *attributes[] = { + &dev_attr_request_vreg.attr, + &dev_attr_pinctl_set.attr, + &dev_attr_device_prepare.attr, + &dev_attr_regulator_enable.attr, + &dev_attr_hw_reset.attr, + &dev_attr_wakeup_enable.attr, + &dev_attr_handle_wakelock.attr, + &dev_attr_clk_enable.attr, + &dev_attr_irq_enable.attr, + &dev_attr_irq.attr, + &dev_attr_vendor.attr, + &dev_attr_fingerdown_wait.attr, + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +#ifndef FPC_DRM_INTERFACE_WA +static void notification_work(struct work_struct *work) +{ + pr_info("%s: unblank\n", __func__); + dsi_bridge_interface_enable(FP_UNLOCK_REJECTION_TIMEOUT); +} +#endif + +static irqreturn_t fpc1020_irq_handler(int irq, void *handle) +{ + struct fpc1020_data *fpc1020 = handle; + + dev_dbg(fpc1020->dev, "%s\n", __func__); + + mutex_lock(&fpc1020->lock); + if (atomic_read(&fpc1020->wakeup_enabled)) { + fpc1020->nbr_irqs_received++; + __pm_wakeup_event(&fpc1020->ttw_wl, FPC_TTW_HOLD_TIME); + } + mutex_unlock(&fpc1020->lock); + + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); + if (fpc1020->wait_finger_down && fpc1020->fb_black && fpc1020->prepared) { + pr_info("%s enter fingerdown & fb_black then schedule_work\n", __func__); + fpc1020->wait_finger_down = false; +#ifndef FPC_DRM_INTERFACE_WA + schedule_work(&fpc1020->work); +#endif + } + + return IRQ_HANDLED; +} + +static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, + const char *label, int *gpio) +{ + int rc; + struct device *dev = fpc1020->dev; + struct device_node *np = dev->of_node; + + dev_dbg(dev, "fpc %s --->: enter!\n", __func__); + + rc = of_get_named_gpio(np, label, 0); + + if (rc < 0) { + dev_err(dev, "failed to get '%s'\n", label); + return rc; + } + *gpio = rc; + + rc = devm_gpio_request(dev, *gpio, label); + if (rc) { + dev_err(dev, "failed to request gpio %d\n", *gpio); + return rc; + } + + dev_dbg(dev, "fpc %s <--- %s at %d\n", __func__, label, *gpio); + + return 0; +} + +static int fpc_fb_notif_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct fpc1020_data *fpc1020 = container_of(nb, struct fpc1020_data, + fb_notifier); + struct fb_event *evdata = data; + unsigned int blank; + + if (!fpc1020) + return 0; + + if (val != DRM_EVENT_BLANK || fpc1020->prepared == false) + return 0; + + pr_debug("[info] %s value = %d\n", __func__, (int)val); + + if (evdata && evdata->data && val == DRM_EVENT_BLANK) { + blank = *(int *)(evdata->data); + switch (blank) { + case DRM_BLANK_POWERDOWN: + fpc1020->fb_black = true; + break; + case DRM_BLANK_UNBLANK: + fpc1020->fb_black = false; + break; + default: + pr_debug("%s defalut\n", __func__); + break; + } + } + return NOTIFY_OK; +} + + +static struct notifier_block fpc_notif_block = { + .notifier_call = fpc_fb_notif_callback, +}; + +static int fpc1020_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc = 0; + size_t i; + + struct device_node *np = dev->of_node; + struct fpc1020_data *fpc1020 = devm_kzalloc(dev, sizeof(*fpc1020), + GFP_KERNEL); + + dev_info(dev, "fpc %s --->: enter! \n", __func__); + + if (!fpc1020) { + dev_err(dev, + "failed to allocate memory for struct fpc1020_data\n"); + rc = -ENOMEM; + goto exit; + } + + fpc1020->dev = dev; + platform_set_drvdata(pdev, fpc1020); + //register_hw_component_info(HWMON_CONPONENT_NAME); + + if (!np) { + dev_err(dev, "no of node found\n"); + rc = -EINVAL; + goto exit; + } + + fpc1020->fingerprint_pinctrl = devm_pinctrl_get(dev); + if (IS_ERR(fpc1020->fingerprint_pinctrl)) { + if (PTR_ERR(fpc1020->fingerprint_pinctrl) == -EPROBE_DEFER) { + dev_info(dev, "pinctrl not ready\n"); + rc = -EPROBE_DEFER; + goto exit; + } + dev_err(dev, "Target does not use pinctrl\n"); + fpc1020->fingerprint_pinctrl = NULL; + rc = -EINVAL; + goto exit; + } + + for (i = 0; i < ARRAY_SIZE(fpc1020->pinctrl_state); i++) { + const char *n = pctl_names[i]; + struct pinctrl_state *state = + pinctrl_lookup_state(fpc1020->fingerprint_pinctrl, n); + if (IS_ERR(state)) { + dev_err(dev, "cannot find '%s'\n", n); + rc = -EINVAL; + goto exit; + } + dev_info(dev, "found pin control %s\n", n); + fpc1020->pinctrl_state[i] = state; + } + + atomic_set(&fpc1020->wakeup_enabled, 1); + + fpc1020->irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT | IRQF_NO_SUSPEND; + fpc1020->irq_requested = false; + device_init_wakeup(dev, 1); +/* + if (of_property_read_bool(dev->of_node, "fpc,enable-wakeup")) { + irqf |= IRQF_NO_SUSPEND; + device_init_wakeup(dev, 1); + } +*/ + mutex_init(&fpc1020->lock); + + wakeup_source_init(&fpc1020->ttw_wl, "fpc_ttw_wl"); + + rc = sysfs_create_group(&dev->kobj, &attribute_group); + if (rc) { + dev_err(dev, "fpc could not create sysfs\n"); + goto exit; + } + + rc = reset_gpio_res(fpc1020); + if(rc){ + dev_err(dev, "fpc %s: could not initialize GPIOs No.!\n", __func__); + goto exit; + } + + if (of_property_read_bool(dev->of_node, "fpc,enable-on-boot")) { + dev_info(dev, "Enabling hardware\n"); + (void)device_prepare(fpc1020, true); + } + + fpc1020->fb_black = false; + fpc1020->wait_finger_down = false; +#ifndef FPC_DRM_INTERFACE_WA + INIT_WORK(&fpc1020->work, notification_work); +#endif + fpc1020->fb_notifier = fpc_notif_block; + drm_register_client(&fpc1020->fb_notifier); + + //rc = hw_reset(fpc1020); + + dev_info(dev, "%s: ok\n", __func__); + +exit: + dev_info(dev, "fpc %s <---: exit! \n", __func__); + return rc; +} + +static int fpc1020_remove(struct platform_device *pdev) +{ + struct fpc1020_data *fpc1020 = platform_get_drvdata(pdev); + + drm_unregister_client(&fpc1020->fb_notifier); + sysfs_remove_group(&pdev->dev.kobj, &attribute_group); + mutex_destroy(&fpc1020->lock); + wakeup_source_trash(&fpc1020->ttw_wl); + (void)vreg_setup(fpc1020, "vdd_ana", false); + (void)reset_gpio_res(fpc1020); + //unregister_hw_component_info(HWMON_CONPONENT_NAME); + /* + (void)vreg_setup(fpc1020, "vdd_io", false); + (void)vreg_setup(fpc1020, "vcc_spi", false); + */ + dev_info(&pdev->dev, "%s\n", __func__); + + return 0; +} + +static struct of_device_id fpc1020_of_match[] = { + { .compatible = "fpc,fpc1020", }, + {} +}; +MODULE_DEVICE_TABLE(of, fpc1020_of_match); + +static struct platform_driver fpc1020_driver = { + .driver = { + .name = "fpc1020", + .owner = THIS_MODULE, + .of_match_table = fpc1020_of_match, + }, + .probe = fpc1020_probe, + .remove = fpc1020_remove, +}; + +static int __init fpc1020_init(void) +{ + int rc; + + rc = platform_driver_register(&fpc1020_driver); + if (!rc) + pr_info("%s OK\n", __func__); + else + pr_err("%s %d\n", __func__, rc); + + return rc; +} + +static void __exit fpc1020_exit(void) +{ + pr_info("%s\n", __func__); + platform_driver_unregister(&fpc1020_driver); +} + +module_init(fpc1020_init); +module_exit(fpc1020_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Aleksej Makarov"); +MODULE_AUTHOR("Henrik Tillman "); +MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver."); diff --git a/drivers/input/fingerprint/fs_tee/Kconfig b/drivers/input/fingerprint/fs_tee/Kconfig new file mode 100644 index 000000000000..5f008a96af12 --- /dev/null +++ b/drivers/input/fingerprint/fs_tee/Kconfig @@ -0,0 +1,11 @@ +config FINGERPRINT_FS_TEE + tristate "FortSense Fingerprint" + default y + help + If you say Y to this option, support will be included for + the FortSense's fingerprint sensor. This driver supports + both REE and TEE. If in REE, CONFIG_SPI_SPIDEV must be set + to use the standard 'spidev' driver. + + This driver can also be built as a module. If so, the module + will be called 'fortsense_driver_all_in_one'. diff --git a/drivers/input/fingerprint/fs_tee/Makefile b/drivers/input/fingerprint/fs_tee/Makefile new file mode 100644 index 000000000000..8836e6352eb7 --- /dev/null +++ b/drivers/input/fingerprint/fs_tee/Makefile @@ -0,0 +1,60 @@ +# If KERNELRELEASE is defined, we've been invoked from the +# kernel build system and can use its language. +ifneq ($(KERNELRELEASE),) + +######################### fortsense driver config begin ######################### + +#just for mtk platform head file, kernel version less than kernle 4.0 +#mtk平台,内核版本小于4.0参考头文件 +ccflags-y += -I$(srctree)/drivers/spi/mediatek/$(MTK_PLATFORM) + +#just for mtk platform head file, kernel version larger than kernle 4.0 +#mtk平台,内核版本大于4.0参考头文件 +MTK_PLATFORM := $(subst ",,$(CONFIG_MTK_PLATFORM)) +ccflags-y += -I$(srctree)/drivers/spi/mediatek/$(MTK_PLATFORM) +ccflags-y += -I$(srctree)/drivers/misc/mediatek/include +ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat +ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include + +#just for beanpod v2 compatible +#豆荚 V2 TEE平台,兼容头文件 +ccflags-y += -I$(srctree)/drivers/misc/mediatek/teei/V1.0/tz_vfs + +#甁砵 TEE 平台,兼容头文件 +subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/tkcore/core +subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/tkcore/include/ + +# Android L +#include $(srctree)/drivers/misc/mediatek/Makefile.custom + +#fortsense driver file +# 如果没有移植 Kconfig 导致驱动没编译到,请打开下面的 obj-y 并屏蔽 obj-$(CONFIG_FINGERPRINT_FORTSENSE) +obj-y += fortsense_fp.o +#obj-$(CONFIG_FINGERPRINT_FORTSENSE) += fortsense_fp.o +fortsense_fp-objs := sf_ctl.o sf_hw.o + +########################## fortsense driver config end ########################## + +else + +KBUILDDIR := $(ANDROID_PRODUCT_OUT)/obj/KERNEL_OBJ +MODULEPWD := $(shell pwd) + +.PHONY: all install clean + +# $(MAKE) -C $(KBUILDDIR) ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- +# $(MAKE) -C $(KBUILDDIR) ARCH=arm CROSS_COMPILE=arm-eabi- + +all: + $(MAKE) -C $(KBUILDDIR) ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- \ + M=$(MODULEPWD) CONFIG_FINGERPRINT_FORTSENSE=m modules + +install: + adb remount + adb push fortsense_fp.ko /data/ + adb shell sync + +clean: + rm -rf *.ko *.o *.mod.c .*.cmd .*tmp_versions *.symvers *.order + +endif # End of KERNELRELEASE. diff --git a/drivers/input/fingerprint/fs_tee/sf_auto.h b/drivers/input/fingerprint/fs_tee/sf_auto.h new file mode 100644 index 000000000000..e26c9c96b130 --- /dev/null +++ b/drivers/input/fingerprint/fs_tee/sf_auto.h @@ -0,0 +1,126 @@ +#ifndef __SF_AUTO_H__ +#define __SF_AUTO_H__ + +// auto + +/* mtk or not */ +#if (SF_PLATFORM_SEL == SF_REE_QUALCOMM) || (SF_PLATFORM_SEL == SF_REE_SPREAD) || \ + (SF_PLATFORM_SEL == SF_TEE_QSEE) || (SF_PLATFORM_SEL == SF_TEE_TRUSTY) || \ + (SF_PLATFORM_SEL == SF_REE_HIKEY9600) +#define SF_MTK_CPU 0 +#else +#define SF_MTK_CPU 1 +#endif + +/* tee or ree */ +#if (SF_PLATFORM_SEL == SF_REE_QUALCOMM) || (SF_PLATFORM_SEL == SF_REE_SPREAD) || \ + (SF_PLATFORM_SEL == SF_REE_MTK) || (SF_PLATFORM_SEL == SF_REE_HIKEY9600) || \ + (SF_PLATFORM_SEL == SF_REE_MTK_L5_X) +#define SF_REE_PLATFORM 1 +#else +#define SF_REE_PLATFORM 0 +#endif + +/* spi or platform bus driver */ +#if (SF_PLATFORM_SEL == SF_TEE_QSEE) || (SF_PLATFORM_SEL == SF_TEE_TRUSTY) +#define SF_SPI_RW_EN 0 +#else +#define SF_SPI_RW_EN 1 +#endif + +#if(SF_COMPATIBLE_SEL == SF_COMPATIBLE_NOF || SF_COMPATIBLE_SEL == SF_COMPATIBLE_NOF_BP_V2_7) +#define MULTI_HAL_COMPATIBLE 0 +#else +#define MULTI_HAL_COMPATIBLE 1 +#endif + +/* check ree, trustkernel, beanpodV2 read chip ID by spi bus or not */ +#if ((SF_REE_PLATFORM) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_REE)) || \ + (((SF_PLATFORM_SEL == SF_TEE_TRUSTKERNEL)) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_TRUSTKERNEL)) || \ + (((SF_PLATFORM_SEL == SF_TEE_BEANPOD)) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_BEANPOD_V2)) || \ + (((SF_PLATFORM_SEL == SF_TEE_BEANPOD)) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_BEANPOD_V2_7)) || \ + (((SF_PLATFORM_SEL == SF_TEE_RONGCARD)) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_RONGCARD)) +#define SF_PROBE_ID_EN 1 +#undef MULTI_HAL_COMPATIBLE +#define MULTI_HAL_COMPATIBLE 0 +#else +#define SF_PROBE_ID_EN 0 +#endif + +/* trustkernel compatible or not */ +#if (SF_PLATFORM_SEL == SF_TEE_TRUSTKERNEL) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_TRUSTKERNEL) +#define SF_TRUSTKERNEL_COMPATIBLE 1 +#else +#define SF_TRUSTKERNEL_COMPATIBLE 0 +#endif + +/* beanpod V1 compatible or not */ +#if (SF_PLATFORM_SEL == SF_TEE_BEANPOD) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_BEANPOD_V1) +#define SF_BEANPOD_COMPATIBLE_V1 1 +#else +#define SF_BEANPOD_COMPATIBLE_V1 0 +#endif + +#if (SF_PLATFORM_SEL == SF_TEE_BEANPOD) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_BEANPOD_V2) +#define SF_BEANPOD_COMPATIBLE_V2 1 +#else +#define SF_BEANPOD_COMPATIBLE_V2 0 +#endif + +#if (SF_PLATFORM_SEL == SF_TEE_BEANPOD) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_BEANPOD_V2_7 || \ + SF_COMPATIBLE_SEL == SF_COMPATIBLE_NOF_BP_V2_7) +#define SF_BEANPOD_COMPATIBLE_V2_7 1 +#else +#define SF_BEANPOD_COMPATIBLE_V2_7 0 +#endif + +#ifdef CONFIG_ARCH_MT6580 +#define MT_CG_PERI_SPI0 MT_CG_SPI_SW_CG +#endif + +/* rongcard compatible or not */ +#if (SF_PLATFORM_SEL == SF_TEE_RONGCARD) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_RONGCARD) +#define SF_RONGCARD_COMPATIBLE 1 +#else +#define SF_RONGCARD_COMPATIBLE 0 +#endif + +#if (SF_BEANPOD_COMPATIBLE_V1 || SF_BEANPOD_COMPATIBLE_V2 || SF_BEANPOD_COMPATIBLE_V2_7) +#undef MULTI_HAL_COMPATIBLE +#define MULTI_HAL_COMPATIBLE 0 +#endif + +#if (SF_PLATFORM_SEL == SF_REE_SPREAD) || (SF_PLATFORM_SEL == SF_TEE_TRUSTY) +#undef SF_INT_TRIG_HIGH +#define SF_INT_TRIG_HIGH 1 +#endif + +/* check log debug */ +#if SF_LOG_ENABLE +#define SF_LOG_LEVEL KERN_INFO +#else +#define SF_LOG_LEVEL KERN_DEBUG +#endif + +/* define androidL mtk */ +#if (SF_PLATFORM_SEL == SF_REE_MTK_L5_X) +#define REE_MTK_ANDROID_L 1 +#else +#define REE_MTK_ANDROID_L 0 +#endif + +#if SF_BEANPOD_COMPATIBLE_V2 || SF_REE_PLATFORM || \ + (SF_PLATFORM_SEL == SF_TEE_BEANPOD && SF_COMPATIBLE_SEL == SF_COMPATIBLE_BEANPOD_V2_7) +#define SF_SPI_TRANSFER 1 +#else +#define SF_SPI_TRANSFER 0 +#endif + +/* beanpod and trustkernel compatible in CONFIG_SPI_MT65XX */ +#if (defined(CONFIG_SPI_MT65XX) && (SF_COMPATIBLE_SEL == SF_COMPATIBLE_TRUSTKERNEL)) +#define SF_TRUSTKERNEL_COMPAT_SPI_MT65XX 1 +#else +#define SF_TRUSTKERNEL_COMPAT_SPI_MT65XX 0 +#endif + +#endif //__SF_AUTO_H__ \ No newline at end of file diff --git a/drivers/input/fingerprint/fs_tee/sf_ctl.c b/drivers/input/fingerprint/fs_tee/sf_ctl.c new file mode 100644 index 000000000000..245f8ee6e03b --- /dev/null +++ b/drivers/input/fingerprint/fs_tee/sf_ctl.c @@ -0,0 +1,1570 @@ +/** + * The device control driver for Fortsense's fingerprint sensor. + * + * Copyright (C) 2018 Fortsense Corporation. + * Copyright (C) 2021 XiaoMi, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sf_ctl.h" + +#if XIAOMI_DRM_INTERFACE_WA +#include +#include + +#define FP_UNLOCK_REJECTION_TIMEOUT 1500 + +#endif + +#if SF_BEANPOD_COMPATIBLE_V1 +#include "nt_smc_call.h" +#endif + +#if SF_INT_TRIG_HIGH +#include +#endif + +#ifdef CONFIG_RSEE +#include +#endif + +//--------------------------------------------------------------------------------- +#define SF_DRV_VERSION "v2.4.2-2020-11-09" + +#define MODULE_NAME "fortsense-sf_ctl" +#define xprintk(level, fmt, args...) printk(level MODULE_NAME": (%d) "fmt, __LINE__, ##args) + +#if SF_TRUSTKERNEL_COMPAT_SPI_MT65XX +#define SPI_MODULE_CLOCK (120 * 1000 * 1000) +#elif defined(CONFIG_MTK_SPI) +#define SPI_MODULE_CLOCK (100 * 1000 * 1000) +#endif + +#define SF_DEFAULT_SPI_SPEED (1 * 1000 * 1000) +//--------------------------------------------------------------------------------- + +//--------------------------------------------------------------------------------- +static long sf_ctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +static int sf_ctl_init_irq(void); +static int sf_ctl_init_input(void); +#ifdef CONFIG_COMPAT +static long sf_ctl_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +#endif +#if SF_REE_PLATFORM +static ssize_t sf_ctl_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos); +static ssize_t sf_ctl_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos); +#endif +#if (SF_PROBE_ID_EN && !SF_RONGCARD_COMPATIBLE) +static int sf_read_sensor_id(void); +#endif + +extern int sf_platform_init(struct sf_ctl_device *ctl_dev); +extern void sf_platform_exit(struct sf_ctl_device *ctl_dev); + +extern int fb_blank(struct fb_info *info, int blank); + +//--------------------------------------------------------------------------------- + +#if QUALCOMM_REE_DEASSERT +static int qualcomm_deassert = 0; +#endif + +#ifdef CONFIG_RSEE +int rsee_client_get_fpid(int *vendor_id); +#endif +static struct file_operations sf_ctl_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = sf_ctl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = sf_ctl_compat_ioctl, +#endif +#if SF_REE_PLATFORM + .read = sf_ctl_read, + .write = sf_ctl_write, +#endif +}; + +static struct sf_ctl_device sf_ctl_dev = { + .miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "fortsense_fp", + .fops = &sf_ctl_fops, + }, + .rst_num = 0, + .irq_pin = 0, + .irq_num = 0, + .spi_buf_size = 25 * 1024, +}; + +#if SF_REG_DEVICE_BY_DRIVER +static struct platform_device *sf_device = NULL; +#endif + +#if (defined(CONFIG_MTK_SPI) || SF_TRUSTKERNEL_COMPAT_SPI_MT65XX) +#define SF_DEFAULT_SPI_HALF_CYCLE_TIME ((SPI_MODULE_CLOCK / SF_DEFAULT_SPI_SPEED) / 2) +#define SF_DEFAULT_SPI_HIGH_TIME (((SPI_MODULE_CLOCK / SF_DEFAULT_SPI_SPEED) % 2 == 0) ? \ + SF_DEFAULT_SPI_HALF_CYCLE_TIME : (SF_DEFAULT_SPI_HALF_CYCLE_TIME + 1)) +#define SF_DEFAULT_SPI_LOW_TIME SF_DEFAULT_SPI_HALF_CYCLE_TIME + +static struct mt_chip_conf smt_conf = { + .setuptime = 15, + .holdtime = 15, + .high_time = SF_DEFAULT_SPI_HIGH_TIME, // 10--6m 15--4m 20--3m 30--2m [ 60--1m 120--0.5m 300--0.2m] + .low_time = SF_DEFAULT_SPI_LOW_TIME, + .cs_idletime = 20, + .ulthgh_thrsh = 0, + .cpol = SPI_CPOL_0, + .cpha = SPI_CPHA_0, + .rx_mlsb = SPI_MSB, + .tx_mlsb = SPI_MSB, + .tx_endian = 0, + .rx_endian = 0, + .com_mod = FIFO_TRANSFER, + .pause = 0, + .finish_intr = 1, + .deassert = 0, + .ulthigh = 0, + .tckdly = 0, +}; +#endif + +static int sf_remove(sf_device_t *pdev); +static int sf_probe(sf_device_t *pdev); + +#if SF_SPI_RW_EN +static struct of_device_id sf_of_match[] = { + { .compatible = COMPATIBLE_SW_FP, }, + {}, +}; + +static struct spi_board_info spi_board_devs[] __initdata = { + [0] = { + .modalias = "fortsense-fp", + .bus_num = 0, + .chip_select = 0, + .mode = SPI_MODE_0, + }, +}; + +static int sf_ctl_spi_speed(unsigned int speed) +{ +#ifdef CONFIG_MTK_SPI + unsigned int time; + time = SPI_MODULE_CLOCK / speed; + sf_ctl_dev.mt_conf.high_time = time / 2; + sf_ctl_dev.mt_conf.low_time = time / 2; + + if ((time % 2) != 0) { + sf_ctl_dev.mt_conf.high_time += 1; + } + +#elif (SF_REE_PLATFORM && QUALCOMM_REE_DEASSERT) + double delay_ns = 0; + + if (speed <= 1000 * 1000) { + speed = 0.96 * 1000 * 1000; //0.96M + qualcomm_deassert = 0; + } + else if (speed <= 4800 * 1000) { + delay_ns = (1.0 / (((double)speed) / (1.0 * 1000 * 1000)) - 1.0 / 4.8) * 8 * 1000; + speed = 4.8 * 1000 * 1000; //4.8M + qualcomm_deassert = 3; + } + else { + delay_ns = (1.0 / (((double)speed) / (1.0 * 1000 * 1000)) - 1.0 / 9.6) * 8 * 1000; + speed = 9.6 * 1000 * 1000; //9.6M + qualcomm_deassert = 10; + } + + xprintk(KERN_INFO, "need delay_ns = xxx, qualcomm_deassert = %d(maybe custom).\n", qualcomm_deassert); +#endif + sf_ctl_dev.pdev->max_speed_hz = speed; + spi_setup(sf_ctl_dev.pdev); + return 0; +} +#endif + +static sf_driver_t sf_driver = { + .driver = { + .name = "fortsense-fp", +#if SF_SPI_RW_EN + .bus = &spi_bus_type, +#endif + .owner = THIS_MODULE, +#ifdef CONFIG_OF +#if SF_SPI_RW_EN + .of_match_table = sf_of_match, +#endif +#endif + }, + .probe = sf_probe, + .remove = sf_remove, +}; + +static sf_version_info_t sf_hw_ver; + + +//--------------------------------------------------------------------------------- +#if SF_INT_TRIG_HIGH +static int sf_ctl_set_irq_type(unsigned long type) +{ + int err = 0; + + if (sf_ctl_dev.irq_num > 0) { + err = irq_set_irq_type(sf_ctl_dev.irq_num, type | IRQF_NO_SUSPEND | IRQF_ONESHOT); + } + + return err; +} +#endif + +static void sf_ctl_device_event(struct work_struct *ws) +{ + char *uevent_env[2] = { SF_INT_EVENT_NAME, NULL }; + kobject_uevent_env(&sf_ctl_dev.miscdev.this_device->kobj, + KOBJ_CHANGE, uevent_env); +} + +static irqreturn_t sf_ctl_device_irq(int irq, void *dev_id) +{ + disable_irq_nosync(irq); + xprintk(SF_LOG_LEVEL, "%s(irq = %d, ..) toggled.\n", __FUNCTION__, irq); + schedule_work(&sf_ctl_dev.work_queue); +#ifdef CONFIG_PM_WAKELOCKS + __pm_wakeup_event(&sf_ctl_dev.wakelock, msecs_to_jiffies(5000)); +#else + wake_lock_timeout(&sf_ctl_dev.wakelock, msecs_to_jiffies(5000)); +#endif +#if SF_INT_TRIG_HIGH + sf_ctl_set_irq_type(IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND | IRQF_ONESHOT); +#endif + enable_irq(irq); + return IRQ_HANDLED; +} + +static int sf_ctl_report_key_event(struct input_dev *input, sf_key_event_t *kevent) +{ + int err = 0; + unsigned int key_code = KEY_UNKNOWN; + xprintk(SF_LOG_LEVEL, "%s(..) enter.\n", __FUNCTION__); + + switch (kevent->key) { + case SF_KEY_HOME: + key_code = KEY_HOME; + break; + + case SF_KEY_MENU: + key_code = KEY_MENU; + break; + + case SF_KEY_BACK: + key_code = KEY_BACK; + break; + + case SF_KEY_F11: + key_code = XIAOMI_KEYCODE_SINGLE_CLK; + break; + + case SF_KEY_ENTER: + key_code = KEY_ENTER; + break; + + case SF_KEY_UP: + key_code = KEY_UP; + break; + + case SF_KEY_LEFT: + key_code = KEY_LEFT; + break; + + case SF_KEY_RIGHT: + key_code = KEY_RIGHT; + break; + + case SF_KEY_DOWN: + key_code = KEY_DOWN; + break; + + case SF_KEY_WAKEUP: + key_code = KEY_WAKEUP; +#if XIAOMI_DRM_INTERFACE_WA + + if (kevent->value && sf_ctl_dev.is_fb_black) { + schedule_work(&sf_ctl_dev.work_drm); + } + +#endif + break; + + case SF_KEY_F12: + key_code = XIAOMI_KEYCODE_DOUBLE_CLK; + break; + + default: + break; + } + + input_report_key(input, key_code, kevent->value); + input_sync(input); + return err; +} + +static const char *sf_ctl_get_version(void) +{ + static char version[SF_DRV_VERSION_LEN] = {'\0', }; + strncpy(version, SF_DRV_VERSION, SF_DRV_VERSION_LEN); + version[SF_DRV_VERSION_LEN - 1] = '\0'; + return (const char *)version; +} + +//////////////////////////////////////////////////////////////////////////////// +// struct file_operations fields. + +static long sf_ctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + sf_key_event_t kevent; + + switch (cmd) { + case SF_IOC_INIT_DRIVER: { +#if MULTI_HAL_COMPATIBLE + sf_ctl_dev.gpio_init(&sf_ctl_dev); +#endif + break; + } + + case SF_IOC_DEINIT_DRIVER: { +#if MULTI_HAL_COMPATIBLE + sf_ctl_dev.free_gpio(&sf_ctl_dev); +#endif + break; + } + + case SPI_IOC_RST: + case SF_IOC_RESET_DEVICE: { + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); + break; + } + + case SF_IOC_ENABLE_IRQ: { + // TODO: + break; + } + + case SF_IOC_DISABLE_IRQ: { + // TODO: + break; + } + + case SF_IOC_REQUEST_IRQ: { +#if MULTI_HAL_COMPATIBLE + sf_ctl_init_irq(); +#endif + break; + } + + case SF_IOC_ENABLE_SPI_CLK: { + sf_ctl_dev.spi_clk_on(true); + break; + } + + case SF_IOC_DISABLE_SPI_CLK: { + sf_ctl_dev.spi_clk_on(false); + break; + } + + case SF_IOC_ENABLE_POWER: { + sf_ctl_dev.power_on(true); + break; + } + + case SF_IOC_DISABLE_POWER: { + //sf_ctl_dev.power_on(false); + break; + } + + case SF_IOC_REPORT_KEY_EVENT: { + if (copy_from_user(&kevent, (sf_key_event_t *)arg, sizeof(sf_key_event_t))) { + xprintk(KERN_ERR, "copy_from_user(..) failed.\n"); + err = (-EFAULT); + break; + } + + err = sf_ctl_report_key_event(sf_ctl_dev.input, &kevent); + break; + } + + case SF_IOC_SYNC_CONFIG: { + // TODO: + break; + } + + case SPI_IOC_WR_MAX_SPEED_HZ: + case SF_IOC_SPI_SPEED: { +#if SF_SPI_RW_EN + sf_ctl_spi_speed(arg); +#endif + break; + } + + case SPI_IOC_RD_MAX_SPEED_HZ: { + // TODO: + break; + } + + case FORTSENSE_IOC_ATTRIBUTE: + case SF_IOC_ATTRIBUTE: { + err = __put_user(sf_ctl_dev.attribute, (__u32 __user *)arg); + break; + } + + case SF_IOC_GET_VERSION: { + if (copy_to_user((void *)arg, sf_ctl_get_version(), SF_DRV_VERSION_LEN)) { + xprintk(KERN_ERR, "copy_to_user(..) failed.\n"); + err = (-EFAULT); + break; + } + + break; + } + + case SF_IOC_SET_LIB_VERSION: { + if (copy_from_user((void *)&sf_hw_ver, (void *)arg, sizeof(sf_version_info_t))) { + xprintk(KERN_ERR, "sf_hw_info_t copy_from_user(..) failed.\n"); + err = (-EFAULT); + break; + } + + break; + } + + case SF_IOC_GET_LIB_VERSION: { + if (copy_to_user((void *)arg, (void *)&sf_hw_ver, sizeof(sf_version_info_t))) { + xprintk(KERN_ERR, "sf_hw_info_t copy_to_user(..) failed.\n"); + err = (-EFAULT); + break; + } + + break; + } + + case SF_IOC_SET_SPI_BUF_SIZE: { + sf_ctl_dev.spi_buf_size = arg; + break; + } + + case SF_IOC_SET_RESET_OUTPUT: { + if (arg) { + sf_ctl_dev.reset(true); + } + else { + sf_ctl_dev.reset(false); + } + + break; + } + + default: + err = (-EINVAL); + break; + } + + return err; +} + +static ssize_t fortsense_version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = 0; + len += sprintf(buf, "%s\n", sf_hw_ver.driver); + return len; +} +static DEVICE_ATTR(version, S_IRUGO | S_IWUSR, fortsense_version_show, NULL); + +static ssize_t fortsense_chip_info_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = 0; + len += sprintf((char *)buf, "chip : %s %s\nid : 0x0 lib:%s\nvendor : fw:%s\nmore : fingerprint\n", + sf_hw_ver.fortsense_id, sf_hw_ver.ca_version, + sf_hw_ver.algorithm, + sf_hw_ver.firmware); + return len; +} +static DEVICE_ATTR(chip_info, S_IRUGO | S_IWUSR, fortsense_chip_info_show, NULL); + +static ssize_t sf_show_version(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = 0; + ret += sprintf(buf + ret, "solution:%s\n", sf_hw_ver.tee_solution); + ret += sprintf(buf + ret, "ca :%s\n", sf_hw_ver.ca_version); + ret += sprintf(buf + ret, "ta :%s\n", sf_hw_ver.ta_version); + ret += sprintf(buf + ret, "alg :%s\n", sf_hw_ver.algorithm); + ret += sprintf(buf + ret, "nav :%s\n", sf_hw_ver.algo_nav); + ret += sprintf(buf + ret, "driver :%s\n", sf_hw_ver.driver); + ret += sprintf(buf + ret, "firmware:%s\n", sf_hw_ver.firmware); + ret += sprintf(buf + ret, "sensor :%s\n", sf_hw_ver.fortsense_id); + ret += sprintf(buf + ret, "vendor :%s\n", sf_hw_ver.vendor_id); + return ret; +} + +static DEVICE_ATTR(tee_version, S_IWUSR | S_IRUGO, sf_show_version, NULL); + +static ssize_t +sf_store_set_fun(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + int blank; + int ret; + ret = sscanf(buf, "%d", &blank); + xprintk(KERN_INFO, "sf_store_set_fun (..) blank = %d.\n", blank); + fb_blank(NULL, blank); + xprintk(KERN_INFO, "sf_store_set_fun (..) end.\n"); + return count; +} +static DEVICE_ATTR(set_fun, S_IWUSR | S_IRUGO, NULL, sf_store_set_fun); + +static struct attribute *sf_sysfs_entries[] = { + &dev_attr_set_fun.attr, + &dev_attr_tee_version.attr, + &dev_attr_chip_info.attr, + &dev_attr_version.attr, + NULL +}; + +static struct attribute_group sf_attribute_group = { + .attrs = sf_sysfs_entries, +}; + +#ifdef CONFIG_COMPAT +static long sf_ctl_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return sf_ctl_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static int sf_suspend(void) +{ + char *screen[2] = { "SCREEN_STATUS=OFF", NULL }; + kobject_uevent_env(&sf_ctl_dev.miscdev.this_device->kobj, KOBJ_CHANGE, screen); +#if SF_INT_TRIG_HIGH + sf_ctl_set_irq_type(IRQF_TRIGGER_HIGH); +#endif + return 0; +} + +static int sf_resume(void) +{ + char *screen[2] = { "SCREEN_STATUS=ON", NULL }; + kobject_uevent_env(&sf_ctl_dev.miscdev.this_device->kobj, KOBJ_CHANGE, screen); +#if SF_INT_TRIG_HIGH + sf_ctl_set_irq_type(IRQF_TRIGGER_RISING | IRQF_NO_SUSPEND | IRQF_ONESHOT); +#endif + return 0; +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void sf_early_suspend(struct early_suspend *handler) +{ + sf_suspend(); +} + +static void sf_late_resume(struct early_suspend *handler) +{ + sf_resume(); +} + +#elif defined(CONFIG_ADF_SPRD) +/* + * touchscreen's suspend and resume state should rely on screen state, + * as fb_notifier and early_suspend are all disabled on our platform, + * we can only use adf_event now + */ +static int sf_adf_event_handler( \ + struct notifier_block *nb, unsigned long action, void *data) +{ + struct adf_notifier_event *event = data; + int adf_event_data; + + if (action != ADF_EVENT_BLANK) { + return NOTIFY_DONE; + } + + adf_event_data = *(int *)event->data; + xprintk(KERN_INFO, "receive adf event with adf_event_data=%d \n", adf_event_data); + + switch (adf_event_data) { + case DRM_MODE_DPMS_ON: + sf_resume(); + break; + + case DRM_MODE_DPMS_OFF: + sf_suspend(); + break; + + default: + xprintk(KERN_ERR, "receive adf event with error data, adf_event_data=%d \n", + adf_event_data); + break; + } + + return NOTIFY_OK; +} + +#else + +static int sf_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct sf_ctl_device *ctl_dev = container_of(self, struct sf_ctl_device, notifier); + struct fb_event *evdata = data; + unsigned int blank; + int retval = 0; + + if ((ctl_dev == NULL) || (evdata == NULL) || (evdata->data == NULL)) { + return 0; + } + + blank = *(int *)evdata->data; +#if XIAOMI_DRM_INTERFACE_WA + + if (event == DRM_EVENT_BLANK) { + switch (blank) { + case DRM_BLANK_UNBLANK: + ctl_dev->is_fb_black = false; + break; + + case DRM_BLANK_POWERDOWN: + ctl_dev->is_fb_black = true; + break; + + default: + break; + } + } + +#endif + + if (event == FB_EVENT_BLANK /* FB_EARLY_EVENT_BLANK */) { + switch (blank) { + case FB_BLANK_UNBLANK: + sf_resume(); + break; + + case FB_BLANK_POWERDOWN: + sf_suspend(); + break; + + default: + break; + } + } + + return retval; +} + +#if XIAOMI_DRM_INTERFACE_WA +static void sf_drm_notification_work(struct work_struct *work) +{ + xprintk(KERN_ERR, "receive unblank event\n", __FUNCTION__); + dsi_bridge_interface_enable(FP_UNLOCK_REJECTION_TIMEOUT); +} +#endif + +#endif //SF_CFG_HAS_EARLYSUSPEND +//////////////////////////////////////////////////////////////////////////////// +static int sf_remove(sf_device_t *spi) +{ + int err = 0; + + if (sf_ctl_dev.pdev != NULL && sf_ctl_dev.gpio_init != NULL) { +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&sf_ctl_dev.early_suspend); +#elif defined(CONFIG_ADF_SPRD) + // TODO: is it name adf_unregister_client? Unverified it. + adf_unregister_client(&sf_ctl_dev.adf_event_block); +#else +#if XIAOMI_DRM_INTERFACE_WA + drm_unregister_client(&sf_ctl_dev.notifier); +#else + fb_unregister_client(&sf_ctl_dev.notifier); +#endif +#endif + + if (sf_ctl_dev.input) { + input_unregister_device(sf_ctl_dev.input); + } + + if (sf_ctl_dev.irq_num >= 0) { + free_irq(sf_ctl_dev.irq_num, (void *)&sf_ctl_dev); + sf_ctl_dev.irq_num = 0; + } + + misc_deregister(&sf_ctl_dev.miscdev); +#ifdef CONFIG_PM_WAKELOCKS + wakeup_source_trash(&sf_ctl_dev.wakelock); +#else + wake_lock_destroy(&sf_ctl_dev.wakelock); +#endif + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + sf_ctl_dev.pdev = NULL; + } + + return err; +} + +static int sf_probe(sf_device_t *dev) +{ + int err = 0; + xprintk(KERN_INFO, "fortsense %s enter\n", __FUNCTION__); + sf_ctl_dev.pdev = dev; + /* setup spi config */ +#ifdef CONFIG_MTK_SPI + memcpy(&sf_ctl_dev.mt_conf, &smt_conf, sizeof(struct mt_chip_conf)); + sf_ctl_dev.pdev->controller_data = (void *)&sf_ctl_dev.mt_conf; + xprintk(KERN_INFO, "fortsense %s old MTK SPI.\n", __FUNCTION__); +#endif +#if SF_SPI_RW_EN + sf_ctl_dev.pdev->mode = SPI_MODE_0; + sf_ctl_dev.pdev->bits_per_word = 8; + sf_ctl_dev.pdev->max_speed_hz = SF_DEFAULT_SPI_SPEED; + spi_setup(sf_ctl_dev.pdev); +#endif + /* Initialize the platform config. */ + err = sf_platform_init(&sf_ctl_dev); + + if (err) { + sf_ctl_dev.pdev = NULL; + xprintk(KERN_ERR, "sf_platform_init failed with %d.\n", err); + return err; + } + +#ifdef CONFIG_PM_WAKELOCKS + wakeup_source_init(&sf_ctl_dev.wakelock , "sf_wakelock"); +#else + wake_lock_init(&sf_ctl_dev.wakelock, WAKE_LOCK_SUSPEND, "sf_wakelock"); +#endif + /* Initialize the GPIO pins. */ +#if MULTI_HAL_COMPATIBLE + xprintk(KERN_INFO, " do not initialize the GPIO pins. \n"); +#else + err = sf_ctl_dev.gpio_init(&sf_ctl_dev); + + if (err) { + xprintk(KERN_ERR, "gpio_init failed with %d.\n", err); + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + sf_ctl_dev.pdev = NULL; + return err; + } + + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); +#endif +#if SF_PROBE_ID_EN +#if SF_BEANPOD_COMPATIBLE_V2 + err = get_fp_spi_enable(); + + if (err != 1) { + xprintk(KERN_ERR, "get_fp_spi_enable ret=%d\n", err); + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + sf_ctl_dev.pdev = NULL; + return -1; + } + +#endif +#if SF_RONGCARD_COMPATIBLE +#ifdef CONFIG_RSEE + uint64_t vendor_id = 0x00; + sf_ctl_dev.spi_clk_on(true); + err = rsee_client_get_fpid(&vendor_id); + sf_ctl_dev.spi_clk_on(false); + xprintk(KERN_INFO, "rsee_client_get_fpid vendor id is 0x%x\n", vendor_id); + + if (err || !((vendor_id >> 8) == 0x82)) { + xprintk(KERN_ERR, "rsee_client_get_fpid failed !\n"); + err = -1; + } + +#else + err = -1; + xprintk(KERN_INFO, "CONFIG_RSEE not define, skip rsee_client_get_fpid!\n"); +#endif +#else + sf_ctl_dev.spi_clk_on(true); + err = sf_read_sensor_id(); + sf_ctl_dev.spi_clk_on(false); +#endif + + if (err < 0) { + xprintk(KERN_ERR, "fortsense probe read chip id is failed\n"); + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + sf_ctl_dev.pdev = NULL; + return -1; + } + +#if SF_BEANPOD_COMPATIBLE_V2 +#ifndef MAX_TA_NAME_LEN + set_fp_vendor(FP_VENDOR_FORTSENSE); +#else + set_fp_ta_name("fp_server_fortsense", MAX_TA_NAME_LEN); +#endif //MAX_TA_NAME_LEN +#endif //SF_BEANPOD_COMPATIBLE_V2 +#endif //SF_PROBE_ID_EN + /* reset spi dma mode in old MTK. */ +#if (SF_REE_PLATFORM && defined(CONFIG_MTK_SPI)) + { + sf_ctl_dev.mt_conf.com_mod = DMA_TRANSFER; + spi_setup(sf_ctl_dev.pdev); + } +#endif + /* Initialize the input subsystem. */ + err = sf_ctl_init_input(); + + if (err) { + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + sf_ctl_dev.pdev = NULL; + xprintk(KERN_ERR, "sf_ctl_init_input failed with %d.\n", err); + return err; + } + + /* Register as a miscellaneous device. */ + err = misc_register(&sf_ctl_dev.miscdev); + + if (err) { + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + xprintk(KERN_ERR, "misc_register(..) = %d.\n", err); + input_unregister_device(sf_ctl_dev.input); + sf_ctl_dev.pdev = NULL; + return err; + } + + err = sysfs_create_group(&sf_ctl_dev.miscdev.this_device->kobj, &sf_attribute_group); + /* Initialize the interrupt callback. */ + INIT_WORK(&sf_ctl_dev.work_queue, sf_ctl_device_event); +#if MULTI_HAL_COMPATIBLE + xprintk(KERN_INFO, " do not initialize the fingerprint interrupt. \n"); +#else + err = sf_ctl_init_irq(); + + if (err) { + xprintk(KERN_ERR, "sf_ctl_init_irq failed with %d.\n", err); + input_unregister_device(sf_ctl_dev.input); + misc_deregister(&sf_ctl_dev.miscdev); + sf_ctl_dev.free_gpio(&sf_ctl_dev); + sf_platform_exit(&sf_ctl_dev); + sf_ctl_dev.pdev = NULL; + return err; + } + +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND + sf_ctl_dev.early_suspend.level = (EARLY_SUSPEND_LEVEL_DISABLE_FB - 1); + sf_ctl_dev.early_suspend.suspend = sf_early_suspend; + sf_ctl_dev.early_suspend.resume = sf_late_resume; + register_early_suspend(&sf_ctl_dev.early_suspend); +#elif defined(CONFIG_ADF_SPRD) + sf_ctl_dev.adf_event_block.notifier_call = sf_adf_event_handler; + err = adf_register_client(&sf_ctl_dev.adf_event_block); + + if (err < 0) { + xprintk(KERN_ERR, "register adf notifier fail, cannot sleep when screen off"); + } + else { + xprintk(KERN_ERR, "register adf notifier succeed"); + } + +#else + sf_ctl_dev.notifier.notifier_call = sf_fb_notifier_callback; +#if XIAOMI_DRM_INTERFACE_WA + sf_ctl_dev.is_fb_black = false; + INIT_WORK(&sf_ctl_dev.work_drm, sf_drm_notification_work); + drm_register_client(&sf_ctl_dev.notifier); +#else + fb_register_client(&sf_ctl_dev.notifier); +#endif +#endif + /* beanpod ISEE2.7 */ +#if SF_BEANPOD_COMPATIBLE_V2_7 + { + /* fortsense define, flow by trustonic */ + struct TEEC_UUID vendor_uuid = {0x0401c03f, 0xc30c, 0x4dd0, \ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04} \ + }; + memcpy(&uuid_fp, &vendor_uuid, sizeof(struct TEEC_UUID)); + } + xprintk(KERN_ERR, "%s set beanpod isee2.7.0 uuid ok \n", __FUNCTION__); +#endif + xprintk(KERN_ERR, "%s leave\n", __FUNCTION__); + return err; +} + +#if SF_SPI_TRANSFER +static int tee_spi_transfer(void *sf_conf, int cfg_len, const char *txbuf, char *rxbuf, int len) +{ + struct spi_transfer t; + struct spi_message m; + memset(&t, 0, sizeof(t)); + spi_message_init(&m); + t.tx_buf = txbuf; + t.rx_buf = rxbuf; + t.bits_per_word = 8; + t.len = len; +#if QUALCOMM_REE_DEASSERT + t.speed_hz = qualcomm_deassert; +#else + t.speed_hz = sf_ctl_dev.pdev->max_speed_hz; +#endif + spi_message_add_tail(&t, &m); + return spi_sync(sf_ctl_dev.pdev, &m); +} +#endif + +#if SF_REE_PLATFORM +static ssize_t sf_ctl_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + const size_t bufsiz = sf_ctl_dev.spi_buf_size; + ssize_t status = 0; + + /* chipselect only toggles at start or end of operation */ + if (count > bufsiz) { + return (-EMSGSIZE); + } + + if (sf_ctl_dev.spi_buffer == NULL) { + sf_ctl_dev.spi_buffer = kmalloc(bufsiz, GFP_KERNEL); + + if (sf_ctl_dev.spi_buffer == NULL) { + xprintk(KERN_ERR, " %s malloc spi_buffer failed.\n", __FUNCTION__); + return (-ENOMEM); + } + } + + memset(sf_ctl_dev.spi_buffer, 0, bufsiz); + + if (copy_from_user(sf_ctl_dev.spi_buffer, buf, count)) { + xprintk(KERN_ERR, "%s copy_from_user(..) failed.\n", __FUNCTION__); + return (-EMSGSIZE); + } + + { + /* not used */ + void *sf_conf; + int cfg_len = 0; + status = tee_spi_transfer(sf_conf, cfg_len, sf_ctl_dev.spi_buffer, sf_ctl_dev.spi_buffer, count); + } + + if (status == 0) { + status = copy_to_user(buf, sf_ctl_dev.spi_buffer, count); + + if (status != 0) { + status = -EFAULT; + } + else { + status = count; + } + } + else { + xprintk(KERN_ERR, " %s spi_transfer failed.\n", __FUNCTION__); + } + + return status; +} + +static ssize_t sf_ctl_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + const size_t bufsiz = sf_ctl_dev.spi_buf_size; + ssize_t status = 0; + + /* chipselect only toggles at start or end of operation */ + if (count > bufsiz) { + return (-EMSGSIZE); + } + + if (sf_ctl_dev.spi_buffer == NULL) { + sf_ctl_dev.spi_buffer = kmalloc(bufsiz, GFP_KERNEL); + + if (sf_ctl_dev.spi_buffer == NULL) { + xprintk(KERN_ERR, " %s malloc spi_buffer failed.\n", __FUNCTION__); + return (-ENOMEM); + } + } + + memset(sf_ctl_dev.spi_buffer, 0, bufsiz); + + if (copy_from_user(sf_ctl_dev.spi_buffer, buf, count)) { + xprintk(KERN_ERR, "%s copy_from_user(..) failed.\n", __FUNCTION__); + return (-EMSGSIZE); + } + + { + /* not used */ + void *sf_conf; + int cfg_len = 0; + status = tee_spi_transfer(sf_conf, cfg_len, sf_ctl_dev.spi_buffer, sf_ctl_dev.spi_buffer, count); + } + + if (status == 0) { + status = count; + } + else { + xprintk(KERN_ERR, " %s spi_transfer failed.\n", __FUNCTION__); + } + + return status; +} +#endif + +#if (SF_PROBE_ID_EN && !SF_RONGCARD_COMPATIBLE) +static int sf_read_sensor_id(void) +{ + int ret = -1; + int trytimes = 3; + char readbuf[16] = {0}; + char writebuf[16] = {0}; + int cfg_len = 0; + //默认速度设置为1M, 不然8201/8211系列有可能读不到ID +#if (defined(CONFIG_MTK_SPI) || SF_TRUSTKERNEL_COMPAT_SPI_MT65XX) + smt_conf.high_time = SF_DEFAULT_SPI_HIGH_TIME; + smt_conf.low_time = SF_DEFAULT_SPI_LOW_TIME; + smt_conf.com_mod = FIFO_TRANSFER; + smt_conf.cpol = SPI_CPOL_0; // SPI_MODE_0: cpol = 0 and cpha = 0 + smt_conf.cpha = SPI_CPHA_0; +#if (SF_COMPATIBLE_SEL == SF_COMPATIBLE_TRUSTKERNEL) + cfg_len = sizeof(struct mt_chip_conf); +#else + memcpy(&sf_ctl_dev.mt_conf, &smt_conf, sizeof(struct mt_chip_conf)); +#endif +#else + /* not used */ + int smt_conf; +#endif + sf_ctl_dev.pdev->max_speed_hz = SF_DEFAULT_SPI_SPEED; + sf_ctl_dev.pdev->bits_per_word = 8; + sf_ctl_dev.pdev->mode = SPI_MODE_0; + spi_setup(sf_ctl_dev.pdev); + xprintk(SF_LOG_LEVEL, "%s(..) enter.\n", __FUNCTION__); + msleep(10); + + do { + /* 0.under display fingerprint */ + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(50); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + + do { + int ic_type = -1; + char chip_id[2] = {0x00, 0x00}; + writebuf[0] = 0x00; + writebuf[1] = 0x00; + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer address 0x00 = 0x%02x failed, ret = %d\n", readbuf[4], ret); + break; + } + + chip_id[0] = readbuf[4]; + writebuf[0] = 0x00; + writebuf[1] = 0x01; + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer address 0x01 = 0x%02x failed, ret = %d\n", readbuf[4], ret); + break; + } + + chip_id[1] = readbuf[4]; + + if ((chip_id[0] == 0x42) && (chip_id[1] == 0x53)) { + ic_type = 1; + xprintk(KERN_INFO, "%s: found ic_type 1\n", __FUNCTION__); + } + else if ((chip_id[0] == 0x53) && (chip_id[1] == 0x75)) { + ic_type = 2; + xprintk(KERN_INFO, "%s: found ic_type 2\n", __FUNCTION__); + } + else { + ic_type = -1; + xprintk(KERN_ERR, "%s: unknown chip_id = 0x%02x, 0x%02x\n", __FUNCTION__, chip_id[0], chip_id[1]); + break; + } + + if (ic_type == 1) { + // I2C config 0x42 + writebuf[0] = 0x01; + writebuf[1] = 0x42; + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + writebuf[5] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 7); + + if (ret != 0 || readbuf[6] != writebuf[2]) { + xprintk(KERN_ERR, "SPI transfer readbuf[6] = %x failed, ret = %d\n", readbuf[6], ret); + break; + } + + // I2C config 0x43 + writebuf[0] = 0x01; + writebuf[1] = 0x43; + writebuf[2] = 0x3b; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + writebuf[5] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 7); + + if (ret != 0 || readbuf[6] != writebuf[2]) { + xprintk(KERN_ERR, "SPI transfer readbuf[6] = %x failed, ret = %d\n", readbuf[6], ret); + break; + } + + // I2C config 0x40 + writebuf[0] = 0x01; + writebuf[1] = 0x40; + writebuf[2] = 0x80; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + writebuf[5] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 7); + + if (ret != 0 || readbuf[6] != writebuf[2]) { + xprintk(KERN_ERR, "SPI transfer readbuf[6] = %x failed, ret = %d\n", readbuf[6], ret); + break; + } + + // read chip ID 0x31 or 0x32 + writebuf[0] = 0x03; + writebuf[1] = 0x49; + writebuf[2] = 0x31; + writebuf[3] = 0x08; + writebuf[4] = (0x60 | 0x01); + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + msleep(2); + writebuf[0] = 0x00; + writebuf[1] = 0x47; + writebuf[2] = 0x00; + writebuf[3] = 0x00; + ret += tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + + if (ret != 0 || ((readbuf[4] != 0x31) && (readbuf[4] != 0x32))) { + xprintk(KERN_ERR, "SPI transfer readbuf[4] = 0x%02x failed, ret = %d\n", readbuf[4], ret); + break; + } + + xprintk(KERN_INFO, "read under display chip type %d is ok\n", ic_type); + return 0; + } + else { // ic_type == 2 + writebuf[0] = 0xaa; + writebuf[1] = 0x06; + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + writebuf[5] = 0x00; + writebuf[6] = 0x04; + writebuf[7] = 0x31; + writebuf[8] = 0x08; + writebuf[9] = 0x00; + writebuf[10] = 0x01; + writebuf[11] = 0x00; + writebuf[12] = 0x00; + writebuf[13] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 14); + msleep(2); + writebuf[0] = 0xaa; + writebuf[1] = 0x07; + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x00; + writebuf[5] = 0x00; + writebuf[6] = 0x01; + writebuf[7] = 0x00; + writebuf[8] = 0x00; + writebuf[9] = 0x00; + writebuf[10] = 0x00; + ret += tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 11); + + if (ret != 0 || ((readbuf[7] != 0x31) && (readbuf[7] != 0x32))) { + xprintk(KERN_ERR, "SPI transfer readbuf[7] = 0x%02x failed, ret = %d\n", readbuf[7], ret); + break; + } + + xprintk(KERN_INFO, "read under display chip type %d is ok\n", ic_type); + return 0; + } + } + while (0); + + /* 1.detect Fortsense ID */ + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x65; + writebuf[1] = (uint8_t)(~0x65); + writebuf[2] = 0x00; + writebuf[3] = 0x04; + writebuf[4] = 0x20; + writebuf[5] = 0x04; + writebuf[6] = 0x20; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 11); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed\n"); + continue; + } + + xprintk(KERN_INFO, " tee_spi_transfer.readbuf = 0x%02x-%02x-%02x-%02x\n", readbuf[7], readbuf[8], readbuf[9], + readbuf[10]); + + if (((0x53 == readbuf[7]) && (0x75 == readbuf[8]) && (0x6e == readbuf[9]) && (0x57 == readbuf[10])) + || ((0x46 == readbuf[7]) && (0x74 == readbuf[8]) && (0x53 == readbuf[9]) && (0x73 == readbuf[10]))) { + xprintk(KERN_INFO, "read fortsense id is ok\n"); + return 0; + } + + /* 2.detect 8205, 8231, 8241 or 8271 */ + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0xA0; + writebuf[1] = (uint8_t)(~0xA0); + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 6); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed\n"); + continue; + } + + if ((0x53 == readbuf[2]) && (0x75 == readbuf[3]) && (0x6e == readbuf[4]) + && (0x57 == readbuf[5])) { + xprintk(KERN_INFO, "read chip is ok\n"); + return 0; + } + + /* 3.detect 8202, 8205 or 8231 */ + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x60; + writebuf[1] = (uint8_t)(~0x60); + writebuf[2] = 0x28; + writebuf[3] = 0x02; + writebuf[4] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 7); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed\n"); + continue; + } + + if (readbuf[5] == 0x82) { + xprintk(KERN_INFO, "read chip is ok\n"); + return 0; + } + + /* 4.detect 8221 */ + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x60; + writebuf[1] = 0x28; + writebuf[2] = 0x02; + writebuf[3] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 6); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed\n"); + continue; + } + + if (readbuf[4] == 0x82) { + xprintk(KERN_INFO, "read chip is ok\n"); + return 0; + } + + /* trustkernel bug, can not read more than 8 bytes , + #if 1, trustkernel has fix it with 12bytes in version 0.5.2, + show trustkernel version you can see it by cat /proc/tkcore/tkcore_os_version*/ +#if !SF_TRUSTKERNEL_COMPATIBLE + //#if 1 + /* 5.detect 8211 */ + { + int retry_8201 = 0; + + do { + sf_ctl_dev.reset(false); + msleep(1); + sf_ctl_dev.reset(true); + msleep(10); + /* reset 脚拉高后,需在 6ms~26ms 内读 ID,小于 6ms 或者超过 26ms 都读不到 ID */ + msleep(1 + retry_8201 * 2); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x32; + writebuf[1] = (uint8_t)(~0x32); + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x84; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed step 1\n"); + continue; + } + + msleep(1); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x32; + writebuf[1] = (uint8_t)(~0x32); + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x81; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed step 2\n"); + continue; + } + + msleep(5); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x81; + writebuf[1] = (uint8_t)(~0x81); + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x21; + writebuf[5] = 0x00; + writebuf[6] = 0x00; + writebuf[7] = 0x00; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 10); + + if (ret != 0) { + xprintk(KERN_ERR, "SPI transfer failed step 3\n"); + continue; + } + + if (readbuf[8] == 0x82) { + xprintk(KERN_INFO, "read chip is ok: 0x%02x%02x\n", readbuf[8], readbuf[9]); + memset(readbuf, 0, sizeof(readbuf)); + memset(writebuf, 0, sizeof(writebuf)); + writebuf[0] = 0x32; + writebuf[1] = (uint8_t)(~0x32); + writebuf[2] = 0x00; + writebuf[3] = 0x00; + writebuf[4] = 0x83; + ret = tee_spi_transfer(&smt_conf, cfg_len, writebuf, readbuf, 5); + return 0; + } + else { + xprintk(KERN_ERR, "read chip: 0x%02x%02x\n", readbuf[8], readbuf[9]); + } + } + while (retry_8201++ < 10); + } +#endif + } + while (trytimes--); + + return -1; +} + +#endif + +//////////////////////////////////////////////////////////////////////////////// +static int sf_ctl_init_irq(void) +{ + int err = 0; + unsigned long flags = IRQF_TRIGGER_FALLING; // IRQF_TRIGGER_FALLING or IRQF_TRIGGER_RISING +#if !SF_MTK_CPU + flags |= IRQF_ONESHOT; +#if SF_INT_TRIG_HIGH + flags |= IRQF_NO_SUSPEND; +#endif +#endif + xprintk(SF_LOG_LEVEL, "%s(..) enter.\n", __FUNCTION__); + /* Register interrupt callback. */ + err = request_irq(sf_ctl_dev.irq_num, sf_ctl_device_irq, + flags, "sf-irq", NULL); + + if (err) { + xprintk(KERN_ERR, "request_irq(..) = %d.\n", err); + } + + enable_irq_wake(sf_ctl_dev.irq_num); + xprintk(SF_LOG_LEVEL, "%s(..) leave.\n", __FUNCTION__); + return err; +} + +static int sf_ctl_init_input(void) +{ + int err = 0; + xprintk(SF_LOG_LEVEL, "%s(..) enter.\n", __FUNCTION__); + sf_ctl_dev.input = input_allocate_device(); + + if (!sf_ctl_dev.input) { + xprintk(KERN_ERR, "input_allocate_device(..) failed.\n"); + return (-ENOMEM); + } + + sf_ctl_dev.input->name = "uinput-fortsense"; + sf_ctl_dev.input->id.vendor = 0x0666; + sf_ctl_dev.input->id.product = 0x0888; + __set_bit(EV_KEY, sf_ctl_dev.input->evbit ); + __set_bit(KEY_HOME, sf_ctl_dev.input->keybit); + __set_bit(KEY_MENU, sf_ctl_dev.input->keybit); + __set_bit(KEY_BACK, sf_ctl_dev.input->keybit); + __set_bit(XIAOMI_KEYCODE_SINGLE_CLK, sf_ctl_dev.input->keybit); + __set_bit(KEY_ENTER, sf_ctl_dev.input->keybit); + __set_bit(KEY_UP, sf_ctl_dev.input->keybit); + __set_bit(KEY_LEFT, sf_ctl_dev.input->keybit); + __set_bit(KEY_RIGHT, sf_ctl_dev.input->keybit); + __set_bit(KEY_DOWN, sf_ctl_dev.input->keybit); + __set_bit(XIAOMI_KEYCODE_DOUBLE_CLK, sf_ctl_dev.input->keybit); + err = input_register_device(sf_ctl_dev.input); + + if (err) { + xprintk(KERN_ERR, "input_register_device(..) = %d.\n", err); + input_free_device(sf_ctl_dev.input); + sf_ctl_dev.input = NULL; + return (-ENODEV); + } + + xprintk(SF_LOG_LEVEL, "%s(..) leave.\n", __FUNCTION__); + return err; +} + +static int __init sf_ctl_driver_init(void) +{ + int err = 0; + xprintk(KERN_INFO, "'%s' SW_BUS_NAME = %s\n", __FUNCTION__, SW_BUS_NAME); +#if SF_BEANPOD_COMPATIBLE_V1 + uint64_t fp_vendor_id = 0x00; + get_t_device_id(&fp_vendor_id); + xprintk(KERN_INFO, "'%s' fp_vendor_id = 0x%x\n", __FUNCTION__, fp_vendor_id); + + if (fp_vendor_id != 0x02) { + return 0; + } + +#endif +#if SF_SPI_RW_EN + /**register SPI device、driver***/ + spi_register_board_info(spi_board_devs, ARRAY_SIZE(spi_board_devs)); + err = spi_register_driver(&sf_driver); + + if (err < 0) { + xprintk(KERN_ERR, "%s, Failed to register SPI driver.\n", __FUNCTION__); + } + +#else +#if SF_REG_DEVICE_BY_DRIVER + sf_device = platform_device_alloc("fortsense-fp", 0); + + if (sf_device) { + err = platform_device_add(sf_device); + + if (err) { + platform_device_put(sf_device); + sf_device = NULL; + } + } + else { + err = -ENOMEM; + } + +#endif + + if (err) { + xprintk(KERN_ERR, "%s, Failed to register platform device.\n", __FUNCTION__); + } + + err = platform_driver_register(&sf_driver); + + if (err) { + xprintk(KERN_ERR, "%s, Failed to register platform driver.\n", __FUNCTION__); + return -EINVAL; + } + +#endif + xprintk(KERN_INFO, "fortsense fingerprint device control driver registered.\n"); + xprintk(KERN_INFO, "driver version: '%s'.\n", sf_ctl_get_version()); + return err; +} + +static void __exit sf_ctl_driver_exit(void) +{ +#if SF_SPI_RW_EN + spi_unregister_driver(&sf_driver); +#else + platform_driver_unregister(&sf_driver); +#endif +#if SF_REG_DEVICE_BY_DRIVER + + if (sf_device) { + platform_device_unregister(sf_device); + } + +#endif + xprintk(KERN_INFO, "fortsense fingerprint device control driver released.\n"); +} + +module_init(sf_ctl_driver_init); +module_exit(sf_ctl_driver_exit); + +MODULE_DESCRIPTION("The device control driver for Fortsense's fingerprint sensor."); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Fortsense"); + diff --git a/drivers/input/fingerprint/fs_tee/sf_ctl.h b/drivers/input/fingerprint/fs_tee/sf_ctl.h new file mode 100644 index 000000000000..7e13369a45bb --- /dev/null +++ b/drivers/input/fingerprint/fs_tee/sf_ctl.h @@ -0,0 +1,241 @@ +/** + * User space driver API for Fortsense's fingerprint device. + * ATTENTION: Do NOT edit this file unless the corresponding driver changed. + * + * Copyright (C) 2018 Fortsense Corporation. + * Copyright (C) 2021 XiaoMi, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. +**/ + +#ifndef __SF_CTRL_API_H__ +#define __SF_CTRL_API_H__ + +#include "sf_user.h" +#include "sf_spi.h" +#include "linux/version.h" + +#ifdef CONFIG_PM_WAKELOCKS +#include +#else +#include +#endif + +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#elif defined(CONFIG_ADF_SPRD) +//#include +//#include +//#include +//#include +#include