diff --git a/drivers/input/misc/silead_fps_mmi/Android.mk b/drivers/input/misc/silead_fps_mmi/Android.mk new file mode 100644 index 000000000000..e078d6439738 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/Android.mk @@ -0,0 +1,15 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +SILEAD_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +SILEAD_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := silead_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SILEAD_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/silead_fps_mmi/Kbuild b/drivers/input/misc/silead_fps_mmi/Kbuild new file mode 100644 index 000000000000..4a1f8fd9bb75 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/Kbuild @@ -0,0 +1,21 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifdef CONFIG_ARCH_MSM + ccflags-y += -DBSP_SIL_PLAT_QCOM +endif + +ifdef CONFIG_ARCH_QCOM + ccflags-y += -DBSP_SIL_PLAT_QCOM +endif + +ifdef CONFIG_ARCH_SPRD + ccflags-y += -DBSP_SIL_PLAT_SPRD +endif + +silead_fps_mmi-y += silead_fp.o + + +obj-m += silead_fps_mmi.o + diff --git a/drivers/input/misc/silead_fps_mmi/Makefile b/drivers/input/misc/silead_fps_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp.c b/drivers/input/misc/silead_fps_mmi/silead_fp.c new file mode 100644 index 000000000000..e1e35542d034 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp.c @@ -0,0 +1,1543 @@ +/* + * @file silead_fp_platform.c + * @brief Contains silead_fp device implementation. + * + * + * Copyright 2016-2021 Gigadevice/Silead Inc. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/4/2 0.1.6 Init version + * Bill Yu 2018/5/2 0.1.7 Fix compile error for some platform + * Bill Yu 2018/5/20 0.1.8 Default wait 3ms after reset + * Bill Yu 2018/5/28 0.1.9 Support poll/read if netlink id = 0 + * Bill Yu 2018/6/1 0.2.0 Support wakelock + * Bill Yu 2018/6/5 0.2.1 Support chip enter power down + * Bill Yu 2018/6/7 0.2.2 Support create proc node + * Bill Yu 2018/6/27 0.2.3 Expand pwdn I/F + * Bill Yu 2018/8/5 0.2.4 Support TP Up/Down I/F + * Bill Yu 2018/12/5 0.2.5 Add Spreadtrum platform support + * Melvin 2019/03/18 0.2.6 Support dynamci spi + * Taobb 2019/4/8 0.2.7 Support qcom early event + * Taobb 2019/6/6 0.2.8 Support pin irq func change to pin reset + * Bill Yu 2019/8/10 0.2.9 Fix crash while parse dts fail + * Bill Yu 2019/8/20 0.3.0 Fix crash while g_fp_dev is invalid after resource deinit called. + * Rui Wu 2019/10/10 0.3.1 Support create class node + * Bill Yu 2020/5/11 0.3.2 Fix compile warning + * Bill Yu 2020/5/29 0.3.3 Adjust poll/read code + * Bill Yu 2020/6/8 0.3.4 Fix poll/read return unpredict value if dev_reinit + * Bill Yu 2020/12/16 0.3.5 Allow GPIO ID number to be zero + * Melvin cao 2021/01/29 0.3.6 Support combo spi+silead_fp drivers + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) +#include +#endif +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#else +#include +#endif + +#include "silead_fp.h" + +#define FP_DEV_NAME "silead_fp" +#define FP_DEV_MAJOR 0 /* assigned */ + +#define FP_CLASS_NAME "silead_fp" +#define FP_INPUT_NAME "fp-keys" + +#define FP_DEV_VERSION "v0.3.6" + +#define BSP_SIL_IRQ_ASYNC /* IRQ use asynchrous mode. */ + +#ifndef BSP_SIL_NETLINK +struct silfp_msg_list { + unsigned char msg; + struct list_head list; +}; +#endif /* !BSP_SIL_NETLINK */ + +struct silfp_data { + dev_t devt; + struct cdev cdev; + spinlock_t spi_lock; + struct platform_device *spi; + struct list_head device_entry; + + unsigned users; + + struct device *dev; + int ref; + + struct input_dev *input; + + spinlock_t irq_lock; + int int_port; + int irq; + s32 irq_is_disable; + int irq_no_use; + int irq_ignore; + s32 power_is_off; + int rst_port; + struct work_struct work; + struct completion done; + struct wake_lock wakelock; + struct wake_lock wakelock_hal; + +#ifdef BSP_SIL_NETLINK + /* for netlink use */ + struct sock *nl_sk; +#else + spinlock_t read_lock; + wait_queue_head_t read_queue; + struct list_head msg_q; +#endif + struct fp_dev_kmap_t keymap_cust; + + int scr_off; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend es; +#else + struct notifier_block notif; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + /* for power supply */ +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + struct regulator *avdd_ldo; + struct regulator *vddio_ldo; +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + int avdd_port; + int vddio_port; +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ +#ifdef PROC_NODE + struct proc_dir_entry *proc_root; + struct proc_dir_entry *proc_entry; +#endif /* PROC_NODE */ + + /* for spi enable/disable */ + atomic_t spionoff_count; + + struct fp_plat_t pin; + + atomic_t init; +}; + +typedef enum _fp_spi_speet_t { + SPEED_1M=1*1000*1000, + SPEED_LOW = SPEED_1M, + SPEED_5M=5*1000*1000, + SPEED_MEDIUM = SPEED_5M, + SPEED_8M=8*1000*1000, + SPEED_9M=9*1000*1000, + SPEED_HIGH=SPEED_8M, + SPEED_10M=10*1000*1000, +} fp_spi_speet_t ; + +static struct fp_dev_init_t silfp_dev_init_d = { + .mode = 0, + .bits = 8, + .speed = SPEED_HIGH, + .delay = 100, + .dev = DEVICE, + .nl_id = SIFP_NETLINK_ROUTE, + .dev_id = 0, +}; + +/* on some platform, home key has been redefine to KEY_HOMEPAGE! */ +/* double check the define on customer board!!! + out/target/product/.../system/usr/keylayout/Generic.kl + kernel/include/uapi/linux/input.h + + KEY_HOMEPAGE + KEY_HOME + KEY_MENU + KEY_BACK + KEY_POWER + KEY_CAMERA + KEY_VOLUMEUP + KEY_VOLUMEDOWN */ + +typedef struct _key_map { + int key_orig; + int key_new; +} nav_keymap_t; + +static nav_keymap_t keymap[] = { + { NAV_KEY_UP, KEY_UP, }, /* KEY_RESERVED, ignore this key */ + { NAV_KEY_DOWN, KEY_DOWN, }, + { NAV_KEY_RIGHT, KEY_RIGHT, }, + { NAV_KEY_LEFT, KEY_LEFT, }, + { NAV_KEY_CLICK, KEY_HOMEPAGE, }, + { NAV_KEY_DCLICK, KEY_HOMEPAGE, }, + { NAV_KEY_LONGPRESS,KEY_HOMEPAGE, }, +}; + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +#ifdef BSP_SIL_NETLINK +static int pid; +#endif /* BSP_SIL_NETLINK */ + +#ifdef PROC_NODE +static char vendor_name[PROC_VND_ID_LEN]; +#endif /* PROC_NODE */ + +struct class *silfp_class; + +static struct workqueue_struct *silfp_wq; +struct silfp_data *g_fp_dev = NULL; + +static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay); +static void silfp_irq_disable(struct silfp_data *fp_dev); +static void silfp_irq_enable(struct silfp_data *fp_dev); +static int silfp_wait_irq(struct silfp_data *fp_dev); +static int silfp_irq_status(struct silfp_data *fp_dev); +static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info); +static int silfp_resource_deinit(struct silfp_data *fp_dev); +static void silfp_power_deinit(struct silfp_data *fp_dev); +static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd); +static void silfp_hw_poweron(struct silfp_data *fp_dev); +static int silfp_set_feature(struct silfp_data *fp_dev, u8 feature); + +/* debug log level */ +#ifndef BSP_SIL_DYNAMIC_SPI +static +#endif +fp_debug_level_t sil_debug_level = INFO_LOG; + +#include PLAT_H + +/* -------------------------------------------------------------------- */ +/* netlink functions */ +/* -------------------------------------------------------------------- */ +#ifdef BSP_SIL_NETLINK +static void silfp_netlink_send(struct silfp_data *fp_dev, const int cmd) +{ + struct nlmsghdr *nlh = NULL; + struct sk_buff *skb = NULL; + int ret; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] send cmd %d\n", __func__, cmd); + if (!fp_dev || !fp_dev->nl_sk) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] invalid socket\n", __func__); + return; + } + + if (!pid) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] invalid PID\n", __func__); + return; + } + + /*alloc data buffer for sending to native*/ + /*malloc data space at least 1500 bytes, which is ethernet data length*/ + skb = alloc_skb(NL_MSG_LEN, GFP_ATOMIC); + if (!skb) { + return; + } + + nlh = nlmsg_put(skb, 0, 0, 0, NL_MSG_LEN, 0); + if (!nlh) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] nlmsg_put() failed\n", __func__); + kfree_skb(skb); + return; + } + + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + + *(char *)NLMSG_DATA(nlh) = cmd; + ret = netlink_unicast(fp_dev->nl_sk, skb, pid, MSG_DONTWAIT); + if (!ret) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] send failed\n", __func__); + kfree_skb(skb); + return; + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] sent, len=%d\n", __func__, ret); +} + +static void silfp_netlink_recv(struct sk_buff *__skb) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + char str[128]; + + skb = skb_get(__skb); + if (!skb ) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] skb NULL\n", __func__); + return; + } + + /* presume there is 5byte payload at leaset */ + if (skb->len >= NLMSG_SPACE(0)) { + nlh = nlmsg_hdr(skb); + memcpy(str, NLMSG_DATA(nlh), sizeof(str)); + if ((strlen(str) == strlen("FP")) && !strncmp(str,"FP", strlen(str))) { + pid = nlh->nlmsg_pid; + LOG_MSG_DEBUG(ERR_LOG, "[%s] PID=%d, msg=%s\n", __func__, pid, str); + } else { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Unknown PID=%d, msg=%s\n", __func__, nlh->nlmsg_pid, str); + } + } else { + LOG_MSG_DEBUG(ERR_LOG, "[%s] data len incorrect\n", __func__); + } + + kfree_skb(skb); +} + +static int silfp_netlink_init(struct silfp_data *fp_dev) +{ + struct netlink_kernel_cfg cfg; + + memset(&cfg, 0, sizeof(struct netlink_kernel_cfg)); + cfg.input = silfp_netlink_recv; + + fp_dev->nl_sk = netlink_kernel_create(&init_net, SIFP_NETLINK_ROUTE, &cfg); + if (!fp_dev->nl_sk) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] netlink create failed\n", __func__); + return -1; + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] netlink create success\n", __func__); + return 0; +} + +static int silfp_netlink_reinit(struct silfp_data *fp_dev) +{ + return 0; +} + +static int silfp_netlink_destroy(struct silfp_data *fp_dev) +{ + if (fp_dev->nl_sk != NULL) { + netlink_kernel_release(fp_dev->nl_sk); + fp_dev->nl_sk = NULL; + return 0; + } + + LOG_MSG_DEBUG(ERR_LOG, "[%s] no netlink socket\n", __func__); + return -1; +} +#else +static void silfp_netlink_send(struct silfp_data *fp_dev, const int cmd) +{ + unsigned long flags; + struct silfp_msg_list *list; + + if (!fp_dev) { + return; + } + + list = kzalloc(sizeof(*list), GFP_ATOMIC); + if (!list) { + return; + } + + list->msg = (unsigned char)cmd; + + LOG_MSG_DEBUG(INFO_LOG, "silfp_poll cmd %d\n", cmd); + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_add_tail(&list->list, &fp_dev->msg_q); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + wake_up_interruptible(&fp_dev->read_queue); +} + +static int silfp_netlink_init(struct silfp_data *fp_dev) +{ + spin_lock_init(&fp_dev->read_lock); + init_waitqueue_head(&fp_dev->read_queue); + INIT_LIST_HEAD(&fp_dev->msg_q); + + return 0; +} + +static int silfp_netlink_destroy(struct silfp_data *fp_dev) +{ + struct silfp_msg_list *list, *next; + unsigned long flags; + + if (fp_dev && (!list_empty(&fp_dev->msg_q))) { + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_for_each_entry_safe(list, next, &fp_dev->msg_q, list) { + list_del(&list->list); + kfree(list); + } + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + } + return 0; +} + +static int silfp_netlink_reinit(struct silfp_data *fp_dev) +{ + // Free un-read messages + return silfp_netlink_destroy(fp_dev); +} + +static unsigned int silfp_poll(struct file *fd, poll_table *wait) +{ + struct silfp_data *fp_dev; + unsigned int mask = 0; + + if (!fd) { + return -EINVAL; + } + + fp_dev = fd->private_data; + poll_wait(fd, &fp_dev->read_queue, wait); + + if (!list_empty(&fp_dev->msg_q)) { + mask |= POLLIN | POLLRDNORM; + } + + return mask; +} + +static ssize_t silfp_read(struct file *fd, char __user *buf, size_t len,loff_t *ptr) +{ + struct silfp_data *fp_dev; + struct silfp_msg_list *list; + unsigned long flags; + int32_t msglen = sizeof(char); + + if (!fd || !buf || (len < msglen)) { + return -EINVAL; + } + + fp_dev = fd->private_data; + /*if (!list_empty(&fp_dev->msg_q)) { + return -EINVAL; + }*/ + + spin_lock_irqsave(&fp_dev->read_lock, flags); + while(list_empty(&fp_dev->msg_q)) { + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + if (wait_event_interruptible(fp_dev->read_queue, !list_empty(&fp_dev->msg_q)) < 0) { + return -EINVAL; + } + spin_lock_irqsave(&fp_dev->read_lock, flags); + } + /* pick the first one */ + list = list_first_entry(&fp_dev->msg_q, + struct silfp_msg_list, list); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + if (!list) { + return -EFAULT; + } + + if (copy_to_user(buf, &list->msg, msglen)) { + LOG_MSG_DEBUG(ERR_LOG, "copy_to fail\n"); + msglen = -EFAULT; + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s] %d\n", __func__, list->msg); + } + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_del(&list->list); + kfree(list); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + return msglen; +} +#endif /* BSP_SIL_NETLINK */ + + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void silfp_early_suspend(struct early_suspend *es) +{ + struct silfp_data *fp_dev = container_of(es, struct silfp_data, es); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + fp_dev->scr_off = 1; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_OFF); +} + +static void silfp_late_resume(struct early_suspend *es) +{ + struct silfp_data *fp_dev = container_of(es, struct silfp_data, es); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + fp_dev->scr_off = 0; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_ON); +} +#else + +static int silfp_fb_callback(struct notifier_block *notif, + unsigned long event, void *data) +{ + struct silfp_data *fp_dev = container_of(notif, struct silfp_data, notif); + struct fb_event *evdata = data; + unsigned int blank; + int retval = 0; + + /* If we aren't interested in this event, skip it immediately ... */ + if (event != SIL_EVENT_BLANK /* FB_EARLY_EVENT_BLANK */) { + return 0; + } + + blank = *(int *)evdata->data; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, blank=0x%x\n", __func__, blank); + + switch (blank) { + case SIL_EVENT_UNBLANK: + LOG_MSG_DEBUG(INFO_LOG, "[%s] LCD ON\n", __func__); + fp_dev->scr_off = 0; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_ON); + break; + + case SIL_EVENT_POWERDOWN: + LOG_MSG_DEBUG(INFO_LOG, "[%s] LCD OFF\n", __func__); + fp_dev->scr_off = 1; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_OFF); + break; + + default: + LOG_MSG_DEBUG(INFO_LOG, "[%s] Unknown notifier\n", __func__); + break; + } + return retval; +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +/* -------------------------------------------------------------------- */ +/* IRQ related functions */ +/* -------------------------------------------------------------------- */ +static void silfp_irq_disable(struct silfp_data *fp_dev) +{ + unsigned long irqflags; + + spin_lock_irqsave(&fp_dev->irq_lock, irqflags); + if (!fp_dev->irq_is_disable) { + fp_dev->irq_is_disable = 1; + disable_irq_nosync(fp_dev->irq); + LOG_MSG_DEBUG(DBG_LOG, "[%s] irq disabled\n", __func__); + } + spin_unlock_irqrestore(&fp_dev->irq_lock, irqflags); +} + +static void silfp_irq_enable(struct silfp_data *fp_dev) +{ + unsigned long irqflags = 0; + + spin_lock_irqsave(&fp_dev->irq_lock, irqflags); + if (fp_dev->irq_is_disable) { + enable_irq(fp_dev->irq); + fp_dev->irq_is_disable = 0; + reinit_completion(&fp_dev->done); + LOG_MSG_DEBUG(DBG_LOG, "[%s] irq enabled\n", __func__); + } + spin_unlock_irqrestore(&fp_dev->irq_lock, irqflags); +} + +static int silfp_wait_irq(struct silfp_data *fp_dev) +{ + // this function is obsolete, for test purpose only. + wait_for_completion(&fp_dev->done); + //return wait_for_completion_timeout(&fp_dev->done, msecs_to_jiffies(3000)); + return 1; +} + +static int silfp_irq_status(struct silfp_data *fp_dev) +{ + // this function is obsolete, for test purpose only. + if (fp_dev->int_port >= 0) { + return gpio_get_value(fp_dev->int_port); + } + return -1; +} + +static irqreturn_t silfp_irq_handler(int irq, void *dev_id) +{ + struct silfp_data *fp_dev = (struct silfp_data *)dev_id; + + if (fp_dev->irq_ignore) { + return IRQ_HANDLED; + } + +#ifdef BSP_SIL_IRQ_CONFIRM + if (gpio_get_value(fp_dev->int_port)) { +#else + if (true) { +#endif /* BSP_SIL_IRQ_CONFIRM */ + wake_lock_timeout(&fp_dev->wakelock, 10*HZ); /* set a little long for a poor MCU */ +#ifdef BSP_SIL_IRQ_ASYNC + queue_work(silfp_wq, &fp_dev->work); +#else + silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); +#endif /* BSP_SIL_IRQ_ASYNC */ + complete(&fp_dev->done); + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq ignore\n", __func__); + } + + return IRQ_HANDLED; +} + +static void silfp_work_func(struct work_struct *work) +{ + struct silfp_data *fp_dev = container_of(work, struct silfp_data, work); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] running\n", __func__); + silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); +} + +/* -------------------------------------------------------------------- */ +/* key event functions */ +/* -------------------------------------------------------------------- */ +static int silfp_keyevent(struct silfp_data *fp_dev, struct fp_dev_key_t *pkey) +{ + int ret = -EFAULT; + int i; + + //LOG_MSG_DEBUG(INFO_LOG, "[%s] key %d, flag %d\n", __func__,pkey->value,pkey->flag); + if (!fp_dev->input) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] invalid input device\n",__func__); + return -1; + } + if (IS_KEY_VALID(pkey->value)) { + /* Translate Click Down/Up key to Click key. */ + switch( pkey->value ) { + case NAV_KEY_CLICK_DOWN: + pkey->value = NAV_KEY_CLICK; + pkey->flag = NAV_KEY_FLAG_DOWN; + break; + case NAV_KEY_CLICK_UP: + pkey->value = NAV_KEY_CLICK; + pkey->flag = NAV_KEY_FLAG_UP; + break; + default: + break; + } + + /* Check the custom define keymap */ + if (fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] custom-key %d\n", __func__,fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]); + if (KEY_RESERVED != fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]) { + if (NAV_KEY_FLAG_CLICK == pkey->flag) { + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], NAV_KEY_FLAG_DOWN); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], NAV_KEY_FLAG_UP); + input_sync(fp_dev->input); + } else { + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], pkey->flag); + input_sync(fp_dev->input); + } + } else { + // Here means this key is not set, simply ignore it. + } + ret = 0; + } + } + + for (i = 0; ret && i < ARRAY_SIZE(keymap); i++) { + if (keymap[i].key_orig == pkey->value) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] key %d\n", __func__,keymap[i].key_new); + if (KEY_RESERVED != keymap[i].key_new) { + if (NAV_KEY_FLAG_CLICK == pkey->flag) { + input_report_key(fp_dev->input, keymap[i].key_new, NAV_KEY_FLAG_DOWN); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, keymap[i].key_new, NAV_KEY_FLAG_UP); + input_sync(fp_dev->input); + } else { + input_report_key(fp_dev->input, keymap[i].key_new, pkey->flag); + input_sync(fp_dev->input); + } + } else { + // Here means this key is not set, simply ignore it. + } + ret = 0; + } + } + + if (ret) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] unregister custom-key %d\n", __func__,pkey->value); + input_report_key(fp_dev->input, pkey->value, pkey->flag); + input_sync(fp_dev->input); + ret = 0; + } + return ret; +} + +/* -------------------------------------------------------------------- */ +/* proc node functions */ +/* -------------------------------------------------------------------- */ +#ifdef PROC_NODE +static int silfp_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s\n", vendor_name); + return 0; +} + +static int silfp_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, silfp_proc_show, NULL); +} + +static const struct file_operations silfp_proc_fops = { + .owner = THIS_MODULE, + .read = seq_read, + .open = silfp_proc_open, + .release = single_release, +}; + +static int silfp_proc_create_node(struct silfp_data *fp_dev) +{ + if (!fp_dev->proc_entry) { + fp_dev->proc_root = NULL; +#ifdef PROC_DIR + fp_dev->proc_root = proc_mkdir(PROC_DIR, NULL); + if (!fp_dev->proc_root) { + LOG_MSG_DEBUG(ERR_LOG, "Create dir %s under /proc error!\n", PROC_DIR); + goto err_out; + } +#endif /* PROC_DIR */ + fp_dev->proc_entry = proc_create(PROC_NODE, 0666, fp_dev->proc_root, &silfp_proc_fops); + if (!fp_dev->proc_entry) { + LOG_MSG_DEBUG(ERR_LOG, "Create entry %s under /proc error!\n", PROC_NODE); + goto err_out1; + } + } + + return 0; + +err_out1: + remove_proc_entry(PROC_NODE, fp_dev->proc_root); +#ifdef PROC_DIR + remove_proc_entry(PROC_DIR, NULL); +err_out: +#endif /* PROC_DIR */ + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + return -ENOMEM; +} + +static int silfp_proc_init(struct silfp_data *fp_dev) +{ + if (fp_dev) { + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + } + return 0; + +} + +static void silfp_proc_deinit(struct silfp_data *fp_dev) +{ + if (fp_dev) { + if (fp_dev->proc_entry) { + remove_proc_entry(PROC_NODE, fp_dev->proc_root); + } +#ifdef PROC_DIR + if (fp_dev->proc_root) { + remove_proc_entry(PROC_DIR, NULL); + } +#endif /* PROC_DIR */ + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + } +} +#endif /* PROC_NODE */ + + +/* -------------------------------------------------------------------- */ +/* class node functions */ +/* -------------------------------------------------------------------- */ +#ifdef CLASS_NODE +#define CLASS_NODE_NAME adm +static struct class *fp_cls = NULL; + +static ssize_t silfp_class_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 1); +} + +static DEVICE_ATTR(CLASS_NODE_NAME, S_IRUSR|S_IRGRP|S_IROTH, silfp_class_show, NULL); + +struct file_operations silfp_class_node_ops = { + .owner = THIS_MODULE, +}; + +static int silfp_class_init(void) +{ + int ret = 0; + struct device *silfp_class_device; + fp_cls = class_create(THIS_MODULE, CLASS_NODE); + + if (IS_ERR(fp_cls)) { + LOG_MSG_DEBUG(ERR_LOG, "Failed to create class %s!\n", CLASS_NODE); + return ENOMEM; + } + silfp_class_device = device_create(fp_cls, NULL, 0, NULL, CLASS_NODE); + if (IS_ERR(silfp_class_device)) { + LOG_MSG_DEBUG(ERR_LOG, "Failed to create class device %s!\n", CLASS_NODE); + return ENOMEM; + } + ret = sysfs_create_file(&(silfp_class_device->kobj), &dev_attr_CLASS_NODE_NAME.attr); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "Failed to create class device file!\n"); + } + return ret; +} + +static void silfp_class_deinit(void) +{ + if (fp_cls) { + device_destroy(fp_cls, 0); + class_destroy(fp_cls); + fp_cls = NULL; + } +} +#endif /* CLASS_NODE */ + +/* -------------------------------------------------------------------- */ +/* init/deinit functions */ +/* -------------------------------------------------------------------- */ +static int silfp_input_init(struct silfp_data *fp_dev) +{ + int i, status = 0; + + /*register device within input system.*/ + fp_dev->input = input_allocate_device(); + if (!fp_dev->input) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] input_allocate_device() fail!\n", __func__); + status = -ENOMEM; + return status; + } + + __set_bit(EV_KEY, fp_dev->input->evbit); + //__set_bit(KEY_Q, fp_dev->input->keybit); // it will cause Android think this is a physical keyboard. + __set_bit(KEY_HOME, fp_dev->input->keybit); + __set_bit(KEY_HOMEPAGE, fp_dev->input->keybit); + + __set_bit(KEY_MENU, fp_dev->input->keybit); + __set_bit(KEY_BACK, fp_dev->input->keybit); + __set_bit(KEY_CAMERA, fp_dev->input->keybit); + + for (i = 0; i < ARRAY_SIZE(keymap); i++) { + if (keymap[i].key_new != KEY_RESERVED) { + __set_bit(keymap[i].key_new, fp_dev->input->keybit); + } + } + + for (i = 0; i < ARRAY_SIZE(fp_dev->keymap_cust.k); i++) { + if (fp_dev->keymap_cust.k[i] && (fp_dev->keymap_cust.k[i] != KEY_RESERVED)) { + __set_bit(fp_dev->keymap_cust.k[i], fp_dev->input->keybit); + } + } + + fp_dev->input->name = FP_INPUT_NAME; + if (input_register_device(fp_dev->input)) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] input_register_device() fail!\n", __func__); + input_free_device(fp_dev->input); + fp_dev->input = NULL; + status = -ENODEV; + } + return status; +} + +static int silfp_input_deinit(struct silfp_data *fp_dev) +{ + if (fp_dev->input) { + input_unregister_device(fp_dev->input); + fp_dev->input = NULL; + } + return 0; +} + +static int silfp_input_reinit(struct silfp_data *fp_dev) +{ + if (fp_dev->input) { + silfp_input_deinit(fp_dev); + } + return silfp_input_init(fp_dev); +} + +static int silfp_init(struct silfp_data *fp_dev) +{ + int status = 0; + + init_completion(&fp_dev->done); + spin_lock_init(&fp_dev->irq_lock); + INIT_WORK(&fp_dev->work, silfp_work_func); + + /* netlink interface init */ + status = silfp_netlink_init(fp_dev); + if (status == -1) { + return status; + } + +#if defined(CONFIG_HAS_EARLYSUSPEND) + LOG_MSG_DEBUG(INFO_LOG, "[%s] register_early_suspend\n", __func__); + fp_dev->es.level = (EARLY_SUSPEND_LEVEL_DISABLE_FB - 1); + fp_dev->es.suspend = silfp_early_suspend; + fp_dev->es.resume = silfp_late_resume; + register_early_suspend(&fp_dev->es); +#else + /* register screen on/off callback */ + fp_dev->notif.notifier_call = silfp_fb_callback; + SIL_REGISTER_CLIENT(&fp_dev->notif); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + atomic_set(&fp_dev->spionoff_count,0); + LOG_MSG_DEBUG(INFO_LOG, "[%s] success\n", __func__); + + return status; +} + +static int silfp_resource_deinit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + + if (atomic_read(&fp_dev->init)) { + atomic_dec(&fp_dev->init); + + if (!atomic_read(&fp_dev->init)) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] no more users, free GPIOs\n", __func__); + if (!fp_dev->irq_no_use) { + silfp_irq_disable(fp_dev); + free_irq(fp_dev->irq, fp_dev); + } + + gpio_direction_input(fp_dev->int_port); +#if !defined(BSP_SIL_PLAT_MTK) + gpio_free(fp_dev->int_port); + if (fp_dev->rst_port >= 0) { + gpio_free(fp_dev->rst_port); + } +#endif /* !BSP_SIL_PLAT_MTK */ + fp_dev->irq_no_use = 0; + fp_dev->int_port = -1; + fp_dev->rst_port = -1; + + silfp_input_deinit(fp_dev); + silfp_power_deinit(fp_dev); +#ifdef PROC_NODE + silfp_proc_deinit(fp_dev); +#endif /* PROC_NODE */ +#ifdef CLASS_NODE + silfp_class_deinit(); +#endif /* CLASS_NODE */ + } + silfp_netlink_send(fp_dev, SIFP_NETLINK_DISCONNECT); + } + return 0; +} + +static void silfp_exit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (fp_dev->es.suspend) { + unregister_early_suspend(&fp_dev->es); + } +#else + fb_unregister_client(&fp_dev->notif); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + //silfp_set_spi(fp_dev, false); /* release SPI resources */ + + if (silfp_wq) { + destroy_workqueue(silfp_wq); + silfp_wq = NULL; + } + silfp_netlink_destroy(fp_dev); +#ifdef PROC_NODE + silfp_proc_deinit(fp_dev); +#endif /* PROC_NODE */ +#ifdef CLASS_NODE + silfp_class_deinit(); +#endif /* CLASS_NODE */ +} + +static void silfp_wakelock_ctl(struct silfp_data *fp_dev, unsigned char lock) +{ + if (lock) { + wake_lock_timeout(&fp_dev->wakelock_hal, 10*HZ); + } else { + wake_unlock(&fp_dev->wakelock_hal); + } +} +/*-------------------------------------------------------------------------*/ + +static long +silfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct silfp_data *fp_dev; + struct fp_dev_key_t key; + unsigned char delay; + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != SIFP_IOC_MAGIC) { + return -ENOTTY; + } + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + fp_dev = filp->private_data; + + switch (cmd) { + case SIFP_IOC_INIT: + silfp_netlink_reinit(fp_dev); + silfp_resource_init(fp_dev,&silfp_dev_init_d); + if (copy_to_user((void __user *)arg, (void *)&silfp_dev_init_d, sizeof(struct fp_dev_init_t))) { + retval = -EFAULT; + } + g_fp_dev = fp_dev; + + break; + case SIFP_IOC_DEINIT: + g_fp_dev = NULL; + silfp_resource_deinit(fp_dev); + break; + + case SIFP_IOC_RESET: + delay = RESET_TIME; + LOG_MSG_DEBUG(INFO_LOG, "[%s] chip reset\n", __func__); + if (arg) { + if (copy_from_user(&delay, (void __user *)arg, sizeof(char))) { + retval = -EFAULT; + break; + } + } + + if (fp_dev->power_is_off) { + silfp_hw_poweron(fp_dev); + mdelay(5); + } + fp_dev->irq_ignore = 1; + if (fp_dev->irq_is_disable && !fp_dev->irq_no_use) { + silfp_irq_enable(fp_dev); + silfp_hw_reset(fp_dev, delay); + silfp_irq_disable(fp_dev); + } else { + silfp_hw_reset(fp_dev, delay); + } + fp_dev->irq_ignore = 0; + break; + + case SIFP_IOC_ENABLE_IRQ: + LOG_MSG_DEBUG(DBG_LOG, "[%s] enable irq\n", __func__); + silfp_irq_enable(fp_dev); + break; + + case SIFP_IOC_DISABLE_IRQ: + LOG_MSG_DEBUG(DBG_LOG, "[%s] disable irq\n", __func__); + silfp_irq_disable(fp_dev); + break; + + case SIFP_IOC_CLR_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] clear irq\n", __func__); + reinit_completion(&fp_dev->done); + break; + + case SIFP_IOC_WAIT_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] wait irq\n", __func__); + retval = __put_user(silfp_wait_irq(fp_dev)?1:0, (__u8 __user *)arg); + break; + + case SIFP_IOC_IRQ_STATUS: + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq status\n", __func__); + retval = __put_user((char)silfp_irq_status(fp_dev), (__u8 __user *)arg); + break; + + case SIFP_IOC_KEY_EVENT: + if (copy_from_user(&key, (struct fp_dev_key_t *)arg, sizeof(struct fp_dev_key_t))) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] copy key fail?\n",__func__); + retval = -EFAULT; + } else { + retval = silfp_keyevent(fp_dev,&key); + } + break; + + case SIFP_IOC_SCR_STATUS: + if (arg) { + LOG_MSG_DEBUG(INFO_LOG, "[IOC_SCR_STATUS] put v = %d\n",(u8)(!fp_dev->scr_off)); + retval = __put_user((u8)(!fp_dev->scr_off), (__u8 __user *)arg); + } else { + LOG_MSG_DEBUG(INFO_LOG, "[IOC_SCR_STATUS] send v = %d\n",(u8)(!fp_dev->scr_off)); + silfp_netlink_send(fp_dev, fp_dev->scr_off?SIFP_NETLINK_SCR_OFF:SIFP_NETLINK_SCR_ON); + } + break; + + case SIFP_IOC_GET_VER: + if (copy_to_user((void __user *)arg, (void *)FP_DEV_VERSION, sizeof(char)*7)) { + LOG_MSG_DEBUG(ERR_LOG, "[IOC_GET_VER] copy_to fail\n"); + retval = -EFAULT; + } + break; + + case SIFP_IOC_SET_KMAP: + if (!arg) { + retval = -EFAULT; + break; + } + if (copy_from_user(&fp_dev->keymap_cust.k, (void __user *)arg, sizeof(struct fp_dev_kmap_t))) { + LOG_MSG_DEBUG(ERR_LOG, "[IOC_SET_KMAP] copy_from fail\n"); + retval = -EFAULT; + break; + } + + if (silfp_input_reinit(fp_dev)) { + retval = -EFAULT; + } + break; + + case SIFP_IOC_ACQ_SPI: + LOG_MSG_DEBUG(INFO_LOG, "[%s] acq spi\n", __func__); + retval = silfp_set_spi(fp_dev,true); + break; + + case SIFP_IOC_RLS_SPI: + LOG_MSG_DEBUG(INFO_LOG, "[%s] release spi\n", __func__); + retval = silfp_set_spi(fp_dev,false); + break; + + case SIFP_IOC_PKG_SIZE: + if (arg) { + LOG_MSG_DEBUG(DBG_LOG, "[IOC_PKG_SIZE] %d\n",(u8)(PKG_SIZE)); + retval = __put_user((u8)(PKG_SIZE), (__u8 __user *)arg); + } + break; + + case SIFP_IOC_DBG_LEVEL: + if (arg) { + unsigned char level = 0; + if (copy_from_user(&level, (void __user *)arg, sizeof(char)) || level > (uint8_t)ALL_LOG ) { + retval = -EFAULT; + break; + } + LOG_MSG_DEBUG(ERR_LOG, "debug level %d-->%d\n",sil_debug_level,level); + retval = __put_user((u8)(sil_debug_level), (__u8 __user *)arg); + sil_debug_level = (fp_debug_level_t)level; + } + break; + + case SIFP_IOC_WAKELOCK: + if (arg) { + unsigned char lock = 0; + if (copy_from_user(&lock, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_WAKELOCK] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_wakelock_ctl(fp_dev, lock); + } + break; + + case SIFP_IOC_PWDN: + if (arg) { + unsigned char flag_avdd = 0; + if (copy_from_user(&flag_avdd, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_PWDN] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_pwdn(fp_dev, flag_avdd); + } else { + silfp_pwdn(fp_dev, SIFP_PWDN_NONE); + } + + break; + +#if defined(PROC_NODE) || defined(CLASS_NODE) + case SIFP_IOC_PROC_NODE: +#ifdef PROC_NODE + if (arg) { + if (copy_from_user(vendor_name, (void __user *)arg, PROC_VND_ID_LEN)) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_PROC_NODE] copy_from fail\n"); + retval = -EFAULT; + break; + } + retval = silfp_proc_create_node(fp_dev); + } +#endif /* PROC_NODE */ +#ifdef CLASS_NODE + retval = silfp_class_init(); +#endif /* CLASS_NODE */ + break; +#endif /* PROC_NODE | CLASS_NODE */ + + case SIFP_IOC_SET_FEATURE: + if (arg) { + unsigned char feature = 0; + if (copy_from_user(&feature, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_SET_FEATURE] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_set_feature(fp_dev, feature); + } + break; + + default: + LOG_MSG_DEBUG(ERR_LOG, "Unknown IOCTL %d\n", cmd); + retval = -ENOTTY; + break; + } + + return retval; +} + +int silfp_touch_event_handler(struct fp_dev_touch_info* tp_info) +{ + static uint8_t lasttouchmode = 0; + + if (!g_fp_dev || !tp_info) { + return 0; + } + + LOG_MSG_DEBUG(INFO_LOG, "tp_info %d, %d, %d, %d \n", tp_info->touch_state, tp_info->area_rate, tp_info->x, tp_info->y); + + if (tp_info->touch_state == lasttouchmode) { + return 0; + } + + if (1 == tp_info->touch_state) { + LOG_MSG_DEBUG(INFO_LOG, "touch finger down\n"); + silfp_netlink_send(g_fp_dev, SIFP_NETLINK_TP_TOUCHDOWN); + lasttouchmode = tp_info->touch_state; + } else { + LOG_MSG_DEBUG(INFO_LOG, "touch finger up\n"); + silfp_netlink_send(g_fp_dev, SIFP_NETLINK_TP_TOUCHUP); + lasttouchmode = tp_info->touch_state; + } + + if (g_fp_dev) { + wake_lock_timeout(&g_fp_dev->wakelock, 10*HZ); + } + return 0; +} +EXPORT_SYMBOL(silfp_touch_event_handler); + +#ifdef CONFIG_COMPAT +static long +silfp_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return silfp_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define silfp_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + +static int silfp_open(struct inode *inode, struct file *filp) +{ + struct silfp_data *fp_dev; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(fp_dev, &device_list, device_entry) { + if (fp_dev->devt == inode->i_rdev) { + status = 0; + break; + } + } + if (status == 0) { + fp_dev->users++; + filp->private_data = fp_dev; + nonseekable_open(inode, filp); + } else { + LOG_MSG_DEBUG(ERR_LOG, "silfp: nothing for minor %d\n", iminor(inode)); + } + + mutex_unlock(&device_list_lock); + + return status; +} + +static int silfp_release(struct inode *inode, struct file *filp) +{ + struct silfp_data *fp_dev; + int status = 0; + + mutex_lock(&device_list_lock); + fp_dev = filp->private_data; + filp->private_data = NULL; + + /* last close? */ + fp_dev->users--; + if (!fp_dev->users) { + // todo + } + mutex_unlock(&device_list_lock); + + return status; +} + +static const struct file_operations silfp_dev_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .unlocked_ioctl = silfp_ioctl, + .compat_ioctl = silfp_compat_ioctl, + .open = silfp_open, + .release = silfp_release, +#ifndef BSP_SIL_NETLINK + .read = silfp_read, + .poll = silfp_poll, +#endif /* !BSP_SIL_NETLINK */ +}; + +/*-------------------------------------------------------------------------*/ + +/* The main reason to have this class is to make mdev/udev create the + * /dev/spidevB.C character device nodes exposing our userspace API. + * It also simplifies memory management. + */ + +/*-------------------------------------------------------------------------*/ + +static int silfp_probe(struct platform_device *spi) +{ + struct silfp_data *fp_dev; + int status = 0; + //unsigned long minor; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + + /* Allocate driver data */ + fp_dev = kzalloc(sizeof(*fp_dev), GFP_KERNEL); + if (!fp_dev) { + return -ENOMEM; + } + + /* Initialize the driver data */ + fp_dev->spi = spi; + spin_lock_init(&fp_dev->spi_lock); + + wake_lock_init(&fp_dev->wakelock,WAKE_LOCK_SUSPEND,"silfp_wakelock"); + wake_lock_init(&fp_dev->wakelock_hal,WAKE_LOCK_SUSPEND,"silfp_wakelock_hal"); + INIT_LIST_HEAD(&fp_dev->device_entry); + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + + if (FP_DEV_MAJOR > 0) { + fp_dev->devt = MKDEV(FP_DEV_MAJOR, fp_dev->ref++); + status = register_chrdev_region(fp_dev->devt, 1, FP_DEV_NAME); + } else { + status = alloc_chrdev_region(&fp_dev->devt, fp_dev->ref++, 1, FP_DEV_NAME); + } + if (status < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] request devt fail, ret=%d.\n", __func__, status); + goto err_devt; + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s], major=%d, minor=%d\n", __func__, MAJOR(fp_dev->devt), MINOR(fp_dev->devt)); + } + + fp_dev->dev = device_create(silfp_class, &fp_dev->spi->dev, fp_dev->devt, fp_dev, FP_DEV_NAME); + if (IS_ERR(fp_dev->dev)) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] device_create() fail.\n", __func__); + status = -ENODEV; + goto err_dev; + } else { + mutex_lock(&device_list_lock); + list_add(&fp_dev->device_entry, &device_list); + mutex_unlock(&device_list_lock); + LOG_MSG_DEBUG(INFO_LOG, "[%s] device create success.\n", __func__); + } + + cdev_init(&fp_dev->cdev, &silfp_dev_fops); + fp_dev->cdev.owner = THIS_MODULE; + status = cdev_add(&fp_dev->cdev, fp_dev->devt, 1); + if (status) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] cdev_add() fail, ret=%d.\n", __func__, status); + goto err_cdev; + } + + atomic_set(&fp_dev->init,0); + status = silfp_init(fp_dev); + g_fp_dev = NULL; + + if (status) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] silfp_init fail ret=%d.\n", __func__, status); + goto err_cdev; + } + LOG_MSG_DEBUG(INFO_LOG, "%s: success ret:%d \n", __func__, status); + +#ifdef PROC_NODE + silfp_proc_init(fp_dev); +#endif /* PROC_NODE */ + platform_set_drvdata(spi, fp_dev); + + return status; + +err_cdev: + list_del(&fp_dev->device_entry); + +err_dev: + unregister_chrdev_region(fp_dev->devt, 1); + +err_devt: + platform_set_drvdata(spi, NULL); + fp_dev->spi = NULL; + kfree(fp_dev); + fp_dev = NULL; + + return status; +} + +static int silfp_remove(struct platform_device *spi) +{ + struct silfp_data *fp_dev = platform_get_drvdata(spi); + + wake_lock_destroy(&fp_dev->wakelock); + wake_lock_destroy(&fp_dev->wakelock_hal); + /* make sure ops on existing fds can abort cleanly */ + spin_lock_irq(&fp_dev->spi_lock); + fp_dev->spi = NULL; + spin_unlock_irq(&fp_dev->spi_lock); + + silfp_exit(fp_dev); + /* prevent new opens */ + mutex_lock(&device_list_lock); + cdev_del(&fp_dev->cdev); + list_del(&fp_dev->device_entry); + device_destroy(silfp_class, fp_dev->devt); + if (fp_dev->users == 0) + kfree(fp_dev); + mutex_unlock(&device_list_lock); + + return 0; +} + +static const struct of_device_id sildev_dt_ids[] = { + { .compatible = "sil,silead_fp" }, + { .compatible = "sil,silead-fp" }, + { .compatible = "sil,fingerprint" }, + { .compatible = "sil,silead_fp-pins" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sildev_dt_ids); + +static struct platform_driver silfp_driver = { + .driver = { + .name = "silead_fp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sildev_dt_ids), + }, + .probe = silfp_probe, + .remove = silfp_remove, + + /* NOTE: suspend/resume methods are not necessary here. + * We don't do anything except pass the requests to/from + * the underlying controller. The refrigerator handles + * most issues; the controller driver handles the rest. + */ +}; + +/*-------------------------------------------------------------------------*/ +#ifndef BSP_SIL_DYNAMIC_SPI +static +#endif +int silfp_dev_init(void) +{ + int status = 0; + + LOG_MSG_DEBUG(ERR_LOG, "SILEAD_FP Driver, Version: %s.\n", FP_DEV_VERSION); + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + status = register_chrdev(FP_DEV_MAJOR, "sil", &silfp_dev_fops); + if (status < 0) { + return status; + } + + silfp_class = class_create(THIS_MODULE, "silead_fp"); + if (IS_ERR(silfp_class)) { + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); + return PTR_ERR(silfp_class); + } + + status = platform_driver_register(&silfp_driver); + if (status < 0) { + class_destroy(silfp_class); + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); + LOG_MSG_DEBUG(ERR_LOG, "[%s] spi_register_driver fail ret=%d.\n", __func__, status); + return status; + } + silfp_wq = create_singlethread_workqueue("silfp_wq"); + return status; +} + +#ifndef BSP_SIL_DYNAMIC_SPI +static +#endif +void silfp_dev_exit(void) +{ + platform_driver_unregister(&silfp_driver); + class_destroy(silfp_class); + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); +} + +#ifdef BSP_SIL_DYNAMIC_SPI +EXPORT_SYMBOL(silfp_dev_init); +EXPORT_SYMBOL(silfp_dev_exit); +EXPORT_SYMBOL(sil_debug_level); +#else +module_exit(silfp_dev_exit); +module_init(silfp_dev_init); + +MODULE_AUTHOR("Bill Yu "); +MODULE_DESCRIPTION("Gigadevice/Silead Fingerprint driver for GSL6xxx/GSL7xxx/GSL8xxx series."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("sil:silead_fp"); +#endif /* BSP_SIL_DYNAMIC_SPI */ diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp.h b/drivers/input/misc/silead_fps_mmi/silead_fp.h new file mode 100644 index 000000000000..b7202bf4484c --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp.h @@ -0,0 +1,316 @@ +/* + * @file silead_fp.h + * @brief Contains silead_fp device head file. + * + * + * Copyright 2016-2021 GigaDevice/Silead Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * Bill Yu 2018/5/28 0.1.1 Disable netlink if netlink id = 0 + * Bill Yu 2018/6/1 0.1.2 Support wakelock + * Bill Yu 2018/6/5 0.1.3 Support chip enter power down + * Bill Yu 2018/6/7 0.1.4 Support create proc node + * Bill Yu 2018/6/27 0.1.5 Expand pwdn I/F + * Rui Wu 2019/10/10 0.1.6 Support create class node + * Bill Yu 2020/5/11 0.1.7 Netlink id should < 32 + * Bill Yu 2020/5/29 0.1.8 Default use poll + * Bill Yu 2020/12/22 0.1.9 Support 5.x version wakeup + * Melvin cao 2021/01/29 0.2.0 Support silead spi device driver + */ + +#ifndef __SILEAD_FP_H__ +#define __SILEAD_FP_H__ + +#include +#include + +#ifndef _LINUX_WAKELOCK_H + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#define USE_WAKEUP_REG +#endif + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +#ifdef USE_WAKEUP_REG +struct wake_lock { + struct wakeup_source *ws; +}; + +#define ws_init(s, n) s = wakeup_source_register(NULL, n) +#define ws_deinit(s) wakeup_source_unregister(s) +#define ws_lock(s) __pm_stay_awake(s) +#define ws_lock_tm(s, t) __pm_wakeup_event(s, t) +#define ws_unlock(s) __pm_relax(s) + +#else +struct wake_lock { + struct wakeup_source ws; +}; + +#define ws_init(s, n) wakeup_source_init(&s, n) +#define ws_deinit(s) wakeup_source_trash(&s) +#define ws_lock(s) __pm_stay_awake(&s) +#define ws_lock_tm(s, t) __pm_wakeup_event(&s, t) +#define ws_unlock(s) __pm_relax(&s) + +#endif /* USE_WAKEUP_REG */ + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + ws_init(lock->ws, name); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + ws_deinit(lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + ws_lock(lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + ws_lock_tm(lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + ws_unlock(lock->ws); +} +#endif /* _LINUX_WAKELOCK_H */ + +enum _pwdn_mode_t { + SIFP_PWDN_NONE = 0, + SIFP_PWDN_POWEROFF = 1, /* shutdown the avdd power supply */ + SIFP_PWDN_FLASH = 2, /* shutdown avdd 200ms for H/W full reset */ + SIFP_PWDN_MAX, +}; + +enum _netlink_cmd_t { + SIFP_NETLINK_START = 0, + SIFP_NETLINK_IRQ = 1, + SIFP_NETLINK_SCR_OFF, + SIFP_NETLINK_SCR_ON, + SIFP_NETLINK_CONNECT, + SIFP_NETLINK_DISCONNECT, + SIFP_NETLINK_TP_TOUCHDOWN, + SIFP_NETLINK_TP_TOUCHUP, + SIFP_NETLINK_MAX, +}; + +enum _fp_nav_key_v_t { + NAV_KEY_UNKNOWN = 0, + NAV_KEY_START = 1, + NAV_KEY_UP = NAV_KEY_START, + NAV_KEY_DOWN, + NAV_KEY_RIGHT, + NAV_KEY_LEFT, + NAV_KEY_CLICK, + NAV_KEY_DCLICK, + NAV_KEY_LONGPRESS, + NAV_KEY_CLICK_DOWN, + NAV_KEY_CLICK_UP, + NAV_KEY_MAX, + NAV_KEY_WAITMORE = 1000, +}; + +#define IS_KEY_VALID(k) ((k) > NAV_KEY_UNKNOWN && (k) < NAV_KEY_MAX) + +enum _fp_nav_key_f_t { + NAV_KEY_FLAG_UP = 0, + NAV_KEY_FLAG_DOWN, + NAV_KEY_FLAG_CLICK, +}; + +struct fp_dev_key_t { + int value; + uint32_t flag; /* key down = 1, key up = 0, key down+up = 2 */ +}; + +#define DEVNAME_LEN 16 +struct fp_dev_init_t { + uint8_t mode; + uint8_t bits; + uint16_t delay; + uint32_t speed; + char dev[DEVNAME_LEN]; + uint8_t nl_id; + uint8_t dev_id; + uint16_t reserve; + uint32_t reg; + char ta[DEVNAME_LEN]; +}; + +struct fp_dev_debug_t { + uint8_t cmd[4]; +}; + +struct fp_dev_kmap_t { + uint16_t k[NAV_KEY_MAX-NAV_KEY_START]; /* Up/Down/Right/Left/Click/Double Click/Longpress */ +}; + +struct fp_dev_touch_info { + uint8_t touch_state; + uint8_t area_rate; + uint16_t x; + uint16_t y; +}; + +#define PROC_VND_ID_LEN 32 + +#define SIFP_IOC_MAGIC 's' + +#define SIFP_IOC_RESET _IOW(SIFP_IOC_MAGIC, 10, u8) + +#define SIFP_IOC_ENABLE_IRQ _IO(SIFP_IOC_MAGIC, 11) +#define SIFP_IOC_DISABLE_IRQ _IO(SIFP_IOC_MAGIC, 12) +#define SIFP_IOC_WAIT_IRQ _IOR(SIFP_IOC_MAGIC, 13, u8) +#define SIFP_IOC_CLR_IRQ _IO(SIFP_IOC_MAGIC, 14) +//#define SPFP_IOC_EXIT _IOR(SIFP_IOC_MAGIC, 1, u8) + +#define SIFP_IOC_KEY_EVENT _IOW(SIFP_IOC_MAGIC, 15, struct fp_dev_key_t) +#define SIFP_IOC_INIT _IOR(SIFP_IOC_MAGIC, 16, struct fp_dev_init_t) +#define SIFP_IOC_DEINIT _IO(SIFP_IOC_MAGIC, 17) +#define SIFP_IOC_IRQ_STATUS _IOR(SIFP_IOC_MAGIC, 18, u8) +#define SIFP_IOC_DEBUG _IOR(SIFP_IOC_MAGIC, 19, struct fp_dev_debug_t) +#define SIFP_IOC_SCR_STATUS _IOR(SIFP_IOC_MAGIC, 20, u8) +#define SIFP_IOC_GET_VER _IOR(SIFP_IOC_MAGIC, 21, char[10]) +#define SIFP_IOC_SET_KMAP _IOW(SIFP_IOC_MAGIC, 22, uint16_t[7]) +#define SIFP_IOC_ACQ_SPI _IO(SIFP_IOC_MAGIC, 23) +#define SIFP_IOC_RLS_SPI _IO(SIFP_IOC_MAGIC, 24) +#define SIFP_IOC_PKG_SIZE _IOR(SIFP_IOC_MAGIC, 25, u8) +#define SIFP_IOC_DBG_LEVEL _IOWR(SIFP_IOC_MAGIC,26, u8) +#define SIFP_IOC_WAKELOCK _IOW(SIFP_IOC_MAGIC, 27, u8) +#define SIFP_IOC_PWDN _IOW(SIFP_IOC_MAGIC, 28, u8) +#define SIFP_IOC_PROC_NODE _IOW(SIFP_IOC_MAGIC, 29, char[PROC_VND_ID_LEN]) +#define SIFP_IOC_SET_FEATURE _IOW(SIFP_IOC_MAGIC, 30, u8) + +#define FEATURE_FLASH_CS 0x01 + +#define RESET_TIME 2 /* Default chip reset wait time(ms) */ +#define RESET_TIME_MULTIPLE 1 /* Multiple for reset time multiple*wait_time */ +#define SIFP_NETLINK_ROUTE 0 /* 0: poll/1-31: netlink(cat /proc/net/netlink)/ > 31: epoll/ 30 */ +#define NL_MSG_LEN 16 + +//#define PROC_DIR "fp" /* if defined, create node under /proc/fp/xxx */ +#define PROC_NODE "fp_id" /* proc node name */ + +//#define CLASS_NODE "fingerprint" /* if defined, create class node /sys/class/fingerprint/fingerprint */ + +#if (SIFP_NETLINK_ROUTE > 0) && (SIFP_NETLINK_ROUTE < 32) + #define BSP_SIL_NETLINK +#endif + +#if !defined(BSP_SIL_PLAT_MTK) && !defined(BSP_SIL_PLAT_QCOM) + #define BSP_SIL_PLAT_COMM +#endif /* ! BSP_SIL_PLAT_MTK & ! BSP_SIL_PLAT_QCOM */ + +/* Todo: enable correct power supply mode */ +//#define BSP_SIL_POWER_SUPPLY_REGULATOR +//#define BSP_SIL_POWER_SUPPLY_PINCTRL +#define BSP_SIL_POWER_SUPPLY_GPIO + +/* AVDD voltage range 2.8v ~ 3.3v */ +#define AVDD_MAX 2800000 +#define AVDD_MIN 2800000 + +/* VDDIO voltage range 1.8v ~ AVDD */ +#define VDDIO_MAX 1800000 +#define VDDIO_MIN 1800000 + +#if defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) || defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_GPIO) || defined(BSP_SIL_POWER_SUPPLY_GPIO) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) + #error "Don't define multiple power supply mode!" +#endif + +#ifdef BSP_SIL_PLAT_MTK + #include "silead_fp_mtk.h" + #define PLAT_H "silead_fp_mtk.c" + #define SILFP_SPI_REE /* REE silead_spi driver, disable it if CONFIG_SPIDEV is enabled */ + + #ifdef SILFP_SPI_REE + #define DEVICE "/dev/silead_spi" + #else + #define DEVICE "/dev/spidev1.0" + #endif + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 1 + #define BSP_SIL_DYNAMIC_SPI + #ifndef CONFIG_SILEAD_FP_PLATFORM + #define BSP_SIL_CTRL_SPI + #endif /* !CONFIG_SILEAD_FP_PLATFORM */ +#elif defined(BSP_SIL_PLAT_QCOM) + #define QSEE_V4 /* Enable it if QSEE v4 or higher */ + #include "silead_fp_qcom.h" + #define PLAT_H "silead_fp_qcom.c" + + #define DEVICE "/dev/spidev0.0" + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 4 + #define TANAME "sileadta" +#elif defined(BSP_SIL_PLAT_SPRD) + #include "silead_fp_sprd.h" + #define PLAT_H "silead_fp_sprd.c" + + #define DEVICE "/dev/spidev0.0" + //#define BSP_SIL_IRQ_CONFIRM + //#define BSP_SIL_QCOM_SPECIAL + #define PKG_SIZE 4 + #define TANAME "silead" +#else + #include "silead_fp_comm.h" + #define PLAT_H "silead_fp_comm.c" + + #define DEVICE "/dev/spidev0.0" + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 4 + #define TANAME "" +#endif /* BSP_SIL_PLAT_XXX */ + +#ifdef BSP_SIL_QCOM_SPECIAL + #define SIL_EVENT_BLANK MSM_DRM_EARLY_EVENT_BLANK + #define SIL_EVENT_UNBLANK MSM_DRM_BLANK_UNBLANK + #define SIL_EVENT_POWERDOWN MSM_DRM_BLANK_POWERDOWN + #define SIL_REGISTER_CLIENT(a) do{ msm_drm_register_client(a); }while(0) +#else + #define SIL_EVENT_BLANK FB_EVENT_BLANK + #define SIL_EVENT_UNBLANK FB_BLANK_UNBLANK + #define SIL_EVENT_POWERDOWN FB_BLANK_POWERDOWN + #define SIL_REGISTER_CLIENT(a) do{ fb_register_client(a); }while(0) +#endif + +/* -------------------------------------------------------------------- */ +/* debug settings */ +/* -------------------------------------------------------------------- */ +typedef enum { + ERR_LOG=0, + INFO_LOG, + DBG_LOG, + ALL_LOG, +} fp_debug_level_t; + +#define LOG_TAG "[+silead_fp-] " +#define LOG_MSG_DEBUG(level, fmt, args...) do { \ + if (sil_debug_level >= level) {\ + pr_warn(LOG_TAG fmt, ##args); \ + } \ + } while (0) + +#endif /* __SILEAD_FP_H__ */ + +/* End of file silead_fp.h */ diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.c b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.c new file mode 100644 index 000000000000..50b6c9c6193b --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.c @@ -0,0 +1,603 @@ +/* + * @file silead_fp_qcom.c + * @brief Contains silead_fp device implements for Qualcomm platform. + * + * + * Copyright 2016-2021 Gigadevice/Silead Inc. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * Bill Yu 2018/5/20 0.1.1 Default wait 3ms after reset + * Bill Yu 2018/6/5 0.1.2 Support chip enter power down + * Bill Yu 2018/6/27 0.1.3 Expand pwdn I/F + * Taobb 2019/6/6 0.1.4 Expand feature interface, irq pin set to reset pin + * Bill Yu 2020/12/16 0.1.5 Allow GPIO ID number to be zero + * + */ + +#ifdef BSP_SIL_PLAT_QCOM + +#ifndef __SILEAD_FP_QCOM__ +#define __SILEAD_FP_QCOM__ + +#include + +#ifndef INIT_COMPLETION +#define INIT_COMPLETION(x) ((x).done = 0) +#endif /* INIT_COMPLETION */ +/* Qualcomm MNC platform, need define this. */ +#define reinit_completion(x) INIT_COMPLETION(*(x)) + +#define SPI_PINCTRL_STATE_DEFAULT "spi_default" +#define SPI_PINCTRL_STATE_SLEEP "spi_sleep" + +static irqreturn_t silfp_irq_handler(int irq, void *dev_id); +static void silfp_work_func(struct work_struct *work); +static int silfp_input_init(struct silfp_data *fp_dev); + +/* -------------------------------------------------------------------- */ +/* power supply */ +/* -------------------------------------------------------------------- */ +static void silfp_hw_poweron(struct silfp_data *fp_dev) +{ + int err = 0; + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo ) { + err = regulator_set_voltage(fp_dev->avdd_ldo, AVDD_MIN, AVDD_MAX); /*set 2.8v*/ + err = regulator_enable(fp_dev->avdd_ldo); /*enable regulator*/ + //pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A); + } + if ( fp_dev->vddio_ldo ) { + err = regulator_set_voltage(fp_dev->vddio_ldo, VDDIO_MIN, VDDIO_MAX); /*set 1.8v*/ + err = regulator_enable(fp_dev->vddio_ldo); /*enable regulator*/ + //pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A); + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + if ( fp_dev->pin.pins_avdd_h ) { + err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_avdd_h); + } + if ( fp_dev->pin.pins_vddio_h ) { + err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_vddio_h); + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port >= 0 ) { + err = gpio_direction_output(fp_dev->avdd_port, 1); + } + if ( fp_dev->vddio_port >= 0 ) { + err = gpio_direction_output(fp_dev->vddio_port, 1); + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + fp_dev->power_is_off = 0; + LOG_MSG_DEBUG(INFO_LOG, "%s: power supply ret:%d \n", __func__, err); +} + +static void silfp_hw_poweroff(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo && (regulator_is_enabled(fp_dev->avdd_ldo) > 0)) { + regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/ + } + if ( fp_dev->vddio_ldo && (regulator_is_enabled(fp_dev->vddio_ldo) > 0)) { + regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/ + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + //fp_dev->pin.pins_avdd_h = NULL; + //fp_dev->pin.pins_vddio_h = NULL; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port >= 0 ) { + gpio_direction_output(fp_dev->avdd_port, 0); + } + if ( fp_dev->vddio_port >= 0 ) { + gpio_direction_output(fp_dev->vddio_port, 0); + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + fp_dev->power_is_off = 1; +} + +static void silfp_power_deinit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo ) { + regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/ + regulator_put(fp_dev->avdd_ldo); + fp_dev->avdd_ldo = NULL; + } + if ( fp_dev->vddio_ldo ) { + regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/ + regulator_put(fp_dev->vddio_ldo); + fp_dev->vddio_ldo = NULL; + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + fp_dev->pin.pins_avdd_h = NULL; + fp_dev->pin.pins_vddio_h = NULL; + if (fp_dev->pin.pinctrl) { + devm_pinctrl_put(fp_dev->pin.pinctrl); + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port >= 0 ) { + gpio_direction_output(fp_dev->avdd_port, 0); + gpio_free(fp_dev->avdd_port); + fp_dev->avdd_port = -1; + } + if ( fp_dev->vddio_port >= 0 ) { + gpio_direction_output(fp_dev->vddio_port, 0); + gpio_free(fp_dev->vddio_port); + fp_dev->vddio_port = -1; + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ +} + +/* -------------------------------------------------------------------- */ +/* hardware reset */ +/* -------------------------------------------------------------------- */ +static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port); + + if ( fp_dev->rst_port >= 0 ) { + gpio_direction_output(fp_dev->rst_port, 0); + if (fp_dev->irq_no_use && fp_dev->int_port >= 0) { + gpio_direction_output(fp_dev->int_port, 1); + } + mdelay((delay?delay:5)*RESET_TIME_MULTIPLE); + gpio_direction_output(fp_dev->rst_port, 1); + if (fp_dev->irq_no_use && fp_dev->int_port >= 0) { + gpio_direction_output(fp_dev->int_port, 0); + } + + mdelay((delay?delay:3)*RESET_TIME_MULTIPLE); + } +} + +/* -------------------------------------------------------------------- */ +/* power down */ +/* -------------------------------------------------------------------- */ +static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port); + + if (SIFP_PWDN_FLASH == flag_avdd) { + silfp_hw_poweroff(fp_dev); + msleep(200*RESET_TIME_MULTIPLE); + silfp_hw_poweron(fp_dev); + } + + if ( fp_dev->rst_port >= 0 ) { + gpio_direction_output(fp_dev->rst_port, 0); + if (fp_dev->irq_no_use && fp_dev->int_port >= 0) { + gpio_direction_output(fp_dev->int_port, 1); + } + } + + if (SIFP_PWDN_POWEROFF == flag_avdd) { + silfp_hw_poweroff(fp_dev); + } +} + +/* -------------------------------------------------------------------- */ +/* init/deinit functions */ +/* -------------------------------------------------------------------- */ +static int silfp_parse_dts(struct silfp_data* fp_dev) +{ + int ret; +#ifndef QSEE_V4 + /* Get the pinctrl node */ + fp_dev->pin.pinctrl = devm_pinctrl_get(&fp_dev->spi->dev); + if (IS_ERR_OR_NULL(fp_dev->pin.pinctrl)) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl\n", __func__); + return PTR_ERR(fp_dev->pin.pinctrl); + } + + /* Get the active setting */ + fp_dev->pin.active = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(fp_dev->pin.active)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state active\n",__func__); + return PTR_ERR(fp_dev->pin.active); + } + + /* Get power settings */ +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + fp_dev->pin.pins_avdd_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "avdd-enable"); + if (IS_ERR_OR_NULL(fp_dev->pin.pins_avdd_h)) { + fp_dev->pin.pins_avdd_h = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp avdd-enable\n", __func__); + // Ignore error + } + + fp_dev->pin.pins_vddio_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "vddio-enable"); + if (IS_ERR_OR_NULL(fp_dev->pin.pins_vddio_h)) { + fp_dev->pin.pins_vddio_h = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp vddio-enable\n", __func__); + // Ignore error + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ +#endif /* QSEE_V4 */ + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + // Todo: use correct settings. + fp_dev->avdd_ldo = regulator_get(&fp_dev->spi->dev, "avdd"); + fp_dev->vddio_ldo= regulator_get(&fp_dev->spi->dev, "vddio"); +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifndef QSEE_V4 + /* Get sleep settings */ + fp_dev->pin.sleep = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(fp_dev->pin.sleep)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state sleep\n",__func__); + return PTR_ERR(fp_dev->pin.sleep); + } + + /* Get iface_clk info */ + fp_dev->pin.iface_clk = clk_get(&fp_dev->spi->dev, "iface_clk"); + if (IS_ERR(fp_dev->pin.iface_clk)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get iface_clk %ld\n", __func__, PTR_ERR(fp_dev->pin.iface_clk)); + return PTR_ERR(fp_dev->pin.iface_clk); + } + + /* Get core_clk info */ + fp_dev->pin.core_clk = clk_get(&fp_dev->spi->dev, "core_clk"); + if (IS_ERR(fp_dev->pin.core_clk)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get core_clk %p\n", __func__, fp_dev->pin.core_clk); + return PTR_ERR(fp_dev->pin.core_clk); + } +#else + /* Get the SPI Max speed */ + ret = of_property_read_u32(fp_dev->spi->dev.of_node,"spi-max-frequency", &fp_dev->pin.max_speed_hz); + if (ret) { + fp_dev->pin.max_speed_hz = 0; + LOG_MSG_DEBUG(ERR_LOG, "Error getting spi max speed\n"); + } +#endif /* QSEE_V4 */ + + /* Get the QUP ID (#1-12) */ + ret = of_property_read_u32(fp_dev->spi->dev.of_node,"qcom,qup-id", &fp_dev->pin.qup_id); + if (ret) { + fp_dev->pin.qup_id = 0; + LOG_MSG_DEBUG(ERR_LOG, "Error getting qup_id\n"); + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] done (%d).\n",__func__,ret); + /* + Grab SPI master lock for exclusive access + call spi_bus_unlock to unlock the lock. + */ + //spi_bus_lock(fp_dev->spi->master); + return ret; +} + +#ifndef QSEE_V4 +static int spi_set_pinctrl(struct silfp_data* fp_dev, bool active) +{ + int ret = -1; + + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) { + LOG_MSG_DEBUG(ERR_LOG, "%s: not support\n", __func__); + return ret; + } + if (active) { /* Change to active settings */ + ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.active); + } else { + ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.sleep); + } + + LOG_MSG_DEBUG(INFO_LOG, "%s: pinctrl_select_state ret:%d Setting:%d\n", __func__, ret, active); + return ret; +} + +static int spi_set_clks(struct silfp_data* fp_dev, bool enable) +{ + int ret = -1; + int clk = 19200000; + + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) { + LOG_MSG_DEBUG(INFO_LOG, "%s: not support\n", __func__); + return ret; + } + if (enable) { + /* Enable the spi clocks */ + ret = clk_set_rate(fp_dev->pin.core_clk, fp_dev->spi->max_speed_hz); + if (ret) { + while ( clk > fp_dev->spi->max_speed_hz ) { + clk >>= 1; + } + if ( clk < 4800000 ) { + clk = 4800000; + } + ret = clk_set_rate(fp_dev->pin.core_clk, clk); + if ( ret ) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error setting clk_rate:%d, ret=%d\n",__func__, clk,ret); + } else { + fp_dev->spi->max_speed_hz = clk; + } + } + ret = clk_prepare_enable(fp_dev->pin.core_clk); + if (ret) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling core clk, ret=%d\n",__func__,ret); + } + ret = clk_prepare_enable(fp_dev->pin.iface_clk); + if (ret) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling iface clk, ret=%d\n",__func__,ret); + } + } else { + /* Disable the clocks */ + clk_disable_unprepare(fp_dev->pin.iface_clk); + clk_disable_unprepare(fp_dev->pin.core_clk); + ret = 0; + } + + LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d), speed = %d.\n",__func__,ret,fp_dev->spi->max_speed_hz ); + return ret; +} + +static int spi_set_fabric(struct silfp_data* fp_dev, bool active) +{ + int ret; + struct spi_master *master = fp_dev->spi->master; + + if (active) { + ret = master->prepare_transfer_hardware(master); + } else { + ret = master->unprepare_transfer_hardware(master); + } + LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d).\n",__func__,ret); + return ret; +} +#endif /* !QSEE_V4 */ + +static int silfp_set_spi(struct silfp_data *fp_dev, bool enable) +{ + int ret = -ENOENT; +#ifndef QSEE_V4 + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active) || !fp_dev->pin.qup_id ) { + LOG_MSG_DEBUG(ERR_LOG, "%s: not support!\n", __func__); + return ret; + } + + if ( enable && !atomic_read(&fp_dev->spionoff_count) ) { + atomic_inc(&fp_dev->spionoff_count); + ret = spi_set_pinctrl(fp_dev, enable); + ret |= spi_set_fabric(fp_dev, enable); + ret |= spi_set_clks(fp_dev, enable); + } else if (atomic_read(&fp_dev->spionoff_count)) { + atomic_dec(&fp_dev->spionoff_count); + //spi_change_pipe_owner(false); + ret = spi_set_clks(fp_dev, enable); + ret |= spi_set_fabric(fp_dev, enable); + ret |= spi_set_pinctrl(fp_dev, enable); + } +#else + LOG_MSG_DEBUG(ERR_LOG, "%s: qsee4 no needed!\n", __func__); +#endif /* !QSEE_V4 */ + return ret; +} + +static int silfp_irq_to_reset_init(struct silfp_data *fp_dev) +{ + int ret = 0; + + silfp_irq_disable(fp_dev); + free_irq(fp_dev->irq, fp_dev); + + if (fp_dev->int_port >= 0) { + gpio_free(fp_dev->int_port); + ret = gpio_request(fp_dev->int_port, "SILFP_IRQ_TO_RST_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret); + return -ENODEV; + } else { + gpio_direction_output(fp_dev->int_port, 0); + } + } + + fp_dev->irq_no_use = 1; + + return ret; +} + +static int silfp_set_feature(struct silfp_data *fp_dev, u8 feature) +{ + int ret = 0; + + switch (feature) { + case FEATURE_FLASH_CS: + LOG_MSG_DEBUG(INFO_LOG, "%s set feature flash cs\n", __func__); + ret = silfp_irq_to_reset_init(fp_dev); + break; + + default: + break; + } + return ret; +} + +static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info) +{ + int status = 0; + int ret; + + LOG_MSG_DEBUG(ERR_LOG, "[%s] enter.\n", __func__); + printk(KERN_INFO "silfp_resource_init enter!\n"); + + if (atomic_read(&fp_dev->init)) { + atomic_inc(&fp_dev->init); + LOG_MSG_DEBUG(DBG_LOG, "[%s] dev already init(%d).\n",__func__,atomic_read(&fp_dev->init)); + return status; + } + + fp_dev->int_port = -1; + fp_dev->rst_port = -1; + silfp_parse_dts(fp_dev); +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + fp_dev->avdd_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "avdd-gpios", 0); + fp_dev->vddio_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "vddio-gpios", 0); + if (fp_dev->avdd_port >= 0 ) { + gpio_free(fp_dev->avdd_port); + ret = gpio_request(fp_dev->avdd_port, "SILFP_AVDD_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->avdd_port, ret); + status = -ENODEV; + goto err_avdd; + } + } + + if (fp_dev->vddio_port >= 0 ) { + gpio_free(fp_dev->vddio_port); + ret = gpio_request(fp_dev->vddio_port, "SILFP_VDDIO_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->vddio_port, ret); + status = -ENODEV; + //goto err_vddio; + } + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + silfp_hw_poweron(fp_dev); + fp_dev->int_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "irq-gpios", 0); + fp_dev->rst_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "rst-gpios", 0); + LOG_MSG_DEBUG(INFO_LOG, "[%s] int_port %d, rst_port %d.\n",__func__,fp_dev->int_port,fp_dev->rst_port); + if (fp_dev->int_port >= 0 ) { + gpio_free(fp_dev->int_port); + } + + if (fp_dev->rst_port >= 0 ) { + gpio_free(fp_dev->rst_port); + } + + ret = gpio_request(fp_dev->int_port, "SILFP_INT_IRQ"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret); + status = -ENODEV; + goto err; + } else { + gpio_direction_input(fp_dev->int_port); + fp_dev->irq = gpio_to_irq(fp_dev->int_port); + fp_dev->irq_is_disable = 0; + + ret = request_irq(fp_dev->irq, + silfp_irq_handler, + IRQ_TYPE_EDGE_RISING, //IRQ_TYPE_LEVEL_HIGH, //irq_table[ts->int_trigger_type], + "silfp", + fp_dev); + if ( ret < 0 ) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Filed to request_irq (%d), ert=%d",__func__,fp_dev->irq, ret); + status = -ENODEV; + goto err_irq; + } else { + LOG_MSG_DEBUG(INFO_LOG,"[%s] Enable_irq_wake.\n",__func__); + enable_irq_wake(fp_dev->irq); + silfp_irq_disable(fp_dev); + } + } + + if (fp_dev->rst_port >= 0 ) { + ret = gpio_request(fp_dev->rst_port, "SILFP_RST_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->rst_port, ret); + status = -ENODEV; + goto err_rst; + } else { + gpio_direction_output(fp_dev->rst_port, 1); + } + } + + if (!ret) { + if (silfp_input_init(fp_dev)) { + goto err_input; + } + atomic_set(&fp_dev->init,1); + } + fp_dev->irq_no_use = 0; + dev_info->reserve = PKG_SIZE; + dev_info->reserve <<= 12; +#ifdef QSEE_V4 + if (fp_dev->pin.max_speed_hz) { + dev_info->speed = fp_dev->pin.max_speed_hz; + } +#endif /* QSEE_V4 */ + + if (dev_info && fp_dev->pin.qup_id && (fp_dev->pin.qup_id <= 32)) { + dev_info->dev_id = fp_dev->pin.qup_id; + strncpy(dev_info->ta,TANAME,sizeof(dev_info->ta)); + } + + return status; + +err_input: + if (fp_dev->rst_port >= 0 ) { + gpio_free(fp_dev->rst_port); + } + +err_rst: + free_irq(fp_dev->irq, fp_dev); + gpio_direction_input(fp_dev->int_port); + +err_irq: + gpio_free(fp_dev->int_port); + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + gpio_free(fp_dev->vddio_port); +#if 0 +err_vddio: + gpio_free(fp_dev->avdd_port); +#endif +err_avdd: + fp_dev->avdd_port = -1; + fp_dev->vddio_port = -1; + +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + +err: + fp_dev->int_port = -1; + fp_dev->rst_port = -1; + + return status; +} + +#endif /* __SILEAD_FP_QCOM__ */ + +#endif /* BSP_SIL_PLAT_QCOM */ + +/* End of file spilead_fp_qcom.c */ diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.h b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.h new file mode 100644 index 000000000000..6e8e2a0a6653 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.h @@ -0,0 +1,52 @@ +/* + * @file silead_fp_qcom.h + * @brief Contains silead_fp Qualcomm platform specific head file. + * + * + * Copyright 2016-2021 Gigadevice/Silead Inc. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * + */ + +#ifndef __SILEAD_FP_QCOM_H__ +#define __SILEAD_FP_QCOM_H__ + +#include +#include + +struct fp_plat_t { + u32 qup_id; +#ifdef QSEE_V4 + u32 max_speed_hz; +#else + /* pinctrl info */ + struct pinctrl *pinctrl; + struct pinctrl_state *active; + struct pinctrl_state *sleep; +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + struct pinctrl_state *pins_avdd_h, *pins_vddio_h; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + /* clock info */ + struct clk *core_clk; + struct clk *iface_clk; +#endif /* QSEE_V4 */ +}; + +#endif /* __SILEAD_FP_QCOM_H__ */ + +/* End of file silead_fp_qcom.h */