GNU Linux-libre 4.14.266-gnu1
[releases.git] / drivers / input / misc / arizona-haptics.c
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>");