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 int ret; 41 42 if (!haptics->arizona->dapm) { 43 dev_err(arizona->dev, "No DAPM context\n"); 44 return; 45 } 46 47 if (haptics->intensity) { 48 ret = regmap_update_bits(arizona->regmap, 49 ARIZONA_HAPTICS_PHASE_2_INTENSITY, 50 ARIZONA_PHASE2_INTENSITY_MASK, 51 haptics->intensity); 52 if (ret != 0) { 53 dev_err(arizona->dev, "Failed to set intensity: %d\n", 54 ret); 55 return; 56 } 57 58 /* This enable sequence will be a noop if already enabled */ 59 ret = regmap_update_bits(arizona->regmap, 60 ARIZONA_HAPTICS_CONTROL_1, 61 ARIZONA_HAP_CTRL_MASK, 62 1 << ARIZONA_HAP_CTRL_SHIFT); 63 if (ret != 0) { 64 dev_err(arizona->dev, "Failed to start haptics: %d\n", 65 ret); 66 return; 67 } 68 69 ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); 70 if (ret != 0) { 71 dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", 72 ret); 73 return; 74 } 75 76 ret = snd_soc_dapm_sync(arizona->dapm); 77 if (ret != 0) { 78 dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 79 ret); 80 return; 81 } 82 } else { 83 /* This disable sequence will be a noop if already enabled */ 84 ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); 85 if (ret != 0) { 86 dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", 87 ret); 88 return; 89 } 90 91 ret = snd_soc_dapm_sync(arizona->dapm); 92 if (ret != 0) { 93 dev_err(arizona->dev, "Failed to sync DAPM: %d\n", 94 ret); 95 return; 96 } 97 98 ret = regmap_update_bits(arizona->regmap, 99 ARIZONA_HAPTICS_CONTROL_1, 100 ARIZONA_HAP_CTRL_MASK, 0); 101 if (ret != 0) { 102 dev_err(arizona->dev, "Failed to stop haptics: %d\n", 103 ret); 104 return; 105 } 106 } 107 } 108 109 static int arizona_haptics_play(struct input_dev *input, void *data, 110 struct ff_effect *effect) 111 { 112 struct arizona_haptics *haptics = input_get_drvdata(input); 113 struct arizona *arizona = haptics->arizona; 114 115 if (!arizona->dapm) { 116 dev_err(arizona->dev, "No DAPM context\n"); 117 return -EBUSY; 118 } 119 120 if (effect->u.rumble.strong_magnitude) { 121 /* Scale the magnitude into the range the device supports */ 122 if (arizona->pdata.hap_act) { 123 haptics->intensity = 124 effect->u.rumble.strong_magnitude >> 9; 125 if (effect->direction < 0x8000) 126 haptics->intensity += 0x7f; 127 } else { 128 haptics->intensity = 129 effect->u.rumble.strong_magnitude >> 8; 130 } 131 } else { 132 haptics->intensity = 0; 133 } 134 135 schedule_work(&haptics->work); 136 137 return 0; 138 } 139 140 static void arizona_haptics_close(struct input_dev *input) 141 { 142 struct arizona_haptics *haptics = input_get_drvdata(input); 143 144 cancel_work_sync(&haptics->work); 145 146 if (haptics->arizona->dapm) 147 snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); 148 } 149 150 static int arizona_haptics_probe(struct platform_device *pdev) 151 { 152 struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); 153 struct arizona_haptics *haptics; 154 int ret; 155 156 haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); 157 if (!haptics) 158 return -ENOMEM; 159 160 haptics->arizona = arizona; 161 162 ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, 163 ARIZONA_HAP_ACT, arizona->pdata.hap_act); 164 if (ret != 0) { 165 dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", 166 ret); 167 return ret; 168 } 169 170 INIT_WORK(&haptics->work, arizona_haptics_work); 171 172 haptics->input_dev = devm_input_allocate_device(&pdev->dev); 173 if (!haptics->input_dev) { 174 dev_err(arizona->dev, "Failed to allocate input device\n"); 175 return -ENOMEM; 176 } 177 178 input_set_drvdata(haptics->input_dev, haptics); 179 180 haptics->input_dev->name = "arizona:haptics"; 181 haptics->input_dev->close = arizona_haptics_close; 182 __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); 183 184 ret = input_ff_create_memless(haptics->input_dev, NULL, 185 arizona_haptics_play); 186 if (ret < 0) { 187 dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", 188 ret); 189 return ret; 190 } 191 192 ret = input_register_device(haptics->input_dev); 193 if (ret < 0) { 194 dev_err(arizona->dev, "couldn't register input device: %d\n", 195 ret); 196 return ret; 197 } 198 199 platform_set_drvdata(pdev, haptics); 200 201 return 0; 202 } 203 204 static struct platform_driver arizona_haptics_driver = { 205 .probe = arizona_haptics_probe, 206 .driver = { 207 .name = "arizona-haptics", 208 }, 209 }; 210 module_platform_driver(arizona_haptics_driver); 211 212 MODULE_ALIAS("platform:arizona-haptics"); 213 MODULE_DESCRIPTION("Arizona haptics driver"); 214 MODULE_LICENSE("GPL"); 215 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 216