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; 40931afc41SRichard Fitzgerald struct snd_soc_component *component = 41931afc41SRichard Fitzgerald snd_soc_dapm_to_component(arizona->dapm); 429dd555e2SMark Brown int ret; 439dd555e2SMark Brown 449dd555e2SMark Brown if (!haptics->arizona->dapm) { 459dd555e2SMark Brown dev_err(arizona->dev, "No DAPM context\n"); 469dd555e2SMark Brown return; 479dd555e2SMark Brown } 489dd555e2SMark Brown 499dd555e2SMark Brown if (haptics->intensity) { 509dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, 519dd555e2SMark Brown ARIZONA_HAPTICS_PHASE_2_INTENSITY, 529dd555e2SMark Brown ARIZONA_PHASE2_INTENSITY_MASK, 539dd555e2SMark Brown haptics->intensity); 549dd555e2SMark Brown if (ret != 0) { 559dd555e2SMark Brown dev_err(arizona->dev, "Failed to set intensity: %d\n", 569dd555e2SMark Brown ret); 579dd555e2SMark Brown return; 589dd555e2SMark Brown } 599dd555e2SMark Brown 609dd555e2SMark Brown /* This enable sequence will be a noop if already enabled */ 619dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, 629dd555e2SMark Brown ARIZONA_HAPTICS_CONTROL_1, 639dd555e2SMark Brown ARIZONA_HAP_CTRL_MASK, 649dd555e2SMark Brown 1 << ARIZONA_HAP_CTRL_SHIFT); 659dd555e2SMark Brown if (ret != 0) { 669dd555e2SMark Brown dev_err(arizona->dev, "Failed to start haptics: %d\n", 679dd555e2SMark Brown ret); 689dd555e2SMark Brown return; 699dd555e2SMark Brown } 709dd555e2SMark Brown 71931afc41SRichard Fitzgerald ret = snd_soc_component_enable_pin(component, "HAPTICS"); 729dd555e2SMark Brown if (ret != 0) { 739dd555e2SMark Brown dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", 749dd555e2SMark Brown ret); 759dd555e2SMark Brown return; 769dd555e2SMark Brown } 779dd555e2SMark Brown 789dd555e2SMark Brown ret = snd_soc_dapm_sync(arizona->dapm); 799dd555e2SMark Brown if (ret != 0) { 809dd555e2SMark Brown dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 819dd555e2SMark Brown ret); 829dd555e2SMark Brown return; 839dd555e2SMark Brown } 849dd555e2SMark Brown } else { 859dd555e2SMark Brown /* This disable sequence will be a noop if already enabled */ 86931afc41SRichard Fitzgerald ret = snd_soc_component_disable_pin(component, "HAPTICS"); 879dd555e2SMark Brown if (ret != 0) { 889dd555e2SMark Brown dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", 899dd555e2SMark Brown ret); 909dd555e2SMark Brown return; 919dd555e2SMark Brown } 929dd555e2SMark Brown 939dd555e2SMark Brown ret = snd_soc_dapm_sync(arizona->dapm); 949dd555e2SMark Brown if (ret != 0) { 959dd555e2SMark Brown dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 969dd555e2SMark Brown ret); 979dd555e2SMark Brown return; 989dd555e2SMark Brown } 999dd555e2SMark Brown 1009dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, 1019dd555e2SMark Brown ARIZONA_HAPTICS_CONTROL_1, 1021cf44efaSCharles Keepax ARIZONA_HAP_CTRL_MASK, 0); 1039dd555e2SMark Brown if (ret != 0) { 1049dd555e2SMark Brown dev_err(arizona->dev, "Failed to stop haptics: %d\n", 1059dd555e2SMark Brown ret); 1069dd555e2SMark Brown return; 1079dd555e2SMark Brown } 1089dd555e2SMark Brown } 1099dd555e2SMark Brown } 1109dd555e2SMark Brown 1119dd555e2SMark Brown static int arizona_haptics_play(struct input_dev *input, void *data, 1129dd555e2SMark Brown struct ff_effect *effect) 1139dd555e2SMark Brown { 1149dd555e2SMark Brown struct arizona_haptics *haptics = input_get_drvdata(input); 1159dd555e2SMark Brown struct arizona *arizona = haptics->arizona; 1169dd555e2SMark Brown 1179dd555e2SMark Brown if (!arizona->dapm) { 1189dd555e2SMark Brown dev_err(arizona->dev, "No DAPM context\n"); 1199dd555e2SMark Brown return -EBUSY; 1209dd555e2SMark Brown } 1219dd555e2SMark Brown 1229dd555e2SMark Brown if (effect->u.rumble.strong_magnitude) { 1239dd555e2SMark Brown /* Scale the magnitude into the range the device supports */ 1249dd555e2SMark Brown if (arizona->pdata.hap_act) { 1259dd555e2SMark Brown haptics->intensity = 1269dd555e2SMark Brown effect->u.rumble.strong_magnitude >> 9; 1279dd555e2SMark Brown if (effect->direction < 0x8000) 1289dd555e2SMark Brown haptics->intensity += 0x7f; 1299dd555e2SMark Brown } else { 1309dd555e2SMark Brown haptics->intensity = 1319dd555e2SMark Brown effect->u.rumble.strong_magnitude >> 8; 1329dd555e2SMark Brown } 1339dd555e2SMark Brown } else { 1349dd555e2SMark Brown haptics->intensity = 0; 1359dd555e2SMark Brown } 1369dd555e2SMark Brown 1379dd555e2SMark Brown schedule_work(&haptics->work); 1389dd555e2SMark Brown 1399dd555e2SMark Brown return 0; 1409dd555e2SMark Brown } 1419dd555e2SMark Brown 1429dd555e2SMark Brown static void arizona_haptics_close(struct input_dev *input) 1439dd555e2SMark Brown { 1449dd555e2SMark Brown struct arizona_haptics *haptics = input_get_drvdata(input); 145931afc41SRichard Fitzgerald struct snd_soc_component *component; 1469dd555e2SMark Brown 1479dd555e2SMark Brown cancel_work_sync(&haptics->work); 1489dd555e2SMark Brown 149931afc41SRichard Fitzgerald if (haptics->arizona->dapm) { 150931afc41SRichard Fitzgerald component = snd_soc_dapm_to_component(haptics->arizona->dapm); 151931afc41SRichard Fitzgerald snd_soc_component_disable_pin(component, "HAPTICS"); 152931afc41SRichard Fitzgerald } 1539dd555e2SMark Brown } 1549dd555e2SMark Brown 1559dd555e2SMark Brown static int arizona_haptics_probe(struct platform_device *pdev) 1569dd555e2SMark Brown { 1579dd555e2SMark Brown struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); 1589dd555e2SMark Brown struct arizona_haptics *haptics; 1599dd555e2SMark Brown int ret; 1609dd555e2SMark Brown 1619dd555e2SMark Brown haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); 1629dd555e2SMark Brown if (!haptics) 1639dd555e2SMark Brown return -ENOMEM; 1649dd555e2SMark Brown 1659dd555e2SMark Brown haptics->arizona = arizona; 1669dd555e2SMark Brown 1679dd555e2SMark Brown ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, 1689dd555e2SMark Brown ARIZONA_HAP_ACT, arizona->pdata.hap_act); 1699dd555e2SMark Brown if (ret != 0) { 1709dd555e2SMark Brown dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", 1719dd555e2SMark Brown ret); 1729dd555e2SMark Brown return ret; 1739dd555e2SMark Brown } 1749dd555e2SMark Brown 1759dd555e2SMark Brown INIT_WORK(&haptics->work, arizona_haptics_work); 1769dd555e2SMark Brown 177e75ed3c4SDmitry Torokhov haptics->input_dev = devm_input_allocate_device(&pdev->dev); 178e75ed3c4SDmitry Torokhov if (!haptics->input_dev) { 1799dd555e2SMark Brown dev_err(arizona->dev, "Failed to allocate input device\n"); 1809dd555e2SMark Brown return -ENOMEM; 1819dd555e2SMark Brown } 1829dd555e2SMark Brown 1839dd555e2SMark Brown input_set_drvdata(haptics->input_dev, haptics); 1849dd555e2SMark Brown 1859dd555e2SMark Brown haptics->input_dev->name = "arizona:haptics"; 1869dd555e2SMark Brown haptics->input_dev->close = arizona_haptics_close; 1879dd555e2SMark Brown __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); 1889dd555e2SMark Brown 1899dd555e2SMark Brown ret = input_ff_create_memless(haptics->input_dev, NULL, 1909dd555e2SMark Brown arizona_haptics_play); 1919dd555e2SMark Brown if (ret < 0) { 1929dd555e2SMark Brown dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", 1939dd555e2SMark Brown ret); 194e75ed3c4SDmitry Torokhov return ret; 1959dd555e2SMark Brown } 1969dd555e2SMark Brown 1979dd555e2SMark Brown ret = input_register_device(haptics->input_dev); 1989dd555e2SMark Brown if (ret < 0) { 1999dd555e2SMark Brown dev_err(arizona->dev, "couldn't register input device: %d\n", 2009dd555e2SMark Brown ret); 2019dd555e2SMark Brown return ret; 2029dd555e2SMark Brown } 2039dd555e2SMark Brown 204e75ed3c4SDmitry Torokhov platform_set_drvdata(pdev, haptics); 2059dd555e2SMark Brown 2069dd555e2SMark Brown return 0; 2079dd555e2SMark Brown } 2089dd555e2SMark Brown 2099dd555e2SMark Brown static struct platform_driver arizona_haptics_driver = { 2109dd555e2SMark Brown .probe = arizona_haptics_probe, 2119dd555e2SMark Brown .driver = { 2129dd555e2SMark Brown .name = "arizona-haptics", 2139dd555e2SMark Brown }, 2149dd555e2SMark Brown }; 2159dd555e2SMark Brown module_platform_driver(arizona_haptics_driver); 2169dd555e2SMark Brown 2179dd555e2SMark Brown MODULE_ALIAS("platform:arizona-haptics"); 2189dd555e2SMark Brown MODULE_DESCRIPTION("Arizona haptics driver"); 2199dd555e2SMark Brown MODULE_LICENSE("GPL"); 2209dd555e2SMark Brown MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 221