diff options
author | Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com> | 2011-08-30 10:40:09 +0530 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@linaro.org> | 2012-03-19 08:59:35 +0100 |
commit | 333ea0ba6544a8b83095e33e211c3249c5439bcc (patch) | |
tree | 05e0fd26100bb1707ab2b6d4d9a98ae87e0b05d2 /drivers/input | |
parent | 96a1ed9c7f2e7685b1309c3b313fa064931cb543 (diff) |
vibrator: Force feedback vibrator driver
Implementation of ST-Ericsson's force feedback vibrator driver.
ST-Ericsson ID: WP261766
Ref: Commit-id: 3f5213a6837ad774628c20b006958183b2c1932a
Signed-off-by: Avinash A <Avinash.a@stericsson.com>
Change-Id: Iadea56106111af2f7b05b3f74a4aa58cda6721b1
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/29682
Reviewed-by: Avinash A <avinash.a@stericsson.com>
Tested-by: Avinash A <avinash.a@stericsson.com>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/misc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/ste_ff_vibra.c | 234 |
3 files changed, 245 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 340d0aec249..1339e97a647 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -576,4 +576,14 @@ config INPUT_XEN_KBDDEV_FRONTEND To compile this driver as a module, choose M here: the module will be called xen-kbdfront. +config INPUT_STE_FF_VIBRA + tristate "ST-Ericsson Force Feedback Vibrator" + depends on STE_AUDIO_IO_DEV + select INPUT_FF_MEMLESS + help + This option enables support for ST-Ericsson's Vibrator which + registers as an input force feedback driver. + + To compile this driver as a module, choose M here. The module will + be called ste_ff_vibra. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index aca455b733d..f64914025ac 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -54,3 +54,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o +obj-$(CONFIG_INPUT_STE_FF_VIBRA) += ste_ff_vibra.o diff --git a/drivers/input/misc/ste_ff_vibra.c b/drivers/input/misc/ste_ff_vibra.c new file mode 100644 index 00000000000..9038e6be046 --- /dev/null +++ b/drivers/input/misc/ste_ff_vibra.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com> + * for ST-Ericsson + * License Terms: GNU General Public License v2 + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <mach/ste_audio_io_vibrator.h> + +#define FF_VIBRA_DOWN 0x0000 /* 0 degrees */ +#define FF_VIBRA_LEFT 0x4000 /* 90 degrees */ +#define FF_VIBRA_UP 0x8000 /* 180 degrees */ +#define FF_VIBRA_RIGHT 0xC000 /* 270 degrees */ + +/** + * struct vibra_info - Vibrator information structure + * @idev: Pointer to input device structure + * @vibra_workqueue: Pointer to vibrator workqueue structure + * @vibra_work: Vibrator work + * @direction: Vibration direction + * @speed: Vibration speed + * + * Structure vibra_info holds vibrator informations + **/ +struct vibra_info { + struct input_dev *idev; + struct workqueue_struct *vibra_workqueue; + struct work_struct vibra_work; + int direction; + unsigned char speed; +}; + +/** + * vibra_play_work() - Vibrator work, sets speed and direction + * @work: Pointer to work structure + * + * This function is called from workqueue, turns on/off vibrator + **/ +static void vibra_play_work(struct work_struct *work) +{ + struct vibra_info *vinfo = container_of(work, + struct vibra_info, vibra_work); + struct ste_vibra_speed left_speed = { + .positive = 0, + .negative = 0, + }; + struct ste_vibra_speed right_speed = { + .positive = 0, + .negative = 0, + }; + + /* Divide by 2 because supported range by PWM is 0-100 */ + vinfo->speed /= 2; + + if ((vinfo->direction > FF_VIBRA_DOWN) && + (vinfo->direction < FF_VIBRA_UP)) { + /* 1 - 179 degrees, turn on left vibrator */ + left_speed.positive = vinfo->speed; + } else if (vinfo->direction > FF_VIBRA_UP) { + /* more than 180 degrees, turn on right vibrator */ + right_speed.positive = vinfo->speed; + } else { + /* 0 (down) or 180 (up) degrees, turn on 2 vibrators */ + left_speed.positive = vinfo->speed; + right_speed.positive = vinfo->speed; + } + + ste_audioio_vibrator_pwm_control(STE_AUDIOIO_CLIENT_FF_VIBRA, + left_speed, right_speed); +} + +/** + * vibra_play() - Memless device control function + * @idev: Pointer to input device structure + * @data: Pointer to private data (not used) + * @effect: Pointer to force feedback effect structure + * + * This function controls memless device + * + * Returns: + * 0 - success + **/ +static int vibra_play(struct input_dev *idev, void *data, + struct ff_effect *effect) +{ + struct vibra_info *vinfo = input_get_drvdata(idev); + + vinfo->direction = effect->direction; + vinfo->speed = effect->u.rumble.strong_magnitude >> 8; + if (!vinfo->speed) + /* Shift weak magnitude to make it feelable on vibrator */ + vinfo->speed = effect->u.rumble.weak_magnitude >> 9; + + queue_work(vinfo->vibra_workqueue, &vinfo->vibra_work); + + return 0; +} + +/** + * ste_ff_vibra_open() - Input device open function + * @idev: Pointer to input device structure + * + * This function is called on opening input device + * + * Returns: + * -ENOMEM - no memory left + * 0 - success + **/ +static int ste_ff_vibra_open(struct input_dev *idev) +{ + struct vibra_info *vinfo = input_get_drvdata(idev); + + vinfo->vibra_workqueue = + create_singlethread_workqueue("ste_ff-ff-vibra"); + if (!vinfo->vibra_workqueue) { + dev_err(&idev->dev, "couldn't create vibra workqueue\n"); + return -ENOMEM; + } + return 0; +} + +/** + * ste_ff_vibra_close() - Input device close function + * @idev: Pointer to input device structure + * + * This function is called on closing input device + **/ +static void ste_ff_vibra_close(struct input_dev *idev) +{ + struct vibra_info *vinfo = input_get_drvdata(idev); + + cancel_work_sync(&vinfo->vibra_work); + INIT_WORK(&vinfo->vibra_work, vibra_play_work); + destroy_workqueue(vinfo->vibra_workqueue); + vinfo->vibra_workqueue = NULL; +} + +static int __devinit ste_ff_vibra_probe(struct platform_device *pdev) +{ + struct vibra_info *vinfo; + int ret; + + vinfo = kmalloc(sizeof *vinfo, GFP_KERNEL); + if (!vinfo) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + vinfo->idev = input_allocate_device(); + if (!vinfo->idev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + ret = -ENOMEM; + goto exit_vinfo_free; + } + + vinfo->idev->name = "ste-ff-vibra"; + vinfo->idev->dev.parent = pdev->dev.parent; + vinfo->idev->open = ste_ff_vibra_open; + vinfo->idev->close = ste_ff_vibra_close; + INIT_WORK(&vinfo->vibra_work, vibra_play_work); + __set_bit(FF_RUMBLE, vinfo->idev->ffbit); + + ret = input_ff_create_memless(vinfo->idev, NULL, vibra_play); + if (ret) { + dev_err(&pdev->dev, "failed to create memless device\n"); + goto exit_idev_free; + } + + ret = input_register_device(vinfo->idev); + if (ret) { + dev_err(&pdev->dev, "failed to register input device\n"); + goto exit_destroy_memless; + } + + input_set_drvdata(vinfo->idev, vinfo); + platform_set_drvdata(pdev, vinfo); + return 0; + +exit_destroy_memless: + input_ff_destroy(vinfo->idev); +exit_idev_free: + input_free_device(vinfo->idev); +exit_vinfo_free: + kfree(vinfo); + return ret; +} + +static int __devexit ste_ff_vibra_remove(struct platform_device *pdev) +{ + struct vibra_info *vinfo = platform_get_drvdata(pdev); + + /* + * Function device_release() will call input_dev_release() + * which will free ff and input device. No need to call + * input_ff_destroy() and input_free_device() explicitly. + */ + input_unregister_device(vinfo->idev); + kfree(vinfo); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ste_ff_vibra_driver = { + .driver = { + .name = "ste_ff_vibra", + .owner = THIS_MODULE, + }, + .probe = ste_ff_vibra_probe, + .remove = __devexit_p(ste_ff_vibra_remove) +}; + +static int __init ste_ff_vibra_init(void) +{ + return platform_driver_register(&ste_ff_vibra_driver); +} +module_init(ste_ff_vibra_init); + +static void __exit ste_ff_vibra_exit(void) +{ + platform_driver_unregister(&ste_ff_vibra_driver); +} +module_exit(ste_ff_vibra_exit); + +MODULE_AUTHOR("Marcin Mielczarczyk <marcin.mielczarczyk@tieto.com>"); +MODULE_DESCRIPTION("STE Force Feedback Vibrator Driver"); +MODULE_LICENSE("GPL v2"); |