1 /* 2 * Arizona haptics driver 3 * 4 * Copyright 2012 Wolfson Microelectronics plc 5 * 6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License version 2 as 10 * published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 #include <linux/input.h> 16 #include <linux/slab.h> 17 18 #include <sound/soc.h> 19 #include <sound/soc-dapm.h> 20 21 #include <linux/mfd/arizona/core.h> 22 #include <linux/mfd/arizona/pdata.h> 23 #include <linux/mfd/arizona/registers.h> 24 25 struct arizona_haptics { 26 struct arizona *arizona; 27 struct input_dev *input_dev; 28 struct work_struct work; 29 30 struct mutex mutex; 31 u8 intensity; 32 }; 33 34 static void arizona_haptics_work(struct work_struct *work) 35 { 36 struct arizona_haptics *haptics = container_of(work, 37 struct arizona_haptics, 38 work); 39 struct arizona *arizona = haptics->arizona; 40 struct snd_soc_component *component = 41 snd_soc_dapm_to_component(arizona->dapm); 42 int ret; 43 44 if (!haptics->arizona->dapm) { 45 dev_err(arizona->dev, "No DAPM context\n"); 46 return; 47 } 48 49 if (haptics->intensity) { 50 ret = regmap_update_bits(arizona->regmap, 51 ARIZONA_HAPTICS_PHASE_2_INTENSITY, 52 ARIZONA_PHASE2_INTENSITY_MASK, 53 haptics->intensity); 54 if (ret != 0) { 55 dev_err(arizona->dev, "Failed to set intensity: %d\n", 56 ret); 57 return; 58 } 59 60 /* This enable sequence will be a noop if already enabled */ 61 ret = regmap_update_bits(arizona->regmap, 62 ARIZONA_HAPTICS_CONTROL_1, 63 ARIZONA_HAP_CTRL_MASK, 64 1 << ARIZONA_HAP_CTRL_SHIFT); 65 if (ret != 0) { 66 dev_err(arizona->dev, "Failed to start haptics: %d\n", 67 ret); 68 return; 69 } 70 71 ret = snd_soc_component_enable_pin(component, "HAPTICS"); 72 if (ret != 0) { 73 dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", 74 ret); 75 return; 76 } 77 78 ret = snd_soc_dapm_sync(arizona->dapm); 79 if (ret != 0) { 80 dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 81 ret); 82 return; 83 } 84 } else { 85 /* This disable sequence will be a noop if already enabled */ 86 ret = snd_soc_component_disable_pin(component, "HAPTICS"); 87 if (ret != 0) { 88 dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", 89 ret); 90 return; 91 } 92 93 ret = snd_soc_dapm_sync(arizona->dapm); 94 if (ret != 0) { 95 dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 96 ret); 97 return; 98 } 99 100 ret = regmap_update_bits(arizona->regmap, 101 ARIZONA_HAPTICS_CONTROL_1, 102 ARIZONA_HAP_CTRL_MASK, 0); 103 if (ret != 0) { 104 dev_err(arizona->dev, "Failed to stop haptics: %d\n", 105 ret); 106 return; 107 } 108 } 109 } 110 111 static int arizona_haptics_play(struct input_dev *input, void *data, 112 struct ff_effect *effect) 113 { 114 struct arizona_haptics *haptics = input_get_drvdata(input); 115 struct arizona *arizona = haptics->arizona; 116 117 if (!arizona->dapm) { 118 dev_err(arizona->dev, "No DAPM context\n"); 119 return -EBUSY; 120 } 121 122 if (effect->u.rumble.strong_magnitude) { 123 /* Scale the magnitude into the range the device supports */ 124 if (arizona->pdata.hap_act) { 125 haptics->intensity = 126 effect->u.rumble.strong_magnitude >> 9; 127 if (effect->direction < 0x8000) 128 haptics->intensity += 0x7f; 129 } else { 130 haptics->intensity = 131 effect->u.rumble.strong_magnitude >> 8; 132 } 133 } else { 134 haptics->intensity = 0; 135 } 136 137 schedule_work(&haptics->work); 138 139 return 0; 140 } 141 142 static void arizona_haptics_close(struct input_dev *input) 143 { 144 struct arizona_haptics *haptics = input_get_drvdata(input); 145 struct snd_soc_component *component; 146 147 cancel_work_sync(&haptics->work); 148 149 if (haptics->arizona->dapm) { 150 component = snd_soc_dapm_to_component(haptics->arizona->dapm); 151 snd_soc_component_disable_pin(component, "HAPTICS"); 152 } 153 } 154 155 static int arizona_haptics_probe(struct platform_device *pdev) 156 { 157 struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); 158 struct arizona_haptics *haptics; 159 int ret; 160 161 haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); 162 if (!haptics) 163 return -ENOMEM; 164 165 haptics->arizona = arizona; 166 167 ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, 168 ARIZONA_HAP_ACT, arizona->pdata.hap_act); 169 if (ret != 0) { 170 dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", 171 ret); 172 return ret; 173 } 174 175 INIT_WORK(&haptics->work, arizona_haptics_work); 176 177 haptics->input_dev = devm_input_allocate_device(&pdev->dev); 178 if (!haptics->input_dev) { 179 dev_err(arizona->dev, "Failed to allocate input device\n"); 180 return -ENOMEM; 181 } 182 183 input_set_drvdata(haptics->input_dev, haptics); 184 185 haptics->input_dev->name = "arizona:haptics"; 186 haptics->input_dev->close = arizona_haptics_close; 187 __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); 188 189 ret = input_ff_create_memless(haptics->input_dev, NULL, 190 arizona_haptics_play); 191 if (ret < 0) { 192 dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", 193 ret); 194 return ret; 195 } 196 197 ret = input_register_device(haptics->input_dev); 198 if (ret < 0) { 199 dev_err(arizona->dev, "couldn't register input device: %d\n", 200 ret); 201 return ret; 202 } 203 204 return 0; 205 } 206 207 static struct platform_driver arizona_haptics_driver = { 208 .probe = arizona_haptics_probe, 209 .driver = { 210 .name = "arizona-haptics", 211 }, 212 }; 213 module_platform_driver(arizona_haptics_driver); 214 215 MODULE_ALIAS("platform:arizona-haptics"); 216 MODULE_DESCRIPTION("Arizona haptics driver"); 217 MODULE_LICENSE("GPL"); 218 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 219