12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 23dd1b394SJari Vanhala /* 33dd1b394SJari Vanhala * twl4030-vibra.c - TWL4030 Vibrator driver 43dd1b394SJari Vanhala * 53dd1b394SJari Vanhala * Copyright (C) 2008-2010 Nokia Corporation 63dd1b394SJari Vanhala * 73dd1b394SJari Vanhala * Written by Henrik Saari <henrik.saari@nokia.com> 83dd1b394SJari Vanhala * Updates by Felipe Balbi <felipe.balbi@nokia.com> 93dd1b394SJari Vanhala * Input by Jari Vanhala <ext-jari.vanhala@nokia.com> 103dd1b394SJari Vanhala */ 113dd1b394SJari Vanhala 123dd1b394SJari Vanhala #include <linux/module.h> 133dd1b394SJari Vanhala #include <linux/jiffies.h> 143dd1b394SJari Vanhala #include <linux/platform_device.h> 1564b9e4d8SPeter Ujfalusi #include <linux/of.h> 163dd1b394SJari Vanhala #include <linux/workqueue.h> 17a2054256SWolfram Sang #include <linux/mfd/twl.h> 1857fe7251SPeter Ujfalusi #include <linux/mfd/twl4030-audio.h> 193dd1b394SJari Vanhala #include <linux/input.h> 205a0e3ad6STejun Heo #include <linux/slab.h> 213dd1b394SJari Vanhala 223dd1b394SJari Vanhala /* MODULE ID2 */ 233dd1b394SJari Vanhala #define LEDEN 0x00 243dd1b394SJari Vanhala 253dd1b394SJari Vanhala /* ForceFeedback */ 263dd1b394SJari Vanhala #define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */ 273dd1b394SJari Vanhala 283dd1b394SJari Vanhala struct vibra_info { 293dd1b394SJari Vanhala struct device *dev; 303dd1b394SJari Vanhala struct input_dev *input_dev; 313dd1b394SJari Vanhala 323dd1b394SJari Vanhala struct work_struct play_work; 333dd1b394SJari Vanhala 343dd1b394SJari Vanhala bool enabled; 353dd1b394SJari Vanhala int speed; 363dd1b394SJari Vanhala int direction; 373dd1b394SJari Vanhala 383dd1b394SJari Vanhala bool coexist; 393dd1b394SJari Vanhala }; 403dd1b394SJari Vanhala 413dd1b394SJari Vanhala static void vibra_disable_leds(void) 423dd1b394SJari Vanhala { 433dd1b394SJari Vanhala u8 reg; 443dd1b394SJari Vanhala 453dd1b394SJari Vanhala /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */ 463dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_LED, ®, LEDEN); 473dd1b394SJari Vanhala reg &= ~0x03; 483dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg); 493dd1b394SJari Vanhala } 503dd1b394SJari Vanhala 513dd1b394SJari Vanhala /* Powers H-Bridge and enables audio clk */ 523dd1b394SJari Vanhala static void vibra_enable(struct vibra_info *info) 533dd1b394SJari Vanhala { 543dd1b394SJari Vanhala u8 reg; 553dd1b394SJari Vanhala 5657fe7251SPeter Ujfalusi twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); 573dd1b394SJari Vanhala 583dd1b394SJari Vanhala /* turn H-Bridge on */ 593dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 603dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 613dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 623dd1b394SJari Vanhala (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); 633dd1b394SJari Vanhala 6457fe7251SPeter Ujfalusi twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL); 653dd1b394SJari Vanhala 663dd1b394SJari Vanhala info->enabled = true; 673dd1b394SJari Vanhala } 683dd1b394SJari Vanhala 693dd1b394SJari Vanhala static void vibra_disable(struct vibra_info *info) 703dd1b394SJari Vanhala { 713dd1b394SJari Vanhala u8 reg; 723dd1b394SJari Vanhala 733dd1b394SJari Vanhala /* Power down H-Bridge */ 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_disable_resource(TWL4030_AUDIO_RES_APLL); 8057fe7251SPeter Ujfalusi twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); 813dd1b394SJari Vanhala 823dd1b394SJari Vanhala info->enabled = false; 833dd1b394SJari Vanhala } 843dd1b394SJari Vanhala 853dd1b394SJari Vanhala static void vibra_play_work(struct work_struct *work) 863dd1b394SJari Vanhala { 873dd1b394SJari Vanhala struct vibra_info *info = container_of(work, 883dd1b394SJari Vanhala struct vibra_info, play_work); 893dd1b394SJari Vanhala int dir; 903dd1b394SJari Vanhala int pwm; 913dd1b394SJari Vanhala u8 reg; 923dd1b394SJari Vanhala 933dd1b394SJari Vanhala dir = info->direction; 943dd1b394SJari Vanhala pwm = info->speed; 953dd1b394SJari Vanhala 963dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 973dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 983dd1b394SJari Vanhala if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) { 993dd1b394SJari Vanhala 1003dd1b394SJari Vanhala if (!info->enabled) 1013dd1b394SJari Vanhala vibra_enable(info); 1023dd1b394SJari Vanhala 1033dd1b394SJari Vanhala /* set vibra rotation direction */ 1043dd1b394SJari Vanhala twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, 1053dd1b394SJari Vanhala ®, TWL4030_REG_VIBRA_CTL); 1063dd1b394SJari Vanhala reg = (dir) ? (reg | TWL4030_VIBRA_DIR) : 1073dd1b394SJari Vanhala (reg & ~TWL4030_VIBRA_DIR); 1083dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 1093dd1b394SJari Vanhala reg, TWL4030_REG_VIBRA_CTL); 1103dd1b394SJari Vanhala 1113dd1b394SJari Vanhala /* set PWM, 1 = max, 255 = min */ 1123dd1b394SJari Vanhala twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, 1133dd1b394SJari Vanhala 256 - pwm, TWL4030_REG_VIBRA_SET); 1143dd1b394SJari Vanhala } else { 1153dd1b394SJari Vanhala if (info->enabled) 1163dd1b394SJari Vanhala vibra_disable(info); 1173dd1b394SJari Vanhala } 1183dd1b394SJari Vanhala } 1193dd1b394SJari Vanhala 1203dd1b394SJari Vanhala /*** Input/ForceFeedback ***/ 1213dd1b394SJari Vanhala 1223dd1b394SJari Vanhala static int vibra_play(struct input_dev *input, void *data, 1233dd1b394SJari Vanhala struct ff_effect *effect) 1243dd1b394SJari Vanhala { 1253dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1263dd1b394SJari Vanhala 1273dd1b394SJari Vanhala info->speed = effect->u.rumble.strong_magnitude >> 8; 1283dd1b394SJari Vanhala if (!info->speed) 1293dd1b394SJari Vanhala info->speed = effect->u.rumble.weak_magnitude >> 9; 1303dd1b394SJari Vanhala info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; 1314a31ba37SPeter Ujfalusi schedule_work(&info->play_work); 1323dd1b394SJari Vanhala return 0; 1333dd1b394SJari Vanhala } 1343dd1b394SJari Vanhala 1353dd1b394SJari Vanhala static void twl4030_vibra_close(struct input_dev *input) 1363dd1b394SJari Vanhala { 1373dd1b394SJari Vanhala struct vibra_info *info = input_get_drvdata(input); 1383dd1b394SJari Vanhala 1393dd1b394SJari Vanhala cancel_work_sync(&info->play_work); 1403dd1b394SJari Vanhala 1413dd1b394SJari Vanhala if (info->enabled) 1423dd1b394SJari Vanhala vibra_disable(info); 1433dd1b394SJari Vanhala } 1443dd1b394SJari Vanhala 1453dd1b394SJari Vanhala /*** Module ***/ 14697a652a8SJingoo Han static int __maybe_unused twl4030_vibra_suspend(struct device *dev) 1473dd1b394SJari Vanhala { 1483dd1b394SJari Vanhala struct platform_device *pdev = to_platform_device(dev); 1493dd1b394SJari Vanhala struct vibra_info *info = platform_get_drvdata(pdev); 1503dd1b394SJari Vanhala 1513dd1b394SJari Vanhala if (info->enabled) 1523dd1b394SJari Vanhala vibra_disable(info); 1533dd1b394SJari Vanhala 1543dd1b394SJari Vanhala return 0; 1553dd1b394SJari Vanhala } 1563dd1b394SJari Vanhala 15797a652a8SJingoo Han static int __maybe_unused twl4030_vibra_resume(struct device *dev) 1583dd1b394SJari Vanhala { 1593dd1b394SJari Vanhala vibra_disable_leds(); 1603dd1b394SJari Vanhala return 0; 1613dd1b394SJari Vanhala } 1623dd1b394SJari Vanhala 1633dd1b394SJari Vanhala static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, 1643dd1b394SJari Vanhala twl4030_vibra_suspend, twl4030_vibra_resume); 1653dd1b394SJari Vanhala 166*6dc0a438SPeter Ujfalusi static bool twl4030_vibra_check_coexist(struct device_node *parent) 16764b9e4d8SPeter Ujfalusi { 1685b189201SJohan Hovold struct device_node *node; 1695b189201SJohan Hovold 1705b189201SJohan Hovold node = of_get_child_by_name(parent, "codec"); 171e661d0a0SMarek Belisko if (node) { 172a9e1d3c0SLibo Chen of_node_put(node); 17364b9e4d8SPeter Ujfalusi return true; 174a9e1d3c0SLibo Chen } 17564b9e4d8SPeter Ujfalusi 17664b9e4d8SPeter Ujfalusi return false; 17764b9e4d8SPeter Ujfalusi } 17864b9e4d8SPeter Ujfalusi 1795298cc4cSBill Pemberton static int twl4030_vibra_probe(struct platform_device *pdev) 1803dd1b394SJari Vanhala { 18164b9e4d8SPeter Ujfalusi struct device_node *twl4030_core_node = pdev->dev.parent->of_node; 1823dd1b394SJari Vanhala struct vibra_info *info; 1833dd1b394SJari Vanhala int ret; 1843dd1b394SJari Vanhala 185*6dc0a438SPeter Ujfalusi if (!twl4030_core_node) { 186*6dc0a438SPeter Ujfalusi dev_dbg(&pdev->dev, "twl4030 OF node is missing\n"); 1873dd1b394SJari Vanhala return -EINVAL; 1883dd1b394SJari Vanhala } 1893dd1b394SJari Vanhala 190c3ead16eSPeter Ujfalusi info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 1913dd1b394SJari Vanhala if (!info) 1923dd1b394SJari Vanhala return -ENOMEM; 1933dd1b394SJari Vanhala 1943dd1b394SJari Vanhala info->dev = &pdev->dev; 195*6dc0a438SPeter Ujfalusi info->coexist = twl4030_vibra_check_coexist(twl4030_core_node); 1963dd1b394SJari Vanhala INIT_WORK(&info->play_work, vibra_play_work); 1973dd1b394SJari Vanhala 198c3ead16eSPeter Ujfalusi info->input_dev = devm_input_allocate_device(&pdev->dev); 1993dd1b394SJari Vanhala if (info->input_dev == NULL) { 2003dd1b394SJari Vanhala dev_err(&pdev->dev, "couldn't allocate input device\n"); 201c3ead16eSPeter Ujfalusi return -ENOMEM; 2023dd1b394SJari Vanhala } 2033dd1b394SJari Vanhala 2043dd1b394SJari Vanhala input_set_drvdata(info->input_dev, info); 2053dd1b394SJari Vanhala 2063dd1b394SJari Vanhala info->input_dev->name = "twl4030:vibrator"; 2073dd1b394SJari Vanhala info->input_dev->id.version = 1; 2083dd1b394SJari Vanhala info->input_dev->close = twl4030_vibra_close; 2093dd1b394SJari Vanhala __set_bit(FF_RUMBLE, info->input_dev->ffbit); 2103dd1b394SJari Vanhala 2113dd1b394SJari Vanhala ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); 2123dd1b394SJari Vanhala if (ret < 0) { 2133dd1b394SJari Vanhala dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); 214c3ead16eSPeter Ujfalusi return ret; 2153dd1b394SJari Vanhala } 2163dd1b394SJari Vanhala 2173dd1b394SJari Vanhala ret = input_register_device(info->input_dev); 2183dd1b394SJari Vanhala if (ret < 0) { 2193dd1b394SJari Vanhala dev_dbg(&pdev->dev, "couldn't register input device\n"); 2203dd1b394SJari Vanhala goto err_iff; 2213dd1b394SJari Vanhala } 2223dd1b394SJari Vanhala 2233dd1b394SJari Vanhala vibra_disable_leds(); 2243dd1b394SJari Vanhala 2253dd1b394SJari Vanhala platform_set_drvdata(pdev, info); 2263dd1b394SJari Vanhala return 0; 2273dd1b394SJari Vanhala 2283dd1b394SJari Vanhala err_iff: 2293dd1b394SJari Vanhala input_ff_destroy(info->input_dev); 2303dd1b394SJari Vanhala return ret; 2313dd1b394SJari Vanhala } 2323dd1b394SJari Vanhala 2333dd1b394SJari Vanhala static struct platform_driver twl4030_vibra_driver = { 2343dd1b394SJari Vanhala .probe = twl4030_vibra_probe, 2353dd1b394SJari Vanhala .driver = { 236f0fba2adSLiam Girdwood .name = "twl4030-vibra", 2373dd1b394SJari Vanhala .pm = &twl4030_vibra_pm_ops, 2383dd1b394SJari Vanhala }, 2393dd1b394SJari Vanhala }; 240840a746bSJJ Ding module_platform_driver(twl4030_vibra_driver); 2413dd1b394SJari Vanhala 242f0fba2adSLiam Girdwood MODULE_ALIAS("platform:twl4030-vibra"); 2433dd1b394SJari Vanhala MODULE_DESCRIPTION("TWL4030 Vibra driver"); 2443dd1b394SJari Vanhala MODULE_LICENSE("GPL"); 2453dd1b394SJari Vanhala MODULE_AUTHOR("Nokia Corporation"); 246