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