diff options
| author | Doug Zobel <dzobel1@motorola.com> | 2013-11-15 14:29:07 -0600 | 
|---|---|---|
| committer | James Wylder <jwylder@motorola.com> | 2014-03-05 17:46:52 -0600 | 
| commit | d2a782003a6047da120a33e6f8ee6fd33bb825d6 (patch) | |
| tree | 8d20bd4ecda62a06e98993c4108456bc1acb0d0b /drivers/misc/vib-gpio.c | |
| parent | 32fd2d36d2464056d4522a9c02797b7c2b2e884f (diff) | |
| download | olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.tar.xz olio-linux-3.10-d2a782003a6047da120a33e6f8ee6fd33bb825d6.zip | |
CW integration and minnow bringup
  * create minnow machine type
  * create Android makefile
  * add pre-commit syntax check
  * enable -Werror
  * Add drivers: CPCAP, TPS65xxx, m4sensorhub, atmxt, lm3535,
                 usb gadget, minnow display, TI 12xx wireless
Change-Id: I7962f5e1256715f2452aed5a62a4f2f2383d5046
Diffstat (limited to 'drivers/misc/vib-gpio.c')
| -rw-r--r-- | drivers/misc/vib-gpio.c | 224 | 
1 files changed, 224 insertions, 0 deletions
| diff --git a/drivers/misc/vib-gpio.c b/drivers/misc/vib-gpio.c new file mode 100644 index 00000000000..d80d34db9f3 --- /dev/null +++ b/drivers/misc/vib-gpio.c @@ -0,0 +1,224 @@ +/* drivers/misc/vib-gpio.c + * + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2008 Google, Inc. + * Author: Mike Lockwood <lockwood@android.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/err.h> +#include <linux/gpio.h> +#include <linux/hrtimer.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/vib-gpio.h> +#include <linux/workqueue.h> +#include <linux/slab.h> + +/* TODO: replace with correct header */ +#include "../staging/android/timed_output.h" + +struct vib_gpio_data { +	struct timed_output_dev dev; +	struct work_struct vib_work; +	struct hrtimer timer; +	spinlock_t lock; + +	struct vib_gpio_platform_data *pdata; + +	int vib_power_state; +	int vib_state; +}; + +struct vib_gpio_data *misc_data; + +static void vib_gpio_set(int on) +{ +	if (on) { +		if (misc_data->pdata->power_on && !misc_data->vib_power_state) { +			misc_data->pdata->power_on(); +			misc_data->vib_power_state = 1; +		} +		if (misc_data->pdata->gpio >= 0) +			gpio_direction_output(misc_data->pdata->gpio, +					      misc_data->pdata->active_low ? +						 0 : 1); +	} else { +		if (misc_data->pdata->gpio >= 0) +			gpio_direction_output(misc_data->pdata->gpio, +					      misc_data->pdata->active_low ? +						 1 : 0); + +		if (misc_data->pdata->power_off && misc_data->vib_power_state) { +			misc_data->pdata->power_off(); +			misc_data->vib_power_state = 0; +		} +	} +} + +static void vib_gpio_update(struct work_struct *work) +{ +	vib_gpio_set(misc_data->vib_state); +} + +static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer) +{ +	struct vib_gpio_data *data = +	    container_of(timer, struct vib_gpio_data, timer); +	data->vib_state = 0; +	schedule_work(&data->vib_work); +	return HRTIMER_NORESTART; +} + +static int vib_gpio_get_time(struct timed_output_dev *dev) +{ +	struct vib_gpio_data *data = +	    container_of(dev, struct vib_gpio_data, 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 void vib_gpio_enable(struct timed_output_dev *dev, int value) +{ +	struct vib_gpio_data *data = +	    container_of(dev, struct vib_gpio_data, dev); +	unsigned long flags; + +	spin_lock_irqsave(&data->lock, flags); +	hrtimer_cancel(&data->timer); + +	if (value == 0) +		data->vib_state = 0; +	else { +		value = (value > data->pdata->max_timeout ? +				 data->pdata->max_timeout : value); +		data->vib_state = 1; +		hrtimer_start(&data->timer, +			      ktime_set(value / 1000, (value % 1000) * 1000000), +			      HRTIMER_MODE_REL); +	} + +	spin_unlock_irqrestore(&data->lock, flags); + +	schedule_work(&data->vib_work); +} + +/* This is a temporary solution until a more global haptics soltion is + * available for haptics that need to occur in any application */ +void vibrator_haptic_fire(int value) +{ +	vib_gpio_enable(&misc_data->dev, value); +} + +static int vib_gpio_probe(struct platform_device *pdev) +{ +	struct vib_gpio_platform_data *pdata = pdev->dev.platform_data; +	struct vib_gpio_data *gpio_data; +	int ret = 0; + +	if (!pdata) { +		ret = -EBUSY; +		goto err0; +	} +	gpio_data = kzalloc(sizeof(struct vib_gpio_data), GFP_KERNEL); +	if (!gpio_data) { +		ret = -ENOMEM; +		goto err0; +	} + +	gpio_data->pdata = pdata; + +	INIT_WORK(&gpio_data->vib_work, vib_gpio_update); + +	hrtimer_init(&gpio_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + +	gpio_data->timer.function = gpio_timer_func; +	spin_lock_init(&gpio_data->lock); + +	gpio_data->dev.name = "vibrator"; +	gpio_data->dev.get_time = vib_gpio_get_time; +	gpio_data->dev.enable = vib_gpio_enable; +	ret = timed_output_dev_register(&gpio_data->dev); +	if (ret < 0) +		goto err1; + +	if (gpio_data->pdata->init) +		ret = gpio_data->pdata->init(); +	if (ret < 0) +		goto err2; + +	misc_data = gpio_data; +	if (misc_data->pdata->gpio >= 0) +		gpio_direction_output(gpio_data->pdata->gpio, +				      gpio_data->pdata->active_low); + +	platform_set_drvdata(pdev, gpio_data); + +	vib_gpio_enable(&gpio_data->dev, gpio_data->pdata->initial_vibrate); + +	pr_info("vib gpio probe done"); +	return 0; + +err2: +	timed_output_dev_unregister(&gpio_data->dev); +err1: +	kfree(gpio_data->pdata); +	kfree(gpio_data); +err0: +	return ret; +} + +static int vib_gpio_remove(struct platform_device *pdev) +{ +	struct vib_gpio_data *gpio_data = platform_get_drvdata(pdev); + +	if (gpio_data->pdata->exit) +		gpio_data->pdata->exit(); + +	timed_output_dev_unregister(&gpio_data->dev); + +	kfree(gpio_data->pdata); +	kfree(gpio_data); + +	return 0; +} + +static struct platform_driver vib_gpio_driver = { +	.probe = vib_gpio_probe, +	.remove = vib_gpio_remove, +	.driver = { +		   .name = VIB_GPIO_NAME, +		   .owner = THIS_MODULE, +		   }, +}; + +static int __init vib_gpio_init(void) +{ +	return platform_driver_register(&vib_gpio_driver); +} + +static void __exit vib_gpio_exit(void) +{ +	platform_driver_unregister(&vib_gpio_driver); +} + +late_initcall(vib_gpio_init); +module_exit(vib_gpio_exit); + +MODULE_AUTHOR("Motorola"); +MODULE_DESCRIPTION("vib gpio driver"); +MODULE_LICENSE("GPL"); |