13dd1b394SJari Vanhala /* 23dd1b394SJari Vanhala * twl4030-vibra.c - TWL4030 Vibrator driver 33dd1b394SJari Vanhala * 43dd1b394SJari Vanhala * Copyright (C) 2008-2010 Nokia Corporation 53dd1b394SJari Vanhala * 63dd1b394SJari Vanhala * Written by Henrik Saari <henrik.saari@nokia.com> 73dd1b394SJari Vanhala * Updates by Felipe Balbi <felipe.balbi@nokia.com> 83dd1b394SJari Vanhala * Input by Jari Vanhala <ext-jari.vanhala@nokia.com> 93dd1b394SJari Vanhala * 103dd1b394SJari Vanhala * This program is free software; you can redistribute it and/or modify 113dd1b394SJari Vanhala * it under the terms of the GNU General Public License version 2 as 123dd1b394SJari Vanhala * published by the Free Software Foundation. 133dd1b394SJari Vanhala * 143dd1b394SJari Vanhala * This program is distributed in the hope that it will be useful, but 153dd1b394SJari Vanhala * WITHOUT ANY WARRANTY; without even the implied warranty of 163dd1b394SJari Vanhala * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 173dd1b394SJari Vanhala * General Public License for more details. 183dd1b394SJari Vanhala * 193dd1b394SJari Vanhala * You should have received a copy of the GNU General Public License 203dd1b394SJari Vanhala * along with this program; if not, write to the Free Software 213dd1b394SJari Vanhala * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 223dd1b394SJari Vanhala * 02110-1301 USA 233dd1b394SJari Vanhala * 243dd1b394SJari Vanhala */ 253dd1b394SJari Vanhala 263dd1b394SJari Vanhala #include <linux/module.h> 273dd1b394SJari Vanhala #include <linux/jiffies.h> 283dd1b394SJari Vanhala #include <linux/platform_device.h> 2964b9e4d8SPeter Ujfalusi #include <linux/of.h> 303dd1b394SJari Vanhala #include <linux/workqueue.h> 313dd1b394SJari Vanhala #include <linux/i2c/twl.h> 3257fe7251SPeter Ujfalusi #include <linux/mfd/twl4030-audio.h> 333dd1b394SJari Vanhala #include <linux/input.h> 345a0e3ad6STejun Heo #include <linux/slab.h> 353dd1b394SJari Vanhala 363dd1b394SJari Vanhala /* MODULE ID2 */ 373dd1b394SJari Vanhala #define LEDEN 0x00 383dd1b394SJari Vanhala 393dd1b394SJari Vanhala /* ForceFeedback */ 403dd1b394SJari Vanhala #define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */ 413dd1b394SJari Vanhala 423dd1b394SJari Vanhala struct vibra_info { 433dd1b394SJari Vanhala struct device *dev; 443dd1b394SJari Vanhala struct input_dev *input_dev; 453dd1b394SJari Vanhala 463dd1b394SJari Vanhala struct workqueue_struct *workqueue; 473dd1b394SJari Vanhala struct work_struct play_work; 483dd1b394SJari Vanhala 493dd1b394SJari Vanhala bool enabled; 503dd1b394SJari Vanhala int speed; 513dd1b394SJari Vanhala int direction; 523dd1b394SJari Vanhala 533dd1b394SJari Vanhala bool coexist; 543dd1b394SJari Vanhala }; 553dd1b394SJari Vanhala 563dd1b394SJari Vanhala static void vibra_disable_leds(void) 573dd1b394SJari Vanhala { 583dd1b394SJari Vanhala u8 reg; 593dd1b394SJari Vanhala 603dd1b394SJari Vanhala /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */ 613dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_LED, ®, LEDEN); 623dd1b394SJari Vanhala reg &= ~0x03; 633dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg); 643dd1b394SJari Vanhala } 653dd1b394SJari Vanhala 663dd1b394SJari Vanhala /* Powers H-Bridge and enables audio clk */ 673dd1b394SJari Vanhala static void vibra_enable(struct vibra_info *info) 683dd1b394SJari Vanhala { 693dd1b394SJari Vanhala u8 reg; 703dd1b394SJari Vanhala 7157fe7251SPeter Ujfalusi twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); 723dd1b394SJari Vanhala 733dd1b394SJari Vanhala /* turn H-Bridge on */ 743dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 753dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 763dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 773dd1b394SJari Vanhala (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 783dd1b394SJari Vanhala 7957fe7251SPeter Ujfalusi twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL); 803dd1b394SJari Vanhala 813dd1b394SJari Vanhala info->enabled = true; 823dd1b394SJari Vanhala } 833dd1b394SJari Vanhala 843dd1b394SJari Vanhala static void vibra_disable(struct vibra_info *info) 853dd1b394SJari Vanhala { 863dd1b394SJari Vanhala u8 reg; 873dd1b394SJari Vanhala 883dd1b394SJari Vanhala /* Power down H-Bridge */ 893dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 903dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 913dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 923dd1b394SJari Vanhala (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 933dd1b394SJari Vanhala 9457fe7251SPeter Ujfalusi twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL); 9557fe7251SPeter Ujfalusi twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); 963dd1b394SJari Vanhala 973dd1b394SJari Vanhala info->enabled = false; 983dd1b394SJari Vanhala } 993dd1b394SJari Vanhala 1003dd1b394SJari Vanhala static void vibra_play_work(struct work_struct *work) 1013dd1b394SJari Vanhala { 1023dd1b394SJari Vanhala struct vibra_info *info = container_of(work, 1033dd1b394SJari Vanhala struct vibra_info, play_work); 1043dd1b394SJari Vanhala int dir; 1053dd1b394SJari Vanhala int pwm; 1063dd1b394SJari Vanhala u8 reg; 1073dd1b394SJari Vanhala 1083dd1b394SJari Vanhala dir = info->direction; 1093dd1b394SJari Vanhala pwm = info->speed; 1103dd1b394SJari Vanhala 1113dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 1123dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 1133dd1b394SJari Vanhala if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) { 1143dd1b394SJari Vanhala 1153dd1b394SJari Vanhala if (!info->enabled) 1163dd1b394SJari Vanhala vibra_enable(info); 1173dd1b394SJari Vanhala 1183dd1b394SJari Vanhala /* set vibra rotation direction */ 1193dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 1203dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 1213dd1b394SJari Vanhala reg = (dir) ? (reg | TWL4030_VIBRA_DIR) : 1223dd1b394SJari Vanhala (reg & ~TWL4030_VIBRA_DIR); 1233dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 1243dd1b394SJari Vanhala reg, TWL4030_REG_VIBRA_CTL); 1253dd1b394SJari Vanhala 1263dd1b394SJari Vanhala /* set PWM, 1 = max, 255 = min */ 1273dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 1283dd1b394SJari Vanhala 256 - pwm, TWL4030_REG_VIBRA_SET); 1293dd1b394SJari Vanhala } else { 1303dd1b394SJari Vanhala if (info->enabled) 1313dd1b394SJari Vanhala vibra_disable(info); 1323dd1b394SJari Vanhala } 1333dd1b394SJari Vanhala } 1343dd1b394SJari Vanhala 1353dd1b394SJari Vanhala /*** Input/ForceFeedback ***/ 1363dd1b394SJari Vanhala 1373dd1b394SJari Vanhala static int vibra_play(struct input_dev *input, void *data, 1383dd1b394SJari Vanhala struct ff_effect *effect) 1393dd1b394SJari Vanhala { 1403dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1413dd1b394SJari Vanhala 1423dd1b394SJari Vanhala info->speed = effect->u.rumble.strong_magnitude >> 8; 1433dd1b394SJari Vanhala if (!info->speed) 1443dd1b394SJari Vanhala info->speed = effect->u.rumble.weak_magnitude >> 9; 1453dd1b394SJari Vanhala info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; 1463dd1b394SJari Vanhala queue_work(info->workqueue, &info->play_work); 1473dd1b394SJari Vanhala return 0; 1483dd1b394SJari Vanhala } 1493dd1b394SJari Vanhala 1503dd1b394SJari Vanhala static int twl4030_vibra_open(struct input_dev *input) 1513dd1b394SJari Vanhala { 1523dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1533dd1b394SJari Vanhala 1543dd1b394SJari Vanhala info->workqueue = create_singlethread_workqueue("vibra"); 1553dd1b394SJari Vanhala if (info->workqueue == NULL) { 1563dd1b394SJari Vanhala dev_err(&input->dev, "couldn't create workqueue\n"); 1573dd1b394SJari Vanhala return -ENOMEM; 1583dd1b394SJari Vanhala } 1593dd1b394SJari Vanhala return 0; 1603dd1b394SJari Vanhala } 1613dd1b394SJari Vanhala 1623dd1b394SJari Vanhala static void twl4030_vibra_close(struct input_dev *input) 1633dd1b394SJari Vanhala { 1643dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1653dd1b394SJari Vanhala 1663dd1b394SJari Vanhala cancel_work_sync(&info->play_work); 1673dd1b394SJari Vanhala INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */ 1683dd1b394SJari Vanhala destroy_workqueue(info->workqueue); 1693dd1b394SJari Vanhala info->workqueue = NULL; 1703dd1b394SJari Vanhala 1713dd1b394SJari Vanhala if (info->enabled) 1723dd1b394SJari Vanhala vibra_disable(info); 1733dd1b394SJari Vanhala } 1743dd1b394SJari Vanhala 1753dd1b394SJari Vanhala /*** Module ***/ 1767a0a1dfeSDmitry Torokhov #ifdef CONFIG_PM_SLEEP 1773dd1b394SJari Vanhala static int twl4030_vibra_suspend(struct device *dev) 1783dd1b394SJari Vanhala { 1793dd1b394SJari Vanhala struct platform_device *pdev = to_platform_device(dev); 1803dd1b394SJari Vanhala struct vibra_info *info = platform_get_drvdata(pdev); 1813dd1b394SJari Vanhala 1823dd1b394SJari Vanhala if (info->enabled) 1833dd1b394SJari Vanhala vibra_disable(info); 1843dd1b394SJari Vanhala 1853dd1b394SJari Vanhala return 0; 1863dd1b394SJari Vanhala } 1873dd1b394SJari Vanhala 1883dd1b394SJari Vanhala static int twl4030_vibra_resume(struct device *dev) 1893dd1b394SJari Vanhala { 1903dd1b394SJari Vanhala vibra_disable_leds(); 1913dd1b394SJari Vanhala return 0; 1923dd1b394SJari Vanhala } 193f3761c07SDmitry Torokhov #endif 1943dd1b394SJari Vanhala 1953dd1b394SJari Vanhala static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, 1963dd1b394SJari Vanhala twl4030_vibra_suspend, twl4030_vibra_resume); 1973dd1b394SJari Vanhala 19864b9e4d8SPeter Ujfalusi static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, 19964b9e4d8SPeter Ujfalusi struct device_node *node) 20064b9e4d8SPeter Ujfalusi { 20164b9e4d8SPeter Ujfalusi if (pdata && pdata->coexist) 20264b9e4d8SPeter Ujfalusi return true; 20364b9e4d8SPeter Ujfalusi 20464b9e4d8SPeter Ujfalusi if (of_find_node_by_name(node, "codec")) 20564b9e4d8SPeter Ujfalusi return true; 20664b9e4d8SPeter Ujfalusi 20764b9e4d8SPeter Ujfalusi return false; 20864b9e4d8SPeter Ujfalusi } 20964b9e4d8SPeter Ujfalusi 2105298cc4cSBill Pemberton static int twl4030_vibra_probe(struct platform_device *pdev) 2113dd1b394SJari Vanhala { 2124ae6df5eSPeter Ujfalusi struct twl4030_vibra_data *pdata = pdev->dev.platform_data; 21364b9e4d8SPeter Ujfalusi struct device_node *twl4030_core_node = pdev->dev.parent->of_node; 2143dd1b394SJari Vanhala struct vibra_info *info; 2153dd1b394SJari Vanhala int ret; 2163dd1b394SJari Vanhala 21764b9e4d8SPeter Ujfalusi if (!pdata && !twl4030_core_node) { 2183dd1b394SJari Vanhala dev_dbg(&pdev->dev, "platform_data not available\n"); 2193dd1b394SJari Vanhala return -EINVAL; 2203dd1b394SJari Vanhala } 2213dd1b394SJari Vanhala 222c3ead16eSPeter Ujfalusi info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 2233dd1b394SJari Vanhala if (!info) 2243dd1b394SJari Vanhala return -ENOMEM; 2253dd1b394SJari Vanhala 2263dd1b394SJari Vanhala info->dev = &pdev->dev; 22764b9e4d8SPeter Ujfalusi info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); 2283dd1b394SJari Vanhala INIT_WORK(&info->play_work, vibra_play_work); 2293dd1b394SJari Vanhala 230c3ead16eSPeter Ujfalusi info->input_dev = devm_input_allocate_device(&pdev->dev); 2313dd1b394SJari Vanhala if (info->input_dev == NULL) { 2323dd1b394SJari Vanhala dev_err(&pdev->dev, "couldn't allocate input device\n"); 233c3ead16eSPeter Ujfalusi return -ENOMEM; 2343dd1b394SJari Vanhala } 2353dd1b394SJari Vanhala 2363dd1b394SJari Vanhala input_set_drvdata(info->input_dev, info); 2373dd1b394SJari Vanhala 2383dd1b394SJari Vanhala info->input_dev->name = "twl4030:vibrator"; 2393dd1b394SJari Vanhala info->input_dev->id.version = 1; 2403dd1b394SJari Vanhala info->input_dev->dev.parent = pdev->dev.parent; 2413dd1b394SJari Vanhala info->input_dev->open = twl4030_vibra_open; 2423dd1b394SJari Vanhala info->input_dev->close = twl4030_vibra_close; 2433dd1b394SJari Vanhala __set_bit(FF_RUMBLE, info->input_dev->ffbit); 2443dd1b394SJari Vanhala 2453dd1b394SJari Vanhala ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); 2463dd1b394SJari Vanhala if (ret < 0) { 2473dd1b394SJari Vanhala dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); 248c3ead16eSPeter Ujfalusi return ret; 2493dd1b394SJari Vanhala } 2503dd1b394SJari Vanhala 2513dd1b394SJari Vanhala ret = input_register_device(info->input_dev); 2523dd1b394SJari Vanhala if (ret < 0) { 2533dd1b394SJari Vanhala dev_dbg(&pdev->dev, "couldn't register input device\n"); 2543dd1b394SJari Vanhala goto err_iff; 2553dd1b394SJari Vanhala } 2563dd1b394SJari Vanhala 2573dd1b394SJari Vanhala vibra_disable_leds(); 2583dd1b394SJari Vanhala 2593dd1b394SJari Vanhala platform_set_drvdata(pdev, info); 2603dd1b394SJari Vanhala return 0; 2613dd1b394SJari Vanhala 2623dd1b394SJari Vanhala err_iff: 2633dd1b394SJari Vanhala input_ff_destroy(info->input_dev); 2643dd1b394SJari Vanhala return ret; 2653dd1b394SJari Vanhala } 2663dd1b394SJari Vanhala 2673dd1b394SJari Vanhala static struct platform_driver twl4030_vibra_driver = { 2683dd1b394SJari Vanhala .probe = twl4030_vibra_probe, 2693dd1b394SJari Vanhala .driver = { 270f0fba2adSLiam Girdwood .name = "twl4030-vibra", 2713dd1b394SJari Vanhala .owner = THIS_MODULE, 2723dd1b394SJari Vanhala .pm = &twl4030_vibra_pm_ops, 2733dd1b394SJari Vanhala }, 2743dd1b394SJari Vanhala }; 275840a746bSJJ Ding module_platform_driver(twl4030_vibra_driver); 2763dd1b394SJari Vanhala 277f0fba2adSLiam Girdwood MODULE_ALIAS("platform:twl4030-vibra"); 2783dd1b394SJari Vanhala MODULE_DESCRIPTION("TWL4030 Vibra driver"); 2793dd1b394SJari Vanhala MODULE_LICENSE("GPL"); 2803dd1b394SJari Vanhala MODULE_AUTHOR("Nokia Corporation"); 281