19dd555e2SMark Brown /* 29dd555e2SMark Brown * Arizona haptics driver 39dd555e2SMark Brown * 49dd555e2SMark Brown * Copyright 2012 Wolfson Microelectronics plc 59dd555e2SMark Brown * 69dd555e2SMark Brown * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 79dd555e2SMark Brown * 89dd555e2SMark Brown * This program is free software; you can redistribute it and/or modify 99dd555e2SMark Brown * it under the terms of the GNU General Public License version 2 as 109dd555e2SMark Brown * published by the Free Software Foundation. 119dd555e2SMark Brown */ 129dd555e2SMark Brown 139dd555e2SMark Brown #include <linux/module.h> 149dd555e2SMark Brown #include <linux/platform_device.h> 159dd555e2SMark Brown #include <linux/input.h> 169dd555e2SMark Brown #include <linux/slab.h> 179dd555e2SMark Brown 189dd555e2SMark Brown #include <sound/soc.h> 199dd555e2SMark Brown #include <sound/soc-dapm.h> 209dd555e2SMark Brown 219dd555e2SMark Brown #include <linux/mfd/arizona/core.h> 229dd555e2SMark Brown #include <linux/mfd/arizona/pdata.h> 239dd555e2SMark Brown #include <linux/mfd/arizona/registers.h> 249dd555e2SMark Brown 259dd555e2SMark Brown struct arizona_haptics { 269dd555e2SMark Brown struct arizona *arizona; 279dd555e2SMark Brown struct input_dev *input_dev; 289dd555e2SMark Brown struct work_struct work; 299dd555e2SMark Brown 309dd555e2SMark Brown struct mutex mutex; 319dd555e2SMark Brown u8 intensity; 329dd555e2SMark Brown }; 339dd555e2SMark Brown 349dd555e2SMark Brown static void arizona_haptics_work(struct work_struct *work) 359dd555e2SMark Brown { 369dd555e2SMark Brown struct arizona_haptics *haptics = container_of(work, 379dd555e2SMark Brown struct arizona_haptics, 389dd555e2SMark Brown work); 399dd555e2SMark Brown struct arizona *arizona = haptics->arizona; 409dd555e2SMark Brown struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex; 419dd555e2SMark Brown int ret; 429dd555e2SMark Brown 439dd555e2SMark Brown if (!haptics->arizona->dapm) { 449dd555e2SMark Brown dev_err(arizona->dev, "No DAPM context\n"); 459dd555e2SMark Brown return; 469dd555e2SMark Brown } 479dd555e2SMark Brown 489dd555e2SMark Brown if (haptics->intensity) { 499dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, 509dd555e2SMark Brown ARIZONA_HAPTICS_PHASE_2_INTENSITY, 519dd555e2SMark Brown ARIZONA_PHASE2_INTENSITY_MASK, 529dd555e2SMark Brown haptics->intensity); 539dd555e2SMark Brown if (ret != 0) { 549dd555e2SMark Brown dev_err(arizona->dev, "Failed to set intensity: %d\n", 559dd555e2SMark Brown ret); 569dd555e2SMark Brown return; 579dd555e2SMark Brown } 589dd555e2SMark Brown 599dd555e2SMark Brown /* This enable sequence will be a noop if already enabled */ 609dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, 619dd555e2SMark Brown ARIZONA_HAPTICS_CONTROL_1, 629dd555e2SMark Brown ARIZONA_HAP_CTRL_MASK, 639dd555e2SMark Brown 1 << ARIZONA_HAP_CTRL_SHIFT); 649dd555e2SMark Brown if (ret != 0) { 659dd555e2SMark Brown dev_err(arizona->dev, "Failed to start haptics: %d\n", 669dd555e2SMark Brown ret); 679dd555e2SMark Brown return; 689dd555e2SMark Brown } 699dd555e2SMark Brown 709dd555e2SMark Brown mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 719dd555e2SMark Brown 729dd555e2SMark Brown ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); 739dd555e2SMark Brown if (ret != 0) { 749dd555e2SMark Brown dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", 759dd555e2SMark Brown ret); 769dd555e2SMark Brown mutex_unlock(dapm_mutex); 779dd555e2SMark Brown return; 789dd555e2SMark Brown } 799dd555e2SMark Brown 809dd555e2SMark Brown ret = snd_soc_dapm_sync(arizona->dapm); 819dd555e2SMark Brown if (ret != 0) { 829dd555e2SMark Brown dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 839dd555e2SMark Brown ret); 849dd555e2SMark Brown mutex_unlock(dapm_mutex); 859dd555e2SMark Brown return; 869dd555e2SMark Brown } 879dd555e2SMark Brown 889dd555e2SMark Brown mutex_unlock(dapm_mutex); 899dd555e2SMark Brown 909dd555e2SMark Brown } else { 919dd555e2SMark Brown /* This disable sequence will be a noop if already enabled */ 929dd555e2SMark Brown mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 939dd555e2SMark Brown 949dd555e2SMark Brown ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); 959dd555e2SMark Brown if (ret != 0) { 969dd555e2SMark Brown dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", 979dd555e2SMark Brown ret); 989dd555e2SMark Brown mutex_unlock(dapm_mutex); 999dd555e2SMark Brown return; 1009dd555e2SMark Brown } 1019dd555e2SMark Brown 1029dd555e2SMark Brown ret = snd_soc_dapm_sync(arizona->dapm); 1039dd555e2SMark Brown if (ret != 0) { 1049dd555e2SMark Brown dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 1059dd555e2SMark Brown ret); 1069dd555e2SMark Brown mutex_unlock(dapm_mutex); 1079dd555e2SMark Brown return; 1089dd555e2SMark Brown } 1099dd555e2SMark Brown 1109dd555e2SMark Brown mutex_unlock(dapm_mutex); 1119dd555e2SMark Brown 1129dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, 1139dd555e2SMark Brown ARIZONA_HAPTICS_CONTROL_1, 1149dd555e2SMark Brown ARIZONA_HAP_CTRL_MASK, 1159dd555e2SMark Brown 1 << ARIZONA_HAP_CTRL_SHIFT); 1169dd555e2SMark Brown if (ret != 0) { 1179dd555e2SMark Brown dev_err(arizona->dev, "Failed to stop haptics: %d\n", 1189dd555e2SMark Brown ret); 1199dd555e2SMark Brown return; 1209dd555e2SMark Brown } 1219dd555e2SMark Brown } 1229dd555e2SMark Brown } 1239dd555e2SMark Brown 1249dd555e2SMark Brown static int arizona_haptics_play(struct input_dev *input, void *data, 1259dd555e2SMark Brown struct ff_effect *effect) 1269dd555e2SMark Brown { 1279dd555e2SMark Brown struct arizona_haptics *haptics = input_get_drvdata(input); 1289dd555e2SMark Brown struct arizona *arizona = haptics->arizona; 1299dd555e2SMark Brown 1309dd555e2SMark Brown if (!arizona->dapm) { 1319dd555e2SMark Brown dev_err(arizona->dev, "No DAPM context\n"); 1329dd555e2SMark Brown return -EBUSY; 1339dd555e2SMark Brown } 1349dd555e2SMark Brown 1359dd555e2SMark Brown if (effect->u.rumble.strong_magnitude) { 1369dd555e2SMark Brown /* Scale the magnitude into the range the device supports */ 1379dd555e2SMark Brown if (arizona->pdata.hap_act) { 1389dd555e2SMark Brown haptics->intensity = 1399dd555e2SMark Brown effect->u.rumble.strong_magnitude >> 9; 1409dd555e2SMark Brown if (effect->direction < 0x8000) 1419dd555e2SMark Brown haptics->intensity += 0x7f; 1429dd555e2SMark Brown } else { 1439dd555e2SMark Brown haptics->intensity = 1449dd555e2SMark Brown effect->u.rumble.strong_magnitude >> 8; 1459dd555e2SMark Brown } 1469dd555e2SMark Brown } else { 1479dd555e2SMark Brown haptics->intensity = 0; 1489dd555e2SMark Brown } 1499dd555e2SMark Brown 1509dd555e2SMark Brown schedule_work(&haptics->work); 1519dd555e2SMark Brown 1529dd555e2SMark Brown return 0; 1539dd555e2SMark Brown } 1549dd555e2SMark Brown 1559dd555e2SMark Brown static void arizona_haptics_close(struct input_dev *input) 1569dd555e2SMark Brown { 1579dd555e2SMark Brown struct arizona_haptics *haptics = input_get_drvdata(input); 1589dd555e2SMark Brown struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex; 1599dd555e2SMark Brown 1609dd555e2SMark Brown cancel_work_sync(&haptics->work); 1619dd555e2SMark Brown 1629dd555e2SMark Brown mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); 1639dd555e2SMark Brown 1649dd555e2SMark Brown if (haptics->arizona->dapm) 1659dd555e2SMark Brown snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); 1669dd555e2SMark Brown 1679dd555e2SMark Brown mutex_unlock(dapm_mutex); 1689dd555e2SMark Brown } 1699dd555e2SMark Brown 1709dd555e2SMark Brown static int arizona_haptics_probe(struct platform_device *pdev) 1719dd555e2SMark Brown { 1729dd555e2SMark Brown struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); 1739dd555e2SMark Brown struct arizona_haptics *haptics; 1749dd555e2SMark Brown int ret; 1759dd555e2SMark Brown 1769dd555e2SMark Brown haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); 1779dd555e2SMark Brown if (!haptics) 1789dd555e2SMark Brown return -ENOMEM; 1799dd555e2SMark Brown 1809dd555e2SMark Brown haptics->arizona = arizona; 1819dd555e2SMark Brown 1829dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, 1839dd555e2SMark Brown ARIZONA_HAP_ACT, arizona->pdata.hap_act); 1849dd555e2SMark Brown if (ret != 0) { 1859dd555e2SMark Brown dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", 1869dd555e2SMark Brown ret); 1879dd555e2SMark Brown return ret; 1889dd555e2SMark Brown } 1899dd555e2SMark Brown 1909dd555e2SMark Brown INIT_WORK(&haptics->work, arizona_haptics_work); 1919dd555e2SMark Brown 1929dd555e2SMark Brown haptics->input_dev = input_allocate_device(); 1939dd555e2SMark Brown if (haptics->input_dev == NULL) { 1949dd555e2SMark Brown dev_err(arizona->dev, "Failed to allocate input device\n"); 1959dd555e2SMark Brown return -ENOMEM; 1969dd555e2SMark Brown } 1979dd555e2SMark Brown 1989dd555e2SMark Brown input_set_drvdata(haptics->input_dev, haptics); 1999dd555e2SMark Brown 2009dd555e2SMark Brown haptics->input_dev->name = "arizona:haptics"; 2019dd555e2SMark Brown haptics->input_dev->dev.parent = pdev->dev.parent; 2029dd555e2SMark Brown haptics->input_dev->close = arizona_haptics_close; 2039dd555e2SMark Brown __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); 2049dd555e2SMark Brown 2059dd555e2SMark Brown ret = input_ff_create_memless(haptics->input_dev, NULL, 2069dd555e2SMark Brown arizona_haptics_play); 2079dd555e2SMark Brown if (ret < 0) { 2089dd555e2SMark Brown dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", 2099dd555e2SMark Brown ret); 2109dd555e2SMark Brown goto err_ialloc; 2119dd555e2SMark Brown } 2129dd555e2SMark Brown 2139dd555e2SMark Brown ret = input_register_device(haptics->input_dev); 2149dd555e2SMark Brown if (ret < 0) { 2159dd555e2SMark Brown dev_err(arizona->dev, "couldn't register input device: %d\n", 2169dd555e2SMark Brown ret); 2179dd555e2SMark Brown goto err_iff; 2189dd555e2SMark Brown } 2199dd555e2SMark Brown 2209dd555e2SMark Brown platform_set_drvdata(pdev, haptics); 2219dd555e2SMark Brown 2229dd555e2SMark Brown return 0; 2239dd555e2SMark Brown 2249dd555e2SMark Brown err_iff: 2259dd555e2SMark Brown if (haptics->input_dev) 2269dd555e2SMark Brown input_ff_destroy(haptics->input_dev); 2279dd555e2SMark Brown err_ialloc: 2289dd555e2SMark Brown input_free_device(haptics->input_dev); 2299dd555e2SMark Brown 2309dd555e2SMark Brown return ret; 2319dd555e2SMark Brown } 2329dd555e2SMark Brown 2339dd555e2SMark Brown static int arizona_haptics_remove(struct platform_device *pdev) 2349dd555e2SMark Brown { 2359dd555e2SMark Brown struct arizona_haptics *haptics = platform_get_drvdata(pdev); 2369dd555e2SMark Brown 2379dd555e2SMark Brown input_unregister_device(haptics->input_dev); 2389dd555e2SMark Brown 2399dd555e2SMark Brown return 0; 2409dd555e2SMark Brown } 2419dd555e2SMark Brown 2429dd555e2SMark Brown static struct platform_driver arizona_haptics_driver = { 2439dd555e2SMark Brown .probe = arizona_haptics_probe, 2449dd555e2SMark Brown .remove = arizona_haptics_remove, 2459dd555e2SMark Brown .driver = { 2469dd555e2SMark Brown .name = "arizona-haptics", 2479dd555e2SMark Brown .owner = THIS_MODULE, 2489dd555e2SMark Brown }, 2499dd555e2SMark Brown }; 2509dd555e2SMark Brown module_platform_driver(arizona_haptics_driver); 2519dd555e2SMark Brown 2529dd555e2SMark Brown MODULE_ALIAS("platform:arizona-haptics"); 2539dd555e2SMark Brown MODULE_DESCRIPTION("Arizona haptics driver"); 2549dd555e2SMark Brown MODULE_LICENSE("GPL"); 2559dd555e2SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 256