Path: blob/master/drivers/input/misc/twl4030-vibra.c
15111 views
/*1* twl4030-vibra.c - TWL4030 Vibrator driver2*3* Copyright (C) 2008-2010 Nokia Corporation4*5* Written by Henrik Saari <[email protected]>6* Updates by Felipe Balbi <[email protected]>7* Input by Jari Vanhala <[email protected]>8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License version 2 as11* published by the Free Software Foundation.12*13* This program is distributed in the hope that it will be useful, but14* WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU16* General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA21* 02110-1301 USA22*23*/2425#include <linux/module.h>26#include <linux/jiffies.h>27#include <linux/platform_device.h>28#include <linux/workqueue.h>29#include <linux/i2c/twl.h>30#include <linux/mfd/twl4030-codec.h>31#include <linux/input.h>32#include <linux/slab.h>3334/* MODULE ID2 */35#define LEDEN 0x003637/* ForceFeedback */38#define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */3940struct vibra_info {41struct device *dev;42struct input_dev *input_dev;4344struct workqueue_struct *workqueue;45struct work_struct play_work;4647bool enabled;48int speed;49int direction;5051bool coexist;52};5354static void vibra_disable_leds(void)55{56u8 reg;5758/* Disable LEDA & LEDB, cannot be used with vibra (PWM) */59twl_i2c_read_u8(TWL4030_MODULE_LED, ®, LEDEN);60reg &= ~0x03;61twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg);62}6364/* Powers H-Bridge and enables audio clk */65static void vibra_enable(struct vibra_info *info)66{67u8 reg;6869twl4030_codec_enable_resource(TWL4030_CODEC_RES_POWER);7071/* turn H-Bridge on */72twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,73®, TWL4030_REG_VIBRA_CTL);74twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,75(reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);7677twl4030_codec_enable_resource(TWL4030_CODEC_RES_APLL);7879info->enabled = true;80}8182static void vibra_disable(struct vibra_info *info)83{84u8 reg;8586/* Power down H-Bridge */87twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,88®, TWL4030_REG_VIBRA_CTL);89twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,90(reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);9192twl4030_codec_disable_resource(TWL4030_CODEC_RES_APLL);93twl4030_codec_disable_resource(TWL4030_CODEC_RES_POWER);9495info->enabled = false;96}9798static void vibra_play_work(struct work_struct *work)99{100struct vibra_info *info = container_of(work,101struct vibra_info, play_work);102int dir;103int pwm;104u8 reg;105106dir = info->direction;107pwm = info->speed;108109twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,110®, TWL4030_REG_VIBRA_CTL);111if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) {112113if (!info->enabled)114vibra_enable(info);115116/* set vibra rotation direction */117twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,118®, TWL4030_REG_VIBRA_CTL);119reg = (dir) ? (reg | TWL4030_VIBRA_DIR) :120(reg & ~TWL4030_VIBRA_DIR);121twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,122reg, TWL4030_REG_VIBRA_CTL);123124/* set PWM, 1 = max, 255 = min */125twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,126256 - pwm, TWL4030_REG_VIBRA_SET);127} else {128if (info->enabled)129vibra_disable(info);130}131}132133/*** Input/ForceFeedback ***/134135static int vibra_play(struct input_dev *input, void *data,136struct ff_effect *effect)137{138struct vibra_info *info = input_get_drvdata(input);139140info->speed = effect->u.rumble.strong_magnitude >> 8;141if (!info->speed)142info->speed = effect->u.rumble.weak_magnitude >> 9;143info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;144queue_work(info->workqueue, &info->play_work);145return 0;146}147148static int twl4030_vibra_open(struct input_dev *input)149{150struct vibra_info *info = input_get_drvdata(input);151152info->workqueue = create_singlethread_workqueue("vibra");153if (info->workqueue == NULL) {154dev_err(&input->dev, "couldn't create workqueue\n");155return -ENOMEM;156}157return 0;158}159160static void twl4030_vibra_close(struct input_dev *input)161{162struct vibra_info *info = input_get_drvdata(input);163164cancel_work_sync(&info->play_work);165INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */166destroy_workqueue(info->workqueue);167info->workqueue = NULL;168169if (info->enabled)170vibra_disable(info);171}172173/*** Module ***/174#if CONFIG_PM175static int twl4030_vibra_suspend(struct device *dev)176{177struct platform_device *pdev = to_platform_device(dev);178struct vibra_info *info = platform_get_drvdata(pdev);179180if (info->enabled)181vibra_disable(info);182183return 0;184}185186static int twl4030_vibra_resume(struct device *dev)187{188vibra_disable_leds();189return 0;190}191192static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,193twl4030_vibra_suspend, twl4030_vibra_resume);194#endif195196static int __devinit twl4030_vibra_probe(struct platform_device *pdev)197{198struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;199struct vibra_info *info;200int ret;201202if (!pdata) {203dev_dbg(&pdev->dev, "platform_data not available\n");204return -EINVAL;205}206207info = kzalloc(sizeof(*info), GFP_KERNEL);208if (!info)209return -ENOMEM;210211info->dev = &pdev->dev;212info->coexist = pdata->coexist;213INIT_WORK(&info->play_work, vibra_play_work);214215info->input_dev = input_allocate_device();216if (info->input_dev == NULL) {217dev_err(&pdev->dev, "couldn't allocate input device\n");218ret = -ENOMEM;219goto err_kzalloc;220}221222input_set_drvdata(info->input_dev, info);223224info->input_dev->name = "twl4030:vibrator";225info->input_dev->id.version = 1;226info->input_dev->dev.parent = pdev->dev.parent;227info->input_dev->open = twl4030_vibra_open;228info->input_dev->close = twl4030_vibra_close;229__set_bit(FF_RUMBLE, info->input_dev->ffbit);230231ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);232if (ret < 0) {233dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");234goto err_ialloc;235}236237ret = input_register_device(info->input_dev);238if (ret < 0) {239dev_dbg(&pdev->dev, "couldn't register input device\n");240goto err_iff;241}242243vibra_disable_leds();244245platform_set_drvdata(pdev, info);246return 0;247248err_iff:249input_ff_destroy(info->input_dev);250err_ialloc:251input_free_device(info->input_dev);252err_kzalloc:253kfree(info);254return ret;255}256257static int __devexit twl4030_vibra_remove(struct platform_device *pdev)258{259struct vibra_info *info = platform_get_drvdata(pdev);260261/* this also free ff-memless and calls close if needed */262input_unregister_device(info->input_dev);263kfree(info);264platform_set_drvdata(pdev, NULL);265266return 0;267}268269static struct platform_driver twl4030_vibra_driver = {270.probe = twl4030_vibra_probe,271.remove = __devexit_p(twl4030_vibra_remove),272.driver = {273.name = "twl4030-vibra",274.owner = THIS_MODULE,275#ifdef CONFIG_PM276.pm = &twl4030_vibra_pm_ops,277#endif278},279};280281static int __init twl4030_vibra_init(void)282{283return platform_driver_register(&twl4030_vibra_driver);284}285module_init(twl4030_vibra_init);286287static void __exit twl4030_vibra_exit(void)288{289platform_driver_unregister(&twl4030_vibra_driver);290}291module_exit(twl4030_vibra_exit);292293MODULE_ALIAS("platform:twl4030-vibra");294295MODULE_DESCRIPTION("TWL4030 Vibra driver");296MODULE_LICENSE("GPL");297MODULE_AUTHOR("Nokia Corporation");298299300