/* * drivers/misc/max1749.c * * Driver for MAX1749, vibrator motor driver. * * Copyright (c) 2011-2013 NVIDIA Corporation, 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 as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "../staging/android/timed_output.h" struct vibrator_data { struct timed_output_dev dev; struct hrtimer timer; struct regulator *regulator; struct work_struct work; bool vibrator_on; }; static struct vibrator_data *data; static void vibrator_start(void) { if (!data->vibrator_on) { regulator_enable(data->regulator); data->vibrator_on = true; } } static void vibrator_stop(void) { int ret; if (data->vibrator_on) { ret = regulator_is_enabled(data->regulator); if (ret > 0) { regulator_disable(data->regulator); data->vibrator_on = false; } } } static void vibrator_work_func(struct work_struct *work) { vibrator_stop(); } static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) { schedule_work(&data->work); return HRTIMER_NORESTART; } /* * Timeout value can be changed from sysfs entry * created by timed_output_dev. * echo 100 > /sys/class/timed_output/vibrator/enable */ static void vibrator_enable(struct timed_output_dev *dev, int value) { hrtimer_cancel(&data->timer); if (value > 0) { vibrator_start(); hrtimer_start(&data->timer, ktime_set(value / 1000, (value % 1000) * 1000000), HRTIMER_MODE_REL); } else vibrator_stop(); return; } static int vibrator_get_time(struct timed_output_dev *dev) { if (hrtimer_active(&data->timer)) { ktime_t r = hrtimer_get_remaining(&data->timer); struct timeval t = ktime_to_timeval(r); return t.tv_sec * 1000 + t.tv_usec / 1000; } else return 0; } static struct timed_output_dev vibrator_dev = { .name = "vibrator", .get_time = vibrator_get_time, .enable = vibrator_enable, }; static int vibrator_probe(struct platform_device *pdev) { int ret; data = kzalloc(sizeof(struct vibrator_data), GFP_KERNEL); if (!data) return -ENOMEM; data->regulator = regulator_get(NULL, "vdd_vbrtr"); if (IS_ERR_OR_NULL(data->regulator)) { pr_err("vibrator_init:Couldn't get regulator vdd_vbrtr\n"); data->regulator = NULL; ret = PTR_ERR(data->regulator); goto err; } hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); /* Intialize the work queue */ INIT_WORK(&data->work, vibrator_work_func); data->timer.function = vibrator_timer_func; data->dev = vibrator_dev; data->vibrator_on = false; ret = timed_output_dev_register(&data->dev); if (ret) goto err2; return 0; err2: regulator_put(data->regulator); err: kfree(data); return ret; } static int vibrator_remove(struct platform_device *pdev) { timed_output_dev_unregister(&data->dev); regulator_put(data->regulator); kfree(data); return 0; } static struct platform_driver vibrator_driver = { .probe = vibrator_probe, .remove = vibrator_remove, .driver = { .name = "tegra-vibrator", .owner = THIS_MODULE, }, }; static int __init vibrator_init(void) { return platform_driver_register(&vibrator_driver); } static void __exit vibrator_exit(void) { platform_driver_unregister(&vibrator_driver); } MODULE_DESCRIPTION("timed output vibrator device"); MODULE_AUTHOR("GPL"); module_init(vibrator_init); module_exit(vibrator_exit);