staging: android: add timed gpio driver to present vibrator control interface to...
authorjerry.zhang <jerry.zhang@rock-chips.com>
Thu, 18 Aug 2016 01:46:02 +0000 (09:46 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Mon, 5 Sep 2016 06:50:28 +0000 (14:50 +0800)
Change-Id: I725a8620669c4286dda5bcbbbd1b984843a6b006
Signed-off-by: jerry.zhang <jerry.zhang@rock-chips.com>
drivers/staging/android/Makefile
drivers/staging/android/rk_timed_gpio.c [new file with mode: 0644]

index fcb7edc..bca0343 100644 (file)
@@ -5,7 +5,7 @@ obj-$(CONFIG_FIQ_DEBUGGER)              += fiq_debugger/
 
 obj-$(CONFIG_ASHMEM)                   += ashmem.o
 obj-$(CONFIG_ANDROID_TIMED_OUTPUT)     += timed_output.o
-obj-$(CONFIG_ANDROID_TIMED_GPIO)       += timed_gpio.o
+obj-$(CONFIG_ANDROID_TIMED_GPIO)       += timed_gpio.o rk_timed_gpio.o
 obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER)        += lowmemorykiller.o
 obj-$(CONFIG_SYNC)                     += sync.o sync_debug.o
 obj-$(CONFIG_SW_SYNC)                  += sw_sync.o
diff --git a/drivers/staging/android/rk_timed_gpio.c b/drivers/staging/android/rk_timed_gpio.c
new file mode 100644 (file)
index 0000000..77814ef
--- /dev/null
@@ -0,0 +1,161 @@
+/* drivers/staging/android/rk_timed_gpio.c
+ *
+ * Copyright (C) 2012-2016 ROCKCHIP.
+ * Author: jerry <jerry.zhang@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include "timed_output.h"
+
+#define MAX_TIMEOUT        10000       /* 10s */
+
+static struct vibrator {
+       int gpio;
+       struct wake_lock wklock;
+       struct hrtimer timer;
+       struct mutex lock;      /* mutex lock */
+       struct work_struct work;
+} vibdata;
+
+static void rk_vibrator_off(void)
+{
+       gpio_direction_output(vibdata.gpio, 0);
+       gpio_set_value(vibdata.gpio, 0);
+
+       wake_unlock(&vibdata.wklock);
+}
+
+static void rk_vibrator_enable(struct timed_output_dev *sdev, int value)
+{
+       mutex_lock(&vibdata.lock);
+
+       /* cancel previous timer and set GPIO according to value */
+       hrtimer_cancel(&vibdata.timer);
+       cancel_work_sync(&vibdata.work);
+       if (value) {
+               wake_lock(&vibdata.wklock);
+               gpio_direction_output(vibdata.gpio, 1);
+               gpio_set_value(vibdata.gpio, 1);
+
+               if (value > 0) {
+                       if (value > MAX_TIMEOUT)
+                               value = MAX_TIMEOUT;
+               value += 45;
+               hrtimer_start(&vibdata.timer,
+                       ns_to_ktime((u64)value * NSEC_PER_MSEC),
+                       HRTIMER_MODE_REL);
+               }
+       } else {
+               rk_vibrator_off();
+       }
+
+       mutex_unlock(&vibdata.lock);
+}
+
+static int rk_vibrator_get_time(struct timed_output_dev *sdev)
+{
+       if (hrtimer_active(&vibdata.timer)) {
+               ktime_t r = hrtimer_get_remaining(&vibdata.timer);
+
+               return ktime_to_ms(r);
+       }
+
+       return 0;
+}
+
+static enum hrtimer_restart rk_vibrator_timer_func(struct hrtimer *timer)
+{
+       schedule_work(&vibdata.work);
+       return HRTIMER_NORESTART;
+}
+
+static void rk_vibrator_work(struct work_struct *work)
+{
+       rk_vibrator_off();
+}
+
+struct timed_output_dev rk_vibrator_driver = {
+       .name = "vibrator",
+       .enable = rk_vibrator_enable,
+       .get_time = rk_vibrator_get_time,
+};
+
+static int vibrator_probe(struct platform_device *pdev)
+{
+       int ret = 0;
+       struct device_node *np = pdev->dev.of_node;
+
+       vibdata.gpio = of_get_named_gpio_flags(np, "vibrator-gpio", 0, NULL);
+       if (!gpio_is_valid(vibdata.gpio))
+               return -1;
+
+       hrtimer_init(&vibdata.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+       vibdata.timer.function = rk_vibrator_timer_func;
+       INIT_WORK(&vibdata.work, rk_vibrator_work);
+
+       ret = devm_gpio_request(&pdev->dev, vibdata.gpio, "vibrator");
+       if (ret < 0)
+               return ret;
+
+       wake_lock_init(&vibdata.wklock, WAKE_LOCK_SUSPEND, "vibrator");
+       mutex_init(&vibdata.lock);
+       ret = timed_output_dev_register(&rk_vibrator_driver);
+       if (ret < 0)
+               goto err_to_dev_reg;
+
+       return 0;
+
+err_to_dev_reg:
+       mutex_destroy(&vibdata.lock);
+       wake_lock_destroy(&vibdata.wklock);
+
+       return ret;
+}
+
+static int vibrator_remove(struct platform_device *pdev)
+{
+       mutex_destroy(&vibdata.lock);
+       wake_lock_destroy(&vibdata.wklock);
+       timed_output_dev_unregister(&rk_vibrator_driver);
+
+       return 0;
+}
+
+static const struct of_device_id vibrator_of_match[] = {
+       { .compatible = "rk-vibrator-gpio" },
+       { }
+};
+
+static struct platform_driver vibrator_driver = {
+       .probe = vibrator_probe,
+       .remove = vibrator_remove,
+       .driver = {
+               .name           = "rk-vibrator",
+               .of_match_table = of_match_ptr(vibrator_of_match),
+       },
+};
+
+module_platform_driver(vibrator_driver);
+
+MODULE_AUTHOR("jerry.zhang@rock-chips.com");
+MODULE_DESCRIPTION("RK Vibrator driver");
+MODULE_LICENSE("GPL");