From ecf86d43850883d12db30b9cd282a5505be7df0c Mon Sep 17 00:00:00 2001 From: "jerry.zhang" Date: Thu, 18 Aug 2016 09:46:02 +0800 Subject: [PATCH] staging: android: add timed gpio driver to present vibrator control interface to userspace Change-Id: I725a8620669c4286dda5bcbbbd1b984843a6b006 Signed-off-by: jerry.zhang --- drivers/staging/android/Makefile | 2 +- drivers/staging/android/rk_timed_gpio.c | 161 ++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 drivers/staging/android/rk_timed_gpio.c diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index fcb7edca24c9..bca0343272ef 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -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 index 000000000000..77814ef41ca3 --- /dev/null +++ b/drivers/staging/android/rk_timed_gpio.c @@ -0,0 +1,161 @@ +/* drivers/staging/android/rk_timed_gpio.c + * + * Copyright (C) 2012-2016 ROCKCHIP. + * Author: jerry + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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"); -- 2.34.1