Root/
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 mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex; |
41 | int ret; |
42 | |
43 | if (!haptics->arizona->dapm) { |
44 | dev_err(arizona->dev, "No DAPM context\n"); |
45 | return; |
46 | } |
47 | |
48 | if (haptics->intensity) { |
49 | ret = regmap_update_bits(arizona->regmap, |
50 | ARIZONA_HAPTICS_PHASE_2_INTENSITY, |
51 | ARIZONA_PHASE2_INTENSITY_MASK, |
52 | haptics->intensity); |
53 | if (ret != 0) { |
54 | dev_err(arizona->dev, "Failed to set intensity: %d\n", |
55 | ret); |
56 | return; |
57 | } |
58 | |
59 | /* This enable sequence will be a noop if already enabled */ |
60 | ret = regmap_update_bits(arizona->regmap, |
61 | ARIZONA_HAPTICS_CONTROL_1, |
62 | ARIZONA_HAP_CTRL_MASK, |
63 | 1 << ARIZONA_HAP_CTRL_SHIFT); |
64 | if (ret != 0) { |
65 | dev_err(arizona->dev, "Failed to start haptics: %d\n", |
66 | ret); |
67 | return; |
68 | } |
69 | |
70 | mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
71 | |
72 | ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); |
73 | if (ret != 0) { |
74 | dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", |
75 | ret); |
76 | mutex_unlock(dapm_mutex); |
77 | return; |
78 | } |
79 | |
80 | ret = snd_soc_dapm_sync(arizona->dapm); |
81 | if (ret != 0) { |
82 | dev_err(arizona->dev, "Failed to sync DAPM: %d\n", |
83 | ret); |
84 | mutex_unlock(dapm_mutex); |
85 | return; |
86 | } |
87 | |
88 | mutex_unlock(dapm_mutex); |
89 | |
90 | } else { |
91 | /* This disable sequence will be a noop if already enabled */ |
92 | mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
93 | |
94 | ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); |
95 | if (ret != 0) { |
96 | dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", |
97 | ret); |
98 | mutex_unlock(dapm_mutex); |
99 | return; |
100 | } |
101 | |
102 | ret = snd_soc_dapm_sync(arizona->dapm); |
103 | if (ret != 0) { |
104 | dev_err(arizona->dev, "Failed to sync DAPM: %d\n", |
105 | ret); |
106 | mutex_unlock(dapm_mutex); |
107 | return; |
108 | } |
109 | |
110 | mutex_unlock(dapm_mutex); |
111 | |
112 | ret = regmap_update_bits(arizona->regmap, |
113 | ARIZONA_HAPTICS_CONTROL_1, |
114 | ARIZONA_HAP_CTRL_MASK, |
115 | 1 << ARIZONA_HAP_CTRL_SHIFT); |
116 | if (ret != 0) { |
117 | dev_err(arizona->dev, "Failed to stop haptics: %d\n", |
118 | ret); |
119 | return; |
120 | } |
121 | } |
122 | } |
123 | |
124 | static int arizona_haptics_play(struct input_dev *input, void *data, |
125 | struct ff_effect *effect) |
126 | { |
127 | struct arizona_haptics *haptics = input_get_drvdata(input); |
128 | struct arizona *arizona = haptics->arizona; |
129 | |
130 | if (!arizona->dapm) { |
131 | dev_err(arizona->dev, "No DAPM context\n"); |
132 | return -EBUSY; |
133 | } |
134 | |
135 | if (effect->u.rumble.strong_magnitude) { |
136 | /* Scale the magnitude into the range the device supports */ |
137 | if (arizona->pdata.hap_act) { |
138 | haptics->intensity = |
139 | effect->u.rumble.strong_magnitude >> 9; |
140 | if (effect->direction < 0x8000) |
141 | haptics->intensity += 0x7f; |
142 | } else { |
143 | haptics->intensity = |
144 | effect->u.rumble.strong_magnitude >> 8; |
145 | } |
146 | } else { |
147 | haptics->intensity = 0; |
148 | } |
149 | |
150 | schedule_work(&haptics->work); |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static void arizona_haptics_close(struct input_dev *input) |
156 | { |
157 | struct arizona_haptics *haptics = input_get_drvdata(input); |
158 | struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex; |
159 | |
160 | cancel_work_sync(&haptics->work); |
161 | |
162 | mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); |
163 | |
164 | if (haptics->arizona->dapm) |
165 | snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); |
166 | |
167 | mutex_unlock(dapm_mutex); |
168 | } |
169 | |
170 | static int arizona_haptics_probe(struct platform_device *pdev) |
171 | { |
172 | struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); |
173 | struct arizona_haptics *haptics; |
174 | int ret; |
175 | |
176 | haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL); |
177 | if (!haptics) |
178 | return -ENOMEM; |
179 | |
180 | haptics->arizona = arizona; |
181 | |
182 | ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, |
183 | ARIZONA_HAP_ACT, arizona->pdata.hap_act); |
184 | if (ret != 0) { |
185 | dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", |
186 | ret); |
187 | return ret; |
188 | } |
189 | |
190 | INIT_WORK(&haptics->work, arizona_haptics_work); |
191 | |
192 | haptics->input_dev = input_allocate_device(); |
193 | if (haptics->input_dev == NULL) { |
194 | dev_err(arizona->dev, "Failed to allocate input device\n"); |
195 | return -ENOMEM; |
196 | } |
197 | |
198 | input_set_drvdata(haptics->input_dev, haptics); |
199 | |
200 | haptics->input_dev->name = "arizona:haptics"; |
201 | haptics->input_dev->dev.parent = pdev->dev.parent; |
202 | haptics->input_dev->close = arizona_haptics_close; |
203 | __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); |
204 | |
205 | ret = input_ff_create_memless(haptics->input_dev, NULL, |
206 | arizona_haptics_play); |
207 | if (ret < 0) { |
208 | dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", |
209 | ret); |
210 | goto err_ialloc; |
211 | } |
212 | |
213 | ret = input_register_device(haptics->input_dev); |
214 | if (ret < 0) { |
215 | dev_err(arizona->dev, "couldn't register input device: %d\n", |
216 | ret); |
217 | goto err_iff; |
218 | } |
219 | |
220 | platform_set_drvdata(pdev, haptics); |
221 | |
222 | return 0; |
223 | |
224 | err_iff: |
225 | if (haptics->input_dev) |
226 | input_ff_destroy(haptics->input_dev); |
227 | err_ialloc: |
228 | input_free_device(haptics->input_dev); |
229 | |
230 | return ret; |
231 | } |
232 | |
233 | static int arizona_haptics_remove(struct platform_device *pdev) |
234 | { |
235 | struct arizona_haptics *haptics = platform_get_drvdata(pdev); |
236 | |
237 | input_unregister_device(haptics->input_dev); |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static struct platform_driver arizona_haptics_driver = { |
243 | .probe = arizona_haptics_probe, |
244 | .remove = arizona_haptics_remove, |
245 | .driver = { |
246 | .name = "arizona-haptics", |
247 | .owner = THIS_MODULE, |
248 | }, |
249 | }; |
250 | module_platform_driver(arizona_haptics_driver); |
251 | |
252 | MODULE_ALIAS("platform:arizona-haptics"); |
253 | MODULE_DESCRIPTION("Arizona haptics driver"); |
254 | MODULE_LICENSE("GPL"); |
255 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); |
256 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9