* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/input.h>
-#include <linux/clk.h>
#include <linux/err.h>
#include <linux/list.h>
-#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
-#include <linux/ktime.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/gpio.h>
-#include <linux/regulator/consumer.h>
#include <linux/of_gpio.h>
-#include <linux/timer.h>
-#include <linux/notifier.h>
#include <linux/fb.h>
-#include <linux/pm_qos.h>
-#include <linux/cpufreq.h>
-#include <linux/wakelock.h>
#include "gf_spi.h"
-#if defined(USE_SPI_BUS)
-#include <linux/spi/spi.h>
-#include <linux/spi/spidev.h>
-#elif defined(USE_PLATFORM_BUS)
#include <linux/platform_device.h>
-#endif
-
-#define VER_MAJOR 1
-#define VER_MINOR 2
-#define PATCH_LEVEL 1
-
-#define GF_SPIDEV_NAME "goodix,fingerprint"
-/*device name after register in charater*/
-#define GF_DEV_NAME "goodix_fp"
-#define GF_INPUT_NAME "uinput-goodix" /*"goodix_fp" */
-
-#define CHRD_DRIVER_NAME "goodix_fp_spi"
-#define CLASS_NAME "goodix_fp"
-
-#define N_SPI_MINORS 32 /* ... up to 256 */
-static int SPIDEV_MAJOR;
-
-static DECLARE_BITMAP(minors, N_SPI_MINORS);
-static LIST_HEAD(device_list);
-static DEFINE_MUTEX(device_list_lock);
-static struct wake_lock fp_wakelock;
-static struct gf_dev gf;
-
-struct gf_key_map maps[] = {
- { EV_KEY, GF_KEY_INPUT_HOME },
- { EV_KEY, GF_KEY_INPUT_MENU },
- { EV_KEY, GF_KEY_INPUT_BACK },
- { EV_KEY, GF_KEY_INPUT_POWER },
-#if defined(SUPPORT_NAV_EVENT)
- { EV_KEY, GF_NAV_INPUT_UP },
- { EV_KEY, GF_NAV_INPUT_DOWN },
- { EV_KEY, GF_NAV_INPUT_RIGHT },
- { EV_KEY, GF_NAV_INPUT_LEFT },
- { EV_KEY, GF_KEY_INPUT_CAMERA },
- { EV_KEY, GF_NAV_INPUT_CLICK },
- { EV_KEY, GF_NAV_INPUT_DOUBLE_CLICK },
- { EV_KEY, GF_NAV_INPUT_LONG_PRESS },
- { EV_KEY, GF_NAV_INPUT_HEAVY },
- { EV_KEY, KEY_KPENTER },
-#endif
-};
-static void gf_enable_irq(struct gf_dev *gf_dev)
-{
- if (gf_dev->irq_enabled) {
- pr_warn("IRQ has been enabled.\n");
- } else {
- enable_irq(gf_dev->irq);
- gf_dev->irq_enabled = 1;
- }
-}
+#define GF_DEV_NAME "goodix_fp"
+#define GF_IRQ_NAME "goodix_fp-irq"
+#define GF_DRIVER_NAME "goodix_fp_spi"
+#define GF_INPUT_NAME "uinput-goodix"
+#define GF_SPIDEV_NAME "goodix,fingerprint"
-static void gf_disable_irq(struct gf_dev *gf_dev)
-{
- if (gf_dev->irq_enabled) {
- gf_dev->irq_enabled = 0;
- disable_irq(gf_dev->irq);
- } else {
- pr_warn("IRQ has been disabled.\n");
- }
-}
+static struct gf_device gf;
-#ifdef AP_CONTROL_CLK
-static long spi_clk_max_rate(struct clk *clk, unsigned long rate)
+static void gf_hw_reset(struct gf_device *gf_dev, unsigned int delay_ms)
{
- long lowest_available, nearest_low, step_size, cur;
- long step_direction = -1;
- long guess = rate;
- int max_steps = 10;
-
- cur = clk_round_rate(clk, rate);
- if (cur == rate)
- return rate;
-
- /* if we got here then: cur > rate */
- lowest_available = clk_round_rate(clk, 0);
- if (lowest_available > rate)
- return -EINVAL;
-
- step_size = (rate - lowest_available) >> 1;
- nearest_low = lowest_available;
-
- while (max_steps-- && step_size) {
- guess += step_size * step_direction;
- cur = clk_round_rate(clk, guess);
-
- if ((cur < rate) && (cur > nearest_low))
- nearest_low = cur;
- /*
- * if we stepped too far, then start stepping in the other
- * direction with half the step size
- */
- if (((cur > rate) && (step_direction > 0))
- || ((cur < rate) && (step_direction < 0))) {
- step_direction = -step_direction;
- step_size >>= 1;
- }
- }
- return nearest_low;
+ gpio_set_value(gf_dev->reset_gpio, 0);
+ msleep(delay_ms);
+ gpio_set_value(gf_dev->reset_gpio, 1);
+ msleep(delay_ms);
}
-static void spi_clock_set(struct gf_dev *gf_dev, int speed)
-{
- long rate;
- int rc;
-
- rate = spi_clk_max_rate(gf_dev->core_clk, speed);
- if (rate < 0) {
- pr_debug("%s: no match found for requested clock frequency:%d",
- __func__, speed);
+static void gf_irq_config(struct gf_device *gf_dev, bool state) {
+ if (gf_dev->irq_enabled == state)
return;
- }
-
- rc = clk_set_rate(gf_dev->core_clk, rate);
-}
-
-static int gfspi_ioctl_clk_init(struct gf_dev *data)
-{
- pr_debug("%s: enter\n", __func__);
-
- data->clk_enabled = 0;
- data->core_clk = clk_get(&data->spi->dev, "core_clk");
- if (IS_ERR_OR_NULL(data->core_clk)) {
- pr_err("%s: fail to get core_clk\n", __func__);
- return -EPERM;
- }
- data->iface_clk = clk_get(&data->spi->dev, "iface_clk");
- if (IS_ERR_OR_NULL(data->iface_clk)) {
- pr_err("%s: fail to get iface_clk\n", __func__);
- clk_put(data->core_clk);
- data->core_clk = NULL;
- return -ENOENT;
- }
- return 0;
-}
-
-static int gfspi_ioctl_clk_enable(struct gf_dev *data)
-{
- int err;
- pr_debug("%s: enter\n", __func__);
-
- if (data->clk_enabled)
- return 0;
-
- err = clk_prepare_enable(data->core_clk);
- if (err) {
- pr_err("%s: fail to enable core_clk\n", __func__);
- return -EPERM;
- }
-
- err = clk_prepare_enable(data->iface_clk);
- if (err) {
- pr_err("%s: fail to enable iface_clk\n", __func__);
- clk_disable_unprepare(data->core_clk);
- return -ENOENT;
- }
-
- data->clk_enabled = 1;
-
- return 0;
-}
-
-static int gfspi_ioctl_clk_disable(struct gf_dev *data)
-{
- pr_debug("%s: enter\n", __func__);
-
- if (!data->clk_enabled)
- return 0;
-
- clk_disable_unprepare(data->core_clk);
- clk_disable_unprepare(data->iface_clk);
- data->clk_enabled = 0;
-
- return 0;
-}
-
-static int gfspi_ioctl_clk_uninit(struct gf_dev *data)
-{
- pr_debug("%s: enter\n", __func__);
-
- if (data->clk_enabled)
- gfspi_ioctl_clk_disable(data);
-
- if (!IS_ERR_OR_NULL(data->core_clk)) {
- clk_put(data->core_clk);
- data->core_clk = NULL;
- }
-
- if (!IS_ERR_OR_NULL(data->iface_clk)) {
- clk_put(data->iface_clk);
- data->iface_clk = NULL;
- }
-
- return 0;
-}
-#endif
-
-static void nav_event_input(struct gf_dev *gf_dev, gf_nav_event_t nav_event)
-{
- uint32_t nav_input = 0;
-
- switch (nav_event) {
- case GF_NAV_FINGER_DOWN:
- pr_debug("%s nav finger down\n", __func__);
- break;
-
- case GF_NAV_FINGER_UP:
- pr_debug("%s nav finger up\n", __func__);
- break;
-
- case GF_NAV_DOWN:
- nav_input = GF_NAV_INPUT_DOWN;
- pr_debug("%s nav down\n", __func__);
- break;
-
- case GF_NAV_UP:
- nav_input = GF_NAV_INPUT_UP;
- pr_debug("%s nav up\n", __func__);
- break;
-
- case GF_NAV_LEFT:
- nav_input = GF_NAV_INPUT_LEFT;
- pr_debug("%s nav left\n", __func__);
- break;
-
- case GF_NAV_RIGHT:
- nav_input = GF_NAV_INPUT_RIGHT;
- pr_debug("%s nav right\n", __func__);
- break;
-
- case GF_NAV_CLICK:
- nav_input = GF_NAV_INPUT_CLICK;
- pr_debug("%s nav click\n", __func__);
- break;
-
- case GF_NAV_HEAVY:
- nav_input = GF_NAV_INPUT_HEAVY;
- pr_debug("%s nav heavy\n", __func__);
- break;
-
- case GF_NAV_LONG_PRESS:
- nav_input = GF_NAV_INPUT_LONG_PRESS;
- pr_debug("%s nav long press\n", __func__);
- break;
-
- case GF_NAV_DOUBLE_CLICK:
- nav_input = GF_NAV_INPUT_DOUBLE_CLICK;
- pr_debug("%s nav double click\n", __func__);
- break;
-
- default:
- pr_warn("%s unknown nav event: %d\n", __func__, nav_event);
- break;
- }
+ if (state)
+ enable_irq(gf_dev->irq);
+ else
+ disable_irq(gf_dev->irq);
- if ((nav_event != GF_NAV_FINGER_DOWN) && (nav_event != GF_NAV_FINGER_UP)) {
- input_report_key(gf_dev->input, nav_input, 1);
- input_sync(gf_dev->input);
- input_report_key(gf_dev->input, nav_input, 0);
- input_sync(gf_dev->input);
- }
+ gf_dev->irq_enabled = state;
}
-
-static void gf_kernel_key_input(struct gf_dev *gf_dev, struct gf_key *gf_key)
+static void gf_kernel_key_input(struct gf_device *gf_dev, struct gf_key *gf_key)
{
- uint32_t key_input = 0;
-
- if (GF_KEY_HOME == gf_key->key) {
- key_input = GF_KEY_INPUT_HOME;
- } else if (GF_KEY_POWER == gf_key->key) {
- key_input = KEY_KPENTER;
- } else if (GF_KEY_CAMERA == gf_key->key) {
- key_input = GF_KEY_INPUT_CAMERA;
- } else {
- /* add special key define */
- key_input = gf_key->key;
- }
- pr_debug("%s: received key event[%d], key=%d, value=%d\n",
- __func__, key_input, gf_key->key, gf_key->value);
-
- if ((GF_KEY_POWER == gf_key->key || GF_KEY_CAMERA == gf_key->key)
- && (gf_key->value == 1)) {
- input_report_key(gf_dev->input, key_input, 1);
- input_sync(gf_dev->input);
- input_report_key(gf_dev->input, key_input, 0);
- input_sync(gf_dev->input);
- }
+ pr_debug("%s: received key, key=%d, value=%d\n",
+ __func__, gf_key->key, gf_key->value);
- if (GF_KEY_HOME == gf_key->key) {
- input_report_key(gf_dev->input, key_input, gf_key->value);
+ switch (gf_key->key) {
+ case GF_KEY_HOME:
+ input_report_key(gf_dev->input, GF_KEY_INPUT_HOME, gf_key->value);
input_sync(gf_dev->input);
+ break;
}
}
static long gf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
- struct gf_dev *gf_dev = &gf;
+ struct gf_device *gf_dev = &gf;
struct gf_key gf_key;
-#if defined(SUPPORT_NAV_EVENT)
- gf_nav_event_t nav_event = GF_NAV_NONE;
-#endif
- int retval = 0;
+ int rc = 0;
u8 netlink_route = NETLINK_TEST;
- struct gf_ioc_chip_info info;
-
- if (_IOC_TYPE(cmd) != GF_IOC_MAGIC)
- return -ENODEV;
-
- if (_IOC_DIR(cmd) & _IOC_READ)
- retval = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd));
- else if (_IOC_DIR(cmd) & _IOC_WRITE)
- retval = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd));
- if (retval)
- return -EFAULT;
-
- if (gf_dev->device_available == 0) {
- if ((cmd == GF_IOC_ENABLE_POWER) || (cmd == GF_IOC_DISABLE_POWER)) {
- pr_debug("power cmd\n");
- } else {
- pr_debug("Sensor is power off currently.\n");
- return -ENODEV;
- }
- }
switch (cmd) {
case GF_IOC_INIT:
- pr_debug("%s GF_IOC_INIT\n", __func__);
+ pr_debug("%s: GF_IOC_INIT\n", __func__);
if (copy_to_user((void __user *)arg, (void *)&netlink_route, sizeof(u8))) {
- retval = -EFAULT;
+ rc = -EFAULT;
break;
}
break;
- case GF_IOC_EXIT:
- pr_debug("%s GF_IOC_EXIT\n", __func__);
- break;
- case GF_IOC_DISABLE_IRQ:
- pr_debug("%s GF_IOC_DISABEL_IRQ\n", __func__);
- gf_disable_irq(gf_dev);
- break;
- case GF_IOC_ENABLE_IRQ:
- pr_debug("%s GF_IOC_ENABLE_IRQ\n", __func__);
- gf_enable_irq(gf_dev);
- break;
case GF_IOC_RESET:
- pr_debug("%s GF_IOC_RESET.\n", __func__);
+ pr_debug("%s: GF_IOC_RESET.\n", __func__);
gf_hw_reset(gf_dev, 3);
break;
case GF_IOC_INPUT_KEY_EVENT:
if (copy_from_user(&gf_key, (struct gf_key *)arg, sizeof(struct gf_key))) {
- pr_debug("Failed to copy input key event from user to kernel\n");
- retval = -EFAULT;
+ pr_err("%s: failed to copy input key event\n", __func__);
+ rc = -EFAULT;
break;
}
gf_kernel_key_input(gf_dev, &gf_key);
break;
-#if defined(SUPPORT_NAV_EVENT)
- case GF_IOC_NAV_EVENT:
- pr_debug("%s GF_IOC_NAV_EVENT\n", __func__);
- if (copy_from_user(&nav_event, (gf_nav_event_t *)arg, sizeof(gf_nav_event_t))) {
- pr_debug("Failed to copy nav event from user to kernel\n");
- retval = -EFAULT;
- break;
- }
-
- nav_event_input(gf_dev, nav_event);
- break;
-#endif
-
- case GF_IOC_ENABLE_SPI_CLK:
- pr_debug("%s GF_IOC_ENABLE_SPI_CLK\n", __func__);
-#ifdef AP_CONTROL_CLK
- gfspi_ioctl_clk_enable(gf_dev);
-#else
- pr_debug("Doesn't support control clock.\n");
-#endif
- break;
- case GF_IOC_DISABLE_SPI_CLK:
- pr_debug("%s GF_IOC_DISABLE_SPI_CLK\n", __func__);
-#ifdef AP_CONTROL_CLK
- gfspi_ioctl_clk_disable(gf_dev);
-#else
- pr_debug("Doesn't support control clock\n");
-#endif
- break;
- case GF_IOC_ENABLE_POWER:
- pr_debug("%s GF_IOC_ENABLE_POWER\n", __func__);
- if (gf_dev->device_available == 1)
- pr_debug("Sensor has already powered-on.\n");
- else
- gf_power_on(gf_dev);
- gf_dev->device_available = 1;
- break;
- case GF_IOC_DISABLE_POWER:
- pr_debug("%s GF_IOC_DISABLE_POWER\n", __func__);
- if (gf_dev->device_available == 0)
- pr_debug("Sensor has already powered-off.\n");
- else
- gf_power_off(gf_dev);
- gf_dev->device_available = 0;
- break;
- case GF_IOC_ENTER_SLEEP_MODE:
- pr_debug("%s GF_IOC_ENTER_SLEEP_MODE\n", __func__);
- break;
- case GF_IOC_GET_FW_INFO:
- pr_debug("%s GF_IOC_GET_FW_INFO\n", __func__);
- break;
- case GF_IOC_REMOVE:
- pr_debug("%s GF_IOC_REMOVE\n", __func__);
- break;
- case GF_IOC_CHIP_INFO:
- pr_debug("%s GF_IOC_CHIP_INFO\n", __func__);
- if (copy_from_user(&info, (struct gf_ioc_chip_info *)arg, sizeof(struct gf_ioc_chip_info))) {
- retval = -EFAULT;
- break;
- }
- pr_debug("vendor_id : 0x%x\n", info.vendor_id);
- pr_debug("mode : 0x%x\n", info.mode);
- pr_debug("operation: 0x%x\n", info.operation);
- break;
default:
- pr_warn("unsupport cmd:0x%x\n", cmd);
- break;
+ pr_debug("%s: unsupport cmd:0x%x\n", __func__, cmd);
}
- return retval;
+ return rc;
}
#ifdef CONFIG_COMPAT
static irqreturn_t gf_irq(int irq, void *handle)
{
-#if defined(GF_NETLINK_ENABLE)
- char temp[4] = { 0x0 };
- temp[0] = GF_NET_EVENT_IRQ;
- wake_lock_timeout(&fp_wakelock, msecs_to_jiffies(2*1000));
- sendnlmsg(temp);
-#elif defined (GF_FASYNC)
- struct gf_dev *gf_dev = &gf;
+ struct gf_device *gf_dev = handle;
- if (gf_dev->async)
- kill_fasync(&gf_dev->async, SIGIO, POLL_IN);
-#endif
+ gf_dev->event = GF_NET_EVENT_IRQ;
+ queue_work(gf_dev->event_workqueue, &gf_dev->event_work);
return IRQ_HANDLED;
}
static int gf_open(struct inode *inode, struct file *filp)
{
- struct gf_dev *gf_dev;
- int status = -ENXIO;
- int rc = 0;
+ struct gf_device *gf_dev = &gf;
+ int rc;
- mutex_lock(&device_list_lock);
+ /*
+ * If this is not the first user, skip hardware configuration.
+ */
+ if (++gf_dev->users != 1)
+ goto no_config;
- list_for_each_entry(gf_dev, &device_list, device_entry) {
- if (gf_dev->devt == inode->i_rdev) {
- pr_debug("Found\n");
- status = 0;
- break;
- }
+ rc = gpio_request(gf_dev->reset_gpio, "goodix_reset");
+ if (rc) {
+ pr_err("%s: failed to request reset_gpio, rc = %d\n", __func__, rc);
+ goto error_reset_gpio;
}
+ gpio_direction_output(gf_dev->reset_gpio, 1);
- if (status == 0) {
- if (status == 0) {
- rc = gpio_request(gf_dev->reset_gpio, "goodix_reset");
- if (rc) {
- dev_err(&gf_dev->spi->dev, "Failed to request RESET GPIO. rc = %d\n", rc);
- mutex_unlock(&device_list_lock);
- return -EPERM;
- }
-
- gpio_direction_output(gf_dev->reset_gpio, 1);
-
- rc = gpio_request(gf_dev->irq_gpio, "goodix_irq");
- if (rc) {
- dev_err(&gf_dev->spi->dev, "Failed to request IRQ GPIO. rc = %d\n", rc);
- mutex_unlock(&device_list_lock);
- return -EPERM;
- }
- gpio_direction_input(gf_dev->irq_gpio);
-
- rc = request_threaded_irq(gf_dev->irq, NULL, gf_irq,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- "gf", gf_dev);
-
- if (!rc) {
- enable_irq_wake(gf_dev->irq);
- gf_dev->irq_enabled = 1;
- gf_disable_irq(gf_dev);
- }
-
- gf_dev->users++;
- filp->private_data = gf_dev;
- nonseekable_open(inode, filp);
- pr_debug("Succeed to open device. irq = %d\n",
- gf_dev->irq);
- if (gf_dev->users == 1)
- gf_enable_irq(gf_dev);
- gf_hw_reset(gf_dev, 3);
- gf_dev->device_available = 1;
- }
- } else {
- pr_debug("No device for minor %d\n", iminor(inode));
+ rc = gpio_request(gf_dev->irq_gpio, "goodix_irq");
+ if (rc) {
+ pr_err("%s: failed to request irq_gpio, rc = %d\n", __func__, rc);
+ goto error_irq_gpio;
}
- mutex_unlock(&device_list_lock);
- return status;
-}
+ gpio_direction_input(gf_dev->irq_gpio);
-#ifdef GF_FASYNC
-static int gf_fasync(int fd, struct file *filp, int mode)
-{
- struct gf_dev *gf_dev = filp->private_data;
- int ret;
+ /*
+ * Requesting an irq also enables it.
+ */
+ gf_dev->irq_enabled = true;
+ rc = request_threaded_irq(gf_dev->irq, NULL, gf_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ GF_IRQ_NAME, gf_dev);
+ if (rc) {
+ pr_err("%s: failed to request threaded irq, rc = %d\n", __func__, rc);
+ goto error_irq_req;
+ }
+
+ gf_hw_reset(gf_dev, 3);
+
+no_config:
+ filp->private_data = gf_dev;
+ nonseekable_open(inode, filp);
+
+ return 0;
- ret = fasync_helper(fd, filp, mode, &gf_dev->async);
- pr_debug("ret = %d\n", ret);
- return ret;
+error_irq_req:
+ gpio_free(gf_dev->irq_gpio);
+error_irq_gpio:
+ gpio_free(gf_dev->reset_gpio);
+error_reset_gpio:
+ return rc;
}
-#endif
static int gf_release(struct inode *inode, struct file *filp)
{
- struct gf_dev *gf_dev;
- int status = 0;
+ struct gf_device *gf_dev = filp->private_data;
- mutex_lock(&device_list_lock);
- gf_dev = filp->private_data;
+ if (--gf_dev->users != 0)
+ goto no_config;
+
+ gf_irq_config(gf_dev, false);
+ free_irq(gf_dev->irq, gf_dev);
+ gpio_free(gf_dev->irq_gpio);
+ gpio_free(gf_dev->reset_gpio);
+
+no_config:
filp->private_data = NULL;
- /*last close?? */
- gf_dev->users--;
- if (!gf_dev->users) {
-
- pr_debug("disble_irq. irq = %d\n", gf_dev->irq);
- gf_disable_irq(gf_dev);
- /*power off the sensor*/
- gf_dev->device_available = 0;
- free_irq(gf_dev->irq, gf_dev);
- gpio_free(gf_dev->irq_gpio);
- gpio_free(gf_dev->reset_gpio);
- gf_power_off(gf_dev);
- }
- mutex_unlock(&device_list_lock);
- return status;
+ return 0;
}
static const struct file_operations gf_fops = {
#endif /*CONFIG_COMPAT*/
.open = gf_open,
.release = gf_release,
-#ifdef GF_FASYNC
- .fasync = gf_fasync,
-#endif
};
-static int goodix_fb_state_chg_callback(struct notifier_block *nb,
- unsigned long val, void *data)
+#define FINGERPRINT_PROCESSING_MS 2000
+static void gf_event_worker(struct work_struct *work)
+{
+ struct gf_device *gf_dev = container_of(work, typeof(*gf_dev), event_work);
+ char temp[4] = {0x0};
+
+ switch (gf_dev->event) {
+ case GF_NET_EVENT_FB_BLACK:
+ gf_dev->display_on = false;
+ break;
+ case GF_NET_EVENT_FB_UNBLACK:
+ gf_dev->display_on = true;
+ break;
+ /*
+ * IRQs are followed by fingerprint procesing, hold a wakelock to make
+ * sure the fingerprint is processed when screen is off.
+ */
+ case GF_NET_EVENT_IRQ:
+ if (gf_dev->display_on)
+ break;
+
+ wake_lock_timeout(&gf_dev->fp_wakelock,
+ msecs_to_jiffies(FINGERPRINT_PROCESSING_MS));
+ break;
+ }
+
+ temp[0] = gf_dev->event;
+
+ sendnlmsg(temp);
+}
+
+static int gf_fb_state_callback(struct notifier_block *nb,
+ unsigned long type, void *data)
{
- struct gf_dev *gf_dev;
struct fb_event *evdata = data;
+ struct gf_device *gf_dev;
unsigned int blank;
- char temp[4] = { 0x0 };
-
- if (val != FB_EVENT_BLANK)
- return 0;
- pr_debug("[info] %s go to the goodix_fb_state_chg_callback value = %d\n",
- __func__, (int)val);
- gf_dev = container_of(nb, struct gf_dev, notifier);
- if (evdata && evdata->data && val == FB_EVENT_BLANK && gf_dev) {
- blank = *(int *)(evdata->data);
- switch (blank) {
- case FB_BLANK_POWERDOWN:
- if (gf_dev->device_available == 1) {
- gf_dev->fb_black = 1;
-#if defined(GF_NETLINK_ENABLE)
- temp[0] = GF_NET_EVENT_FB_BLACK;
- sendnlmsg(temp);
-#elif defined (GF_FASYNC)
- if (gf_dev->async) {
- kill_fasync(&gf_dev->async, SIGIO, POLL_IN);
- }
-#endif
- }
- break;
- case FB_BLANK_UNBLANK:
- if (gf_dev->device_available == 1) {
- gf_dev->fb_black = 0;
-#if defined(GF_NETLINK_ENABLE)
- temp[0] = GF_NET_EVENT_FB_UNBLACK;
- sendnlmsg(temp);
-#elif defined (GF_FASYNC)
- if (gf_dev->async) {
- kill_fasync(&gf_dev->async, SIGIO, POLL_IN);
- }
-#endif
- }
- break;
- default:
- pr_debug("%s defalut\n", __func__);
- break;
- }
+
+ if (type != FB_EVENT_BLANK)
+ goto end;
+
+ if (!evdata || !evdata->data)
+ goto end;
+
+ pr_debug("%s: type=%d\n", __func__, (int)type);
+
+ gf_dev = container_of(nb, struct gf_device, notifier);
+
+ blank = *(int *)(evdata->data);
+ switch (blank) {
+ case FB_BLANK_POWERDOWN:
+ gf_dev->event = GF_NET_EVENT_FB_BLACK;
+ break;
+ case FB_BLANK_UNBLANK:
+ gf_dev->event = GF_NET_EVENT_FB_UNBLACK;
+ break;
+ default:
+ goto end;
}
+
+ queue_work(gf_dev->event_workqueue, &gf_dev->event_work);
+
+end:
return NOTIFY_OK;
}
-static struct notifier_block goodix_noti_block = {
- .notifier_call = goodix_fb_state_chg_callback,
+static struct notifier_block gf_fb_notifier = {
+ .notifier_call = gf_fb_state_callback,
};
-static struct class *gf_class;
-#if defined(USE_SPI_BUS)
-static int gf_probe(struct spi_device *spi)
-#elif defined(USE_PLATFORM_BUS)
static int gf_probe(struct platform_device *pdev)
-#endif
{
- struct gf_dev *gf_dev = &gf;
- int status = -EINVAL;
- unsigned long minor;
- int i;
-
- /* Initialize the driver data */
- INIT_LIST_HEAD(&gf_dev->device_entry);
-#if defined(USE_SPI_BUS)
- gf_dev->spi = spi;
-#elif defined(USE_PLATFORM_BUS)
- gf_dev->spi = pdev;
-#endif
- gf_dev->irq_gpio = -EINVAL;
- gf_dev->reset_gpio = -EINVAL;
- gf_dev->pwr_gpio = -EINVAL;
- gf_dev->device_available = 0;
- gf_dev->fb_black = 0;
-
- if (gf_parse_dts(gf_dev))
- goto error_hw;
-
- /* If we can allocate a minor number, hook up this device.
- * Reusing minors is fine so long as udev or mdev is working.
- */
- mutex_lock(&device_list_lock);
- minor = find_first_zero_bit(minors, N_SPI_MINORS);
- if (minor < N_SPI_MINORS) {
- struct device *dev;
+ struct gf_device *gf_dev = &gf;
+ struct device *dev;
+ int major;
+ int rc = 0;
- gf_dev->devt = MKDEV(SPIDEV_MAJOR, minor);
- dev = device_create(gf_class, &gf_dev->spi->dev, gf_dev->devt,
- gf_dev, GF_DEV_NAME);
- status = IS_ERR(dev) ? PTR_ERR(dev) : 0;
- } else {
- dev_dbg(&gf_dev->spi->dev, "no minor number available!\n");
- status = -ENODEV;
- mutex_unlock(&device_list_lock);
- goto error_hw;
- }
+ gf_dev->display_on = true;
+ gf_dev->irq_enabled = false;
- if (status == 0) {
- set_bit(minor, minors);
- list_add(&gf_dev->device_entry, &device_list);
- } else {
- gf_dev->devt = 0;
+ gf_dev->reset_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "fp-gpio-reset", 0);
+ if (!gpio_is_valid(gf_dev->reset_gpio)) {
+ pr_err("%s: failed to get reset_gpio, rc = %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto error_dt;
}
- mutex_unlock(&device_list_lock);
-
- if (status == 0) {
- /*input device subsystem */
- gf_dev->input = input_allocate_device();
- if (gf_dev->input == NULL) {
- pr_err("%s, failed to allocate input device\n", __func__);
- status = -ENOMEM;
- goto error_dev;
- }
- for (i = 0; i < ARRAY_SIZE(maps); i++)
- input_set_capability(gf_dev->input, maps[i].type, maps[i].code);
-
- gf_dev->input->name = GF_INPUT_NAME;
- status = input_register_device(gf_dev->input);
- if (status) {
- pr_err("failed to register input device\n");
- goto error_input;
- }
+
+ gf_dev->irq_gpio = of_get_named_gpio(pdev->dev.of_node,
+ "fp-gpio-irq", 0);
+ if (!gpio_is_valid(gf_dev->irq_gpio)) {
+ pr_err("%s: failed to get irq_gpio, rc = %d\n", __func__, rc);
+ rc = -EINVAL;
+ goto error_dt;
}
-#ifdef AP_CONTROL_CLK
- pr_debug("Get the clk resource.\n");
- /* Enable spi clock */
- if (gfspi_ioctl_clk_init(gf_dev))
- goto gfspi_probe_clk_init_failed;
- if (gfspi_ioctl_clk_enable(gf_dev))
- goto gfspi_probe_clk_enable_failed;
+ gf_dev->irq = gpio_to_irq(gf_dev->irq_gpio);
+ enable_irq_wake(gf_dev->irq);
- spi_clock_set(gf_dev, 1000000);
-#endif
+ major = register_chrdev(0, GF_DRIVER_NAME, &gf_fops);
+ if (major < 0) {
+ pr_err("%s: failed to register char device\n", __func__);
+ rc = major;
+ goto error_chardev;
+ }
- gf_dev->notifier = goodix_noti_block;
- fb_register_client(&gf_dev->notifier);
+ gf_dev->cls = class_create(THIS_MODULE, GF_DEV_NAME);
+ if (IS_ERR(gf_dev->cls)) {
+ pr_err("%s: failed to create device class\n", __func__);
+ rc = PTR_ERR(gf_dev->cls);
+ goto error_class;
+ }
- gf_dev->irq = gf_irq_num(gf_dev);
+ gf_dev->devt = MKDEV(major, 0);
+ dev = device_create(gf_dev->cls, &pdev->dev, gf_dev->devt,
+ gf_dev, GF_DEV_NAME);
+ if (IS_ERR(dev)) {
+ pr_err("%s: failed to create device\n", __func__);
+ rc = PTR_ERR(dev);
+ goto error_device;
+ }
- wake_lock_init(&fp_wakelock, WAKE_LOCK_SUSPEND, "fp_wakelock");
- pr_debug("version V%d.%d.%02d\n", VER_MAJOR, VER_MINOR, PATCH_LEVEL);
+ gf_dev->input = input_allocate_device();
+ if (!gf_dev->input) {
+ pr_err("%s: failed to allocate input device\n", __func__);
+ rc = -ENOMEM;
+ goto error_input_alloc;
+ }
- return status;
+ input_set_capability(gf_dev->input, EV_KEY, GF_KEY_INPUT_HOME);
-#ifdef AP_CONTROL_CLK
-gfspi_probe_clk_enable_failed:
- gfspi_ioctl_clk_uninit(gf_dev);
-gfspi_probe_clk_init_failed:
-#endif
- input_unregister_device(gf_dev->input);
-error_input:
- if (gf_dev->input != NULL)
- input_free_device(gf_dev->input);
-error_dev:
- if (gf_dev->devt != 0) {
- pr_debug("Err: status = %d\n", status);
- mutex_lock(&device_list_lock);
- list_del(&gf_dev->device_entry);
- device_destroy(gf_class, gf_dev->devt);
- clear_bit(MINOR(gf_dev->devt), minors);
- mutex_unlock(&device_list_lock);
+ gf_dev->input->name = GF_INPUT_NAME;
+ rc = input_register_device(gf_dev->input);
+ if (rc) {
+ pr_err("%s: failed to register input device\n", __func__);
+ goto error_input_register;
}
-error_hw:
- gf_cleanup(gf_dev);
- gf_dev->device_available = 0;
- return status;
+ gf_dev->notifier = gf_fb_notifier;
+ fb_register_client(&gf_dev->notifier);
+ gf_dev->event_workqueue = alloc_workqueue("gf-event-wq",
+ WQ_MEM_RECLAIM | WQ_HIGHPRI, 0);
+ INIT_WORK(&gf_dev->event_work, gf_event_worker);
+
+ wake_lock_init(&gf_dev->fp_wakelock, WAKE_LOCK_SUSPEND, "fp_wakelock");
+
+ netlink_init();
+
+ return 0;
+
+error_input_register:
+ input_free_device(gf_dev->input);
+error_input_alloc:
+ device_destroy(gf_dev->cls, gf_dev->devt);
+error_device:
+ class_destroy(gf_dev->cls);
+error_class:
+ unregister_chrdev(MAJOR(gf_dev->devt), GF_DEV_NAME);
+error_chardev:
+error_dt:
+ return rc;
}
-#if defined(USE_SPI_BUS)
-static int gf_remove(struct spi_device *spi)
-#elif defined(USE_PLATFORM_BUS)
static int gf_remove(struct platform_device *pdev)
-#endif
{
- struct gf_dev *gf_dev = &gf;
+ struct gf_device *gf_dev = &gf;
- wake_lock_destroy(&fp_wakelock);
- /* make sure ops on existing fds can abort cleanly */
- if (gf_dev->irq)
- free_irq(gf_dev->irq, gf_dev);
-
- if (gf_dev->input != NULL)
- input_unregister_device(gf_dev->input);
- input_free_device(gf_dev->input);
+ netlink_exit();
- /* prevent new opens */
- mutex_lock(&device_list_lock);
- list_del(&gf_dev->device_entry);
- device_destroy(gf_class, gf_dev->devt);
- clear_bit(MINOR(gf_dev->devt), minors);
- if (gf_dev->users == 0)
- gf_cleanup(gf_dev);
+ wake_lock_destroy(&gf_dev->fp_wakelock);
+ destroy_workqueue(gf_dev->event_workqueue);
fb_unregister_client(&gf_dev->notifier);
- mutex_unlock(&device_list_lock);
+
+ input_unregister_device(gf_dev->input);
+
+ device_destroy(gf_dev->cls, gf_dev->devt);
+ class_destroy(gf_dev->cls);
+ unregister_chrdev(MAJOR(gf_dev->devt), GF_DEV_NAME);
return 0;
}
-static struct of_device_id gx_match_table[] = {
+static struct of_device_id gf_match_table[] = {
{ .compatible = GF_SPIDEV_NAME },
{},
};
-#if defined(USE_SPI_BUS)
-static struct spi_driver gf_driver = {
-#elif defined(USE_PLATFORM_BUS)
static struct platform_driver gf_driver = {
-#endif
.driver = {
.name = GF_DEV_NAME,
.owner = THIS_MODULE,
- .of_match_table = gx_match_table,
+ .of_match_table = gf_match_table,
},
.probe = gf_probe,
.remove = gf_remove,
};
-static int __init gf_init(void)
-{
- int status;
-
- /* Claim our 256 reserved device numbers. Then register a class
- * that will key udev/mdev to add/remove /dev nodes. Last, register
- * the driver which manages those device numbers.
- */
-
- BUILD_BUG_ON(N_SPI_MINORS > 256);
- status = register_chrdev(SPIDEV_MAJOR, CHRD_DRIVER_NAME, &gf_fops);
- if (status < 0) {
- pr_warn("Failed to register char device!\n");
- return status;
- }
- SPIDEV_MAJOR = status;
- gf_class = class_create(THIS_MODULE, CLASS_NAME);
- if (IS_ERR(gf_class)) {
- unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name);
- pr_warn("Failed to create class.\n");
- return PTR_ERR(gf_class);
- }
-#if defined(USE_PLATFORM_BUS)
- status = platform_driver_register(&gf_driver);
-#elif defined(USE_SPI_BUS)
- status = spi_register_driver(&gf_driver);
-#endif
- if (status < 0) {
- class_destroy(gf_class);
- unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name);
- pr_warn("Failed to register SPI driver.\n");
- }
-
-#ifdef GF_NETLINK_ENABLE
- netlink_init();
-#endif
- pr_debug("status = 0x%x\n", status);
- return 0;
-}
-module_init(gf_init);
-
-static void __exit gf_exit(void)
-{
-#ifdef GF_NETLINK_ENABLE
- netlink_exit();
-#endif
-#if defined(USE_PLATFORM_BUS)
- platform_driver_unregister(&gf_driver);
-#elif defined(USE_SPI_BUS)
- spi_unregister_driver(&gf_driver);
-#endif
- class_destroy(gf_class);
- unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name);
-}
-module_exit(gf_exit);
+module_platform_driver(gf_driver);
MODULE_AUTHOR("Jiangtao Yi, <yijiangtao@goodix.com>");
MODULE_AUTHOR("Jandy Gou, <gouqingsong@goodix.com>");