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 work_struct play_work; 473dd1b394SJari Vanhala 483dd1b394SJari Vanhala bool enabled; 493dd1b394SJari Vanhala int speed; 503dd1b394SJari Vanhala int direction; 513dd1b394SJari Vanhala 523dd1b394SJari Vanhala bool coexist; 533dd1b394SJari Vanhala }; 543dd1b394SJari Vanhala 553dd1b394SJari Vanhala static void vibra_disable_leds(void) 563dd1b394SJari Vanhala { 573dd1b394SJari Vanhala u8 reg; 583dd1b394SJari Vanhala 593dd1b394SJari Vanhala /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */ 603dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_LED, ®, LEDEN); 613dd1b394SJari Vanhala reg &= ~0x03; 623dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg); 633dd1b394SJari Vanhala } 643dd1b394SJari Vanhala 653dd1b394SJari Vanhala /* Powers H-Bridge and enables audio clk */ 663dd1b394SJari Vanhala static void vibra_enable(struct vibra_info *info) 673dd1b394SJari Vanhala { 683dd1b394SJari Vanhala u8 reg; 693dd1b394SJari Vanhala 7057fe7251SPeter Ujfalusi twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); 713dd1b394SJari Vanhala 723dd1b394SJari Vanhala /* turn H-Bridge on */ 733dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 743dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 753dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 763dd1b394SJari Vanhala (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 773dd1b394SJari Vanhala 7857fe7251SPeter Ujfalusi twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL); 793dd1b394SJari Vanhala 803dd1b394SJari Vanhala info->enabled = true; 813dd1b394SJari Vanhala } 823dd1b394SJari Vanhala 833dd1b394SJari Vanhala static void vibra_disable(struct vibra_info *info) 843dd1b394SJari Vanhala { 853dd1b394SJari Vanhala u8 reg; 863dd1b394SJari Vanhala 873dd1b394SJari Vanhala /* Power down H-Bridge */ 883dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 893dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 903dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 913dd1b394SJari Vanhala (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 923dd1b394SJari Vanhala 9357fe7251SPeter Ujfalusi twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL); 9457fe7251SPeter Ujfalusi twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); 953dd1b394SJari Vanhala 963dd1b394SJari Vanhala info->enabled = false; 973dd1b394SJari Vanhala } 983dd1b394SJari Vanhala 993dd1b394SJari Vanhala static void vibra_play_work(struct work_struct *work) 1003dd1b394SJari Vanhala { 1013dd1b394SJari Vanhala struct vibra_info *info = container_of(work, 1023dd1b394SJari Vanhala struct vibra_info, play_work); 1033dd1b394SJari Vanhala int dir; 1043dd1b394SJari Vanhala int pwm; 1053dd1b394SJari Vanhala u8 reg; 1063dd1b394SJari Vanhala 1073dd1b394SJari Vanhala dir = info->direction; 1083dd1b394SJari Vanhala pwm = info->speed; 1093dd1b394SJari Vanhala 1103dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 1113dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 1123dd1b394SJari Vanhala if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) { 1133dd1b394SJari Vanhala 1143dd1b394SJari Vanhala if (!info->enabled) 1153dd1b394SJari Vanhala vibra_enable(info); 1163dd1b394SJari Vanhala 1173dd1b394SJari Vanhala /* set vibra rotation direction */ 1183dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 1193dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 1203dd1b394SJari Vanhala reg = (dir) ? (reg | TWL4030_VIBRA_DIR) : 1213dd1b394SJari Vanhala (reg & ~TWL4030_VIBRA_DIR); 1223dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 1233dd1b394SJari Vanhala reg, TWL4030_REG_VIBRA_CTL); 1243dd1b394SJari Vanhala 1253dd1b394SJari Vanhala /* set PWM, 1 = max, 255 = min */ 1263dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 1273dd1b394SJari Vanhala 256 - pwm, TWL4030_REG_VIBRA_SET); 1283dd1b394SJari Vanhala } else { 1293dd1b394SJari Vanhala if (info->enabled) 1303dd1b394SJari Vanhala vibra_disable(info); 1313dd1b394SJari Vanhala } 1323dd1b394SJari Vanhala } 1333dd1b394SJari Vanhala 1343dd1b394SJari Vanhala /*** Input/ForceFeedback ***/ 1353dd1b394SJari Vanhala 1363dd1b394SJari Vanhala static int vibra_play(struct input_dev *input, void *data, 1373dd1b394SJari Vanhala struct ff_effect *effect) 1383dd1b394SJari Vanhala { 1393dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1403dd1b394SJari Vanhala 1413dd1b394SJari Vanhala info->speed = effect->u.rumble.strong_magnitude >> 8; 1423dd1b394SJari Vanhala if (!info->speed) 1433dd1b394SJari Vanhala info->speed = effect->u.rumble.weak_magnitude >> 9; 1443dd1b394SJari Vanhala info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; 1454a31ba37SPeter Ujfalusi schedule_work(&info->play_work); 1463dd1b394SJari Vanhala return 0; 1473dd1b394SJari Vanhala } 1483dd1b394SJari Vanhala 1493dd1b394SJari Vanhala static void twl4030_vibra_close(struct input_dev *input) 1503dd1b394SJari Vanhala { 1513dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1523dd1b394SJari Vanhala 1533dd1b394SJari Vanhala cancel_work_sync(&info->play_work); 1543dd1b394SJari Vanhala 1553dd1b394SJari Vanhala if (info->enabled) 1563dd1b394SJari Vanhala vibra_disable(info); 1573dd1b394SJari Vanhala } 1583dd1b394SJari Vanhala 1593dd1b394SJari Vanhala /*** Module ***/ 16097a652a8SJingoo Han static int __maybe_unused twl4030_vibra_suspend(struct device *dev) 1613dd1b394SJari Vanhala { 1623dd1b394SJari Vanhala struct platform_device *pdev = to_platform_device(dev); 1633dd1b394SJari Vanhala struct vibra_info *info = platform_get_drvdata(pdev); 1643dd1b394SJari Vanhala 1653dd1b394SJari Vanhala if (info->enabled) 1663dd1b394SJari Vanhala vibra_disable(info); 1673dd1b394SJari Vanhala 1683dd1b394SJari Vanhala return 0; 1693dd1b394SJari Vanhala } 1703dd1b394SJari Vanhala 17197a652a8SJingoo Han static int __maybe_unused twl4030_vibra_resume(struct device *dev) 1723dd1b394SJari Vanhala { 1733dd1b394SJari Vanhala vibra_disable_leds(); 1743dd1b394SJari Vanhala return 0; 1753dd1b394SJari Vanhala } 1763dd1b394SJari Vanhala 1773dd1b394SJari Vanhala static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, 1783dd1b394SJari Vanhala twl4030_vibra_suspend, twl4030_vibra_resume); 1793dd1b394SJari Vanhala 18064b9e4d8SPeter Ujfalusi static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, 18164b9e4d8SPeter Ujfalusi struct device_node *node) 18264b9e4d8SPeter Ujfalusi { 18364b9e4d8SPeter Ujfalusi if (pdata && pdata->coexist) 18464b9e4d8SPeter Ujfalusi return true; 18564b9e4d8SPeter Ujfalusi 186e661d0a0SMarek Belisko node = of_find_node_by_name(node, "codec"); 187e661d0a0SMarek Belisko if (node) { 188a9e1d3c0SLibo Chen of_node_put(node); 18964b9e4d8SPeter Ujfalusi return true; 190a9e1d3c0SLibo Chen } 19164b9e4d8SPeter Ujfalusi 19264b9e4d8SPeter Ujfalusi return false; 19364b9e4d8SPeter Ujfalusi } 19464b9e4d8SPeter Ujfalusi 1955298cc4cSBill Pemberton static int twl4030_vibra_probe(struct platform_device *pdev) 1963dd1b394SJari Vanhala { 197c838cb3dSJingoo Han struct twl4030_vibra_data *pdata = dev_get_platdata(&pdev->dev); 19864b9e4d8SPeter Ujfalusi struct device_node *twl4030_core_node = pdev->dev.parent->of_node; 1993dd1b394SJari Vanhala struct vibra_info *info; 2003dd1b394SJari Vanhala int ret; 2013dd1b394SJari Vanhala 20264b9e4d8SPeter Ujfalusi if (!pdata && !twl4030_core_node) { 2033dd1b394SJari Vanhala dev_dbg(&pdev->dev, "platform_data not available\n"); 2043dd1b394SJari Vanhala return -EINVAL; 2053dd1b394SJari Vanhala } 2063dd1b394SJari Vanhala 207c3ead16eSPeter Ujfalusi info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 2083dd1b394SJari Vanhala if (!info) 2093dd1b394SJari Vanhala return -ENOMEM; 2103dd1b394SJari Vanhala 2113dd1b394SJari Vanhala info->dev = &pdev->dev; 21264b9e4d8SPeter Ujfalusi info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); 2133dd1b394SJari Vanhala INIT_WORK(&info->play_work, vibra_play_work); 2143dd1b394SJari Vanhala 215c3ead16eSPeter Ujfalusi info->input_dev = devm_input_allocate_device(&pdev->dev); 2163dd1b394SJari Vanhala if (info->input_dev == NULL) { 2173dd1b394SJari Vanhala dev_err(&pdev->dev, "couldn't allocate input device\n"); 218c3ead16eSPeter Ujfalusi return -ENOMEM; 2193dd1b394SJari Vanhala } 2203dd1b394SJari Vanhala 2213dd1b394SJari Vanhala input_set_drvdata(info->input_dev, info); 2223dd1b394SJari Vanhala 2233dd1b394SJari Vanhala info->input_dev->name = "twl4030:vibrator"; 2243dd1b394SJari Vanhala info->input_dev->id.version = 1; 2253dd1b394SJari Vanhala info->input_dev->dev.parent = pdev->dev.parent; 2263dd1b394SJari Vanhala info->input_dev->close = twl4030_vibra_close; 2273dd1b394SJari Vanhala __set_bit(FF_RUMBLE, info->input_dev->ffbit); 2283dd1b394SJari Vanhala 2293dd1b394SJari Vanhala ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); 2303dd1b394SJari Vanhala if (ret < 0) { 2313dd1b394SJari Vanhala dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); 232c3ead16eSPeter Ujfalusi return ret; 2333dd1b394SJari Vanhala } 2343dd1b394SJari Vanhala 2353dd1b394SJari Vanhala ret = input_register_device(info->input_dev); 2363dd1b394SJari Vanhala if (ret < 0) { 2373dd1b394SJari Vanhala dev_dbg(&pdev->dev, "couldn't register input device\n"); 2383dd1b394SJari Vanhala goto err_iff; 2393dd1b394SJari Vanhala } 2403dd1b394SJari Vanhala 2413dd1b394SJari Vanhala vibra_disable_leds(); 2423dd1b394SJari Vanhala 2433dd1b394SJari Vanhala platform_set_drvdata(pdev, info); 2443dd1b394SJari Vanhala return 0; 2453dd1b394SJari Vanhala 2463dd1b394SJari Vanhala err_iff: 2473dd1b394SJari Vanhala input_ff_destroy(info->input_dev); 2483dd1b394SJari Vanhala return ret; 2493dd1b394SJari Vanhala } 2503dd1b394SJari Vanhala 2513dd1b394SJari Vanhala static struct platform_driver twl4030_vibra_driver = { 2523dd1b394SJari Vanhala .probe = twl4030_vibra_probe, 2533dd1b394SJari Vanhala .driver = { 254f0fba2adSLiam Girdwood .name = "twl4030-vibra", 2553dd1b394SJari Vanhala .pm = &twl4030_vibra_pm_ops, 2563dd1b394SJari Vanhala }, 2573dd1b394SJari Vanhala }; 258840a746bSJJ Ding module_platform_driver(twl4030_vibra_driver); 2593dd1b394SJari Vanhala 260f0fba2adSLiam Girdwood MODULE_ALIAS("platform:twl4030-vibra"); 2613dd1b394SJari Vanhala MODULE_DESCRIPTION("TWL4030 Vibra driver"); 2623dd1b394SJari Vanhala MODULE_LICENSE("GPL"); 2633dd1b394SJari Vanhala MODULE_AUTHOR("Nokia Corporation"); 264