/* * ZUK FPC1150 REE driver * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define pr_fmt(fmt) "ZUK-FPC: %s: " fmt, __func__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FPC1020_TOUCH_DEV_NAME "fpc1020tp" #define FPC1020_RESET_LOW_US 1000 #define FPC1020_RESET_HIGH1_US 100 #define FPC1020_RESET_HIGH2_US 1250 #define FPC_TTW_HOLD_TIME 1500 struct fpc1020_data { struct device *dev; struct pinctrl *pin; wait_queue_head_t wq_irq_return; /*Set pins*/ int reset_gpio; int irq_gpio; int irq; bool irq_enabled; int wakeup_enabled; struct notifier_block fb_notif; /*Input device*/ struct input_dev *input_dev; struct work_struct pm_work; struct work_struct input_report_work; struct workqueue_struct *fpc1020_wq; u8 report_key; int screen_on; int proximity_state; /* 0:far 1:near */ }; static void config_irq(struct fpc1020_data *fpc1020, bool enabled) { if (enabled != fpc1020->irq_enabled) { if (enabled) enable_irq(gpio_to_irq(fpc1020->irq_gpio)); else disable_irq(gpio_to_irq(fpc1020->irq_gpio)); dev_info(fpc1020->dev, "%s: %s fpc irq ---\n", __func__, enabled ? "enable" : "disable"); fpc1020->irq_enabled = enabled; } else { dev_info(fpc1020->dev, "%s: dual config irq status: %s\n", __func__, enabled ? "true" : "false"); } } /* From drivers/input/keyboard/gpio_keys.c */ extern bool home_button_pressed(void); extern void reset_home_button(void); bool reset; static bool utouch_disable; static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); static ssize_t irq_get(struct device *device, struct device_attribute *attribute, char *buffer) { struct fpc1020_data *fpc1020 = dev_get_drvdata(device); int irq = gpio_get_value(fpc1020->irq_gpio); return scnprintf(buffer, PAGE_SIZE, "%i\n", irq); } static ssize_t irq_set(struct device *device, struct device_attribute *attribute, const char *buffer, size_t count) { int retval = 0; u64 val; struct fpc1020_data *fpc1020 = dev_get_drvdata(device); retval = kstrtou64(buffer, 0, &val); if (val) enable_irq(fpc1020->irq); else if (!val) disable_irq(fpc1020->irq); else return -ENOENT; return strnlen(buffer, count); } static DEVICE_ATTR(irq, S_IRUSR | S_IWUSR, irq_get, irq_set); static ssize_t get_key(struct device *device, struct device_attribute *attribute, char *buffer) { struct fpc1020_data *fpc1020 = dev_get_drvdata(device); return scnprintf(buffer, PAGE_SIZE, "%i\n", fpc1020->report_key); } static ssize_t set_key(struct device *device, struct device_attribute *attribute, const char *buffer, size_t count) { int retval = 0; u64 val; struct fpc1020_data *fpc1020 = dev_get_drvdata(device); bool home_pressed; retval = kstrtou64(buffer, 0, &val); if (!retval && !utouch_disable) { if (val == KEY_HOME) /* Convert to U-touch long press keyValue */ val = KEY_NAVI_LONG; home_pressed = home_button_pressed(); if (val && home_pressed) val = 0; pr_info("home key pressed = %d\n", (int)home_pressed); fpc1020->report_key = (int)val; queue_work(fpc1020->fpc1020_wq, &fpc1020->input_report_work); if (!val) { pr_info("calling home key reset"); reset_home_button(); } } else if (retval) return -ENOENT; return strnlen(buffer, count); } static DEVICE_ATTR(key, S_IRUSR | S_IWUSR, get_key, set_key); static ssize_t utouch_store_disable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int value; if (1 != sscanf(buf, "%d", &value)) { dev_err(dev, "Failed to parse integer: <%s>\n", buf); return -EINVAL; } if (value == 1) { utouch_disable = true; pr_info("utouch disabled\n"); } else { utouch_disable = false; pr_info("utouch enabled\n"); } return count; } static ssize_t utouch_show_disable(struct device *dev, struct device_attribute *attr, char *buf) { if (utouch_disable) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); } static DEVICE_ATTR(utouch_disable, S_IRUGO|S_IWUSR, utouch_show_disable, utouch_store_disable); static ssize_t enable_wakeup_show(struct device *dev, struct device_attribute *attr, char *buf) { struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); char c; c = fpc1020->wakeup_enabled ? '1' : '0'; return scnprintf(buf, PAGE_SIZE, "%c\n", c); } static ssize_t enable_wakeup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); int i; if (sscanf(buf, "%u", &i) == 1 && i < 2) { fpc1020->wakeup_enabled = (i == 1); dev_info(dev, "%s\n", i ? "wakeup enabled" : "wakeup disabled"); return count; } else { dev_info(dev, "%s: wakeup_enabled write error\n", __func__); return -EINVAL; } } static DEVICE_ATTR(enable_wakeup, S_IWUSR | S_IRUSR, enable_wakeup_show, enable_wakeup_store); static ssize_t proximity_state_set(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); int rc, val; rc = kstrtoint(buf, 10, &val); if (rc) return -EINVAL; fpc1020->proximity_state = !!val; if (!fpc1020->screen_on) { if (fpc1020->proximity_state) { /* Disable IRQ when screen is off and proximity sensor is covered */ config_irq(fpc1020, false); } else if (fpc1020->wakeup_enabled) { /* Enable IRQ when screen is off and proximity sensor is uncovered, but only if fingerprint wake up is enabled */ config_irq(fpc1020, true); } } return count; } static DEVICE_ATTR(proximity_state, S_IWUSR, NULL, proximity_state_set); static struct attribute *attributes[] = { &dev_attr_irq.attr, &dev_attr_key.attr, &dev_attr_enable_wakeup.attr, &dev_attr_proximity_state.attr, &dev_attr_utouch_disable.attr, NULL }; static const struct attribute_group attribute_group = { .attrs = attributes, }; static void fpc1020_report_work_func(struct work_struct *work) { struct fpc1020_data *fpc1020 = NULL; fpc1020 = container_of(work, struct fpc1020_data, input_report_work); if (fpc1020->screen_on) { pr_info("Report key value = %d\n", (int)fpc1020->report_key); input_report_key(fpc1020->input_dev, fpc1020->report_key, 1); input_sync(fpc1020->input_dev); msleep(30); input_report_key(fpc1020->input_dev, fpc1020->report_key, 0); input_sync(fpc1020->input_dev); fpc1020->report_key = 0; } } static void fpc1020_hw_reset(struct fpc1020_data *fpc1020) { pr_info("HW reset\n"); gpio_set_value(fpc1020->reset_gpio, 1); udelay(FPC1020_RESET_HIGH1_US); gpio_set_value(fpc1020->reset_gpio, 0); udelay(FPC1020_RESET_LOW_US); gpio_set_value(fpc1020->reset_gpio, 1); udelay(FPC1020_RESET_HIGH2_US); } static int fpc1020_get_pins(struct fpc1020_data *fpc1020) { int retval = 0; struct device_node *np = fpc1020->dev->of_node; fpc1020->irq_gpio = of_get_named_gpio(np, "fpc,gpio_irq", 0); if (!gpio_is_valid(fpc1020->irq_gpio)) { pr_err("IRQ request failed.\n"); goto err; } fpc1020->reset_gpio = of_get_named_gpio(np, "fpc,gpio_reset", 0); if (!gpio_is_valid(fpc1020->reset_gpio)) { pr_err("RESET pin request failed\n"); goto err; } fpc1020->pin = pinctrl_get_select_default(fpc1020->dev); if (IS_ERR_OR_NULL(fpc1020->pin)) { pr_err("pinctrl get failed.\n"); goto err; } return 0; err: pr_err("%s, err\n", __func__); fpc1020->irq = -EINVAL; fpc1020->irq_gpio = fpc1020->reset_gpio = -EINVAL; retval = -ENODEV; return retval; } static irqreturn_t fpc1020_irq_handler(int irq, void *_fpc1020) { struct fpc1020_data *fpc1020 = _fpc1020; pr_info("fpc1020 IRQ interrupt\n"); /* Make sure 'wakeup_enabled' is updated before using it ** since this is interrupt context (other thread...) */ smp_rmb(); if (fpc1020->wakeup_enabled && !fpc1020->screen_on) { pm_wakeup_event(fpc1020->dev, 5000); } sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); return IRQ_HANDLED; } static int fpc1020_initial_irq(struct fpc1020_data *fpc1020) { int retval = 0; int irqf; if (!gpio_is_valid(fpc1020->irq_gpio)) { pr_err("IRQ pin(%d) is not valid\n", fpc1020->irq_gpio); return -EINVAL; } retval = gpio_request(fpc1020->irq_gpio, "fpc_irq"); if (retval) { pr_err("IRQ(%d) request failed\n", fpc1020->irq_gpio); return -EINVAL; } retval = gpio_direction_input(fpc1020->irq_gpio); if (retval) { pr_err("Set input(%d) failed\n", fpc1020->irq_gpio); return -EINVAL; } fpc1020->irq = gpio_to_irq(fpc1020->irq_gpio); if (fpc1020->irq < 0) { pr_err("gpio_to_irq(%d) failed\n", fpc1020->irq_gpio); return -EINVAL; } irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; if (of_property_read_bool(fpc1020->dev->of_node, "fpc,enable-wakeup")) { irqf |= IRQF_NO_SUSPEND; device_init_wakeup(fpc1020->dev, 1); fpc1020->wakeup_enabled = 1; } retval = devm_request_threaded_irq(fpc1020->dev, fpc1020->irq, NULL, fpc1020_irq_handler, irqf, dev_name(fpc1020->dev), fpc1020); if (retval) { pr_err("request irq %i failed.\n", fpc1020->irq); fpc1020->irq = -EINVAL; return -EINVAL; } dev_info(fpc1020->dev, "requested irq %d\n", fpc1020->irq); /* Request that the interrupt should be wakeable*/ if (fpc1020->wakeup_enabled) { enable_irq_wake(fpc1020->irq); } fpc1020->irq_enabled = true; return 0; } static int fpc1020_manage_sysfs(struct fpc1020_data *fpc1020) { int retval = 0; retval = sysfs_create_group(&fpc1020->dev->kobj, &attribute_group); if (retval) { pr_err("Could not create sysfs\n"); return -EINVAL; } return 0; } static int fpc1020_alloc_input_dev(struct fpc1020_data *fpc1020) { int retval = 0; fpc1020->input_dev = input_allocate_device(); if (!fpc1020->input_dev) { pr_info("Input allocate device failed\n"); retval = -ENOMEM; return retval; } fpc1020->input_dev->name = "fpc1020tp"; set_bit(EV_KEY, fpc1020->input_dev->evbit); set_bit(KEY_BACK, fpc1020->input_dev->keybit); set_bit(KEY_LEFT, fpc1020->input_dev->keybit); set_bit(KEY_RIGHT, fpc1020->input_dev->keybit); set_bit(KEY_NAVI_LONG, fpc1020->input_dev->keybit); input_set_capability(fpc1020->input_dev, EV_KEY, KEY_NAVI_LEFT); input_set_capability(fpc1020->input_dev, EV_KEY, KEY_NAVI_RIGHT); input_set_capability(fpc1020->input_dev, EV_KEY, KEY_BACK); input_set_capability(fpc1020->input_dev, EV_KEY, KEY_NAVI_LONG); /* Register the input device */ retval = input_register_device(fpc1020->input_dev); if (retval) { pr_err("Input_register_device failed.\n"); input_free_device(fpc1020->input_dev); fpc1020->input_dev = NULL; } return retval; } static void set_fingerprintd_nice(int nice) { struct task_struct *p; read_lock(&tasklist_lock); for_each_process(p) { if (!memcmp(p->comm, "fingerprint@2.1", 16)) { pr_debug("fingerprint nice changed to %i\n", nice); set_user_nice(p, nice); break; } } read_unlock(&tasklist_lock); } static void fpc1020_suspend_resume(struct work_struct *work) { struct fpc1020_data *fpc1020 = container_of(work, typeof(*fpc1020), pm_work); /* Escalate fingerprintd priority when screen is off */ if (!fpc1020->screen_on) set_fingerprintd_nice(MIN_NICE); else set_fingerprintd_nice(0); } static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data) { int *blank; struct fb_event *evdata = data; struct fpc1020_data *fpc1020 = container_of(self, struct fpc1020_data, fb_notif); blank = evdata->data; if (evdata && evdata->data && event == FB_EVENT_BLANK && fpc1020) { blank = evdata->data; if (*blank == FB_BLANK_UNBLANK) { pr_debug("ScreenOn\n"); fpc1020->screen_on = 1; queue_work(fpc1020->fpc1020_wq, &fpc1020->pm_work); /* Unconditionally enable IRQ when screen turns on */ config_irq(fpc1020, true); } else if (*blank == FB_BLANK_POWERDOWN) { pr_debug("ScreenOff\n"); fpc1020->screen_on = 0; if (!fpc1020->wakeup_enabled) config_irq(fpc1020, false); queue_work(fpc1020->fpc1020_wq, &fpc1020->pm_work); } } return 0; } static int fpc1020_probe(struct platform_device *pdev) { int retval = 0; struct device *dev = &pdev->dev; struct fpc1020_data *fpc1020 = devm_kzalloc(dev, sizeof(struct fpc1020_data), GFP_KERNEL); if (fpc1020 == NULL) { pr_err("fpc1020 allocation error\n"); retval = -ENOMEM; goto error; } fpc1020->dev = &pdev->dev; dev_set_drvdata(dev, fpc1020); retval = fpc1020_get_pins(fpc1020); if (retval != 0) { pr_err("Get pins failed\n"); goto error; } fpc1020->wakeup_enabled = 0; /*create sfs nodes*/ retval = fpc1020_manage_sysfs(fpc1020); if (retval != 0) { pr_err("Create sysfs nodes failed\n"); goto error; } /*create input device for navigation*/ retval = fpc1020_alloc_input_dev(fpc1020); if (retval != 0) { pr_err("Allocate input device failed\n"); goto error_remove_sysfs; } fpc1020->fpc1020_wq = alloc_workqueue("fpc1020_wq", WQ_HIGHPRI, 1); if (!fpc1020->fpc1020_wq) { pr_err("Create input workqueue failed\n"); goto error_unregister_device; } INIT_WORK(&fpc1020->input_report_work, fpc1020_report_work_func); INIT_WORK(&fpc1020->pm_work, fpc1020_suspend_resume); gpio_direction_output(fpc1020->reset_gpio, 1); /*Do HW reset*/ fpc1020_hw_reset(fpc1020); fpc1020->fb_notif.notifier_call = fb_notifier_callback; retval = fb_register_client(&fpc1020->fb_notif); if (retval) { pr_err("Unable to register fb_notifier : %d\n", retval); goto error_destroy_workqueue; } device_init_wakeup(dev, true); retval = fpc1020_initial_irq(fpc1020); if (retval != 0) { pr_err("IRQ initialized failure\n"); goto error_unregister_client; } /* Disable IRQ */ disable_irq(fpc1020->irq); /* Enable irq wake */ enable_irq_wake(fpc1020->irq); return 0; error_unregister_client: fb_unregister_client(&fpc1020->fb_notif); error_destroy_workqueue: destroy_workqueue(fpc1020->fpc1020_wq); error_unregister_device: input_unregister_device(fpc1020->input_dev); error_remove_sysfs: sysfs_remove_group(&fpc1020->dev->kobj, &attribute_group); error: if (fpc1020 != NULL) kzfree(fpc1020); return retval; } static int fpc1020_remove(struct platform_device *pdev) { int retval = 0; return retval; } static struct of_device_id fpc1020_match[] = { { .compatible = "fpc,fpc1020", }, {} }; static struct platform_driver fpc1020_plat_driver = { .probe = fpc1020_probe, .remove = fpc1020_remove, .driver = { .name = "fpc1020", .owner = THIS_MODULE, .of_match_table = fpc1020_match, }, }; static int fpc1020_init(void) { return platform_driver_register(&fpc1020_plat_driver); } static void fpc1020_exit(void) { platform_driver_unregister(&fpc1020_plat_driver); } module_init(fpc1020_init); module_exit(fpc1020_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZUK ShenQi "); MODULE_DESCRIPTION("FPC1020 fingerprint sensor ree driver.");