Root/
1 | /* |
2 | * twl4030-vibra.c - TWL4030 Vibrator driver |
3 | * |
4 | * Copyright (C) 2008-2010 Nokia Corporation |
5 | * |
6 | * Written by Henrik Saari <henrik.saari@nokia.com> |
7 | * Updates by Felipe Balbi <felipe.balbi@nokia.com> |
8 | * Input by Jari Vanhala <ext-jari.vanhala@nokia.com> |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, but |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
17 | * General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA |
22 | * 02110-1301 USA |
23 | * |
24 | */ |
25 | |
26 | #include <linux/module.h> |
27 | #include <linux/jiffies.h> |
28 | #include <linux/platform_device.h> |
29 | #include <linux/of.h> |
30 | #include <linux/workqueue.h> |
31 | #include <linux/i2c/twl.h> |
32 | #include <linux/mfd/twl4030-audio.h> |
33 | #include <linux/input.h> |
34 | #include <linux/slab.h> |
35 | |
36 | /* MODULE ID2 */ |
37 | #define LEDEN 0x00 |
38 | |
39 | /* ForceFeedback */ |
40 | #define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */ |
41 | |
42 | struct vibra_info { |
43 | struct device *dev; |
44 | struct input_dev *input_dev; |
45 | |
46 | struct work_struct play_work; |
47 | |
48 | bool enabled; |
49 | int speed; |
50 | int direction; |
51 | |
52 | bool coexist; |
53 | }; |
54 | |
55 | static void vibra_disable_leds(void) |
56 | { |
57 | u8 reg; |
58 | |
59 | /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */ |
60 | twl_i2c_read_u8(TWL4030_MODULE_LED, ®, LEDEN); |
61 | reg &= ~0x03; |
62 | twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg); |
63 | } |
64 | |
65 | /* Powers H-Bridge and enables audio clk */ |
66 | static void vibra_enable(struct vibra_info *info) |
67 | { |
68 | u8 reg; |
69 | |
70 | twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER); |
71 | |
72 | /* turn H-Bridge on */ |
73 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, |
74 | ®, TWL4030_REG_VIBRA_CTL); |
75 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
76 | (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); |
77 | |
78 | twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL); |
79 | |
80 | info->enabled = true; |
81 | } |
82 | |
83 | static void vibra_disable(struct vibra_info *info) |
84 | { |
85 | u8 reg; |
86 | |
87 | /* Power down H-Bridge */ |
88 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, |
89 | ®, TWL4030_REG_VIBRA_CTL); |
90 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
91 | (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL); |
92 | |
93 | twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL); |
94 | twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER); |
95 | |
96 | info->enabled = false; |
97 | } |
98 | |
99 | static void vibra_play_work(struct work_struct *work) |
100 | { |
101 | struct vibra_info *info = container_of(work, |
102 | struct vibra_info, play_work); |
103 | int dir; |
104 | int pwm; |
105 | u8 reg; |
106 | |
107 | dir = info->direction; |
108 | pwm = info->speed; |
109 | |
110 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, |
111 | ®, TWL4030_REG_VIBRA_CTL); |
112 | if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) { |
113 | |
114 | if (!info->enabled) |
115 | vibra_enable(info); |
116 | |
117 | /* set vibra rotation direction */ |
118 | twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, |
119 | ®, TWL4030_REG_VIBRA_CTL); |
120 | reg = (dir) ? (reg | TWL4030_VIBRA_DIR) : |
121 | (reg & ~TWL4030_VIBRA_DIR); |
122 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
123 | reg, TWL4030_REG_VIBRA_CTL); |
124 | |
125 | /* set PWM, 1 = max, 255 = min */ |
126 | twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, |
127 | 256 - pwm, TWL4030_REG_VIBRA_SET); |
128 | } else { |
129 | if (info->enabled) |
130 | vibra_disable(info); |
131 | } |
132 | } |
133 | |
134 | /*** Input/ForceFeedback ***/ |
135 | |
136 | static int vibra_play(struct input_dev *input, void *data, |
137 | struct ff_effect *effect) |
138 | { |
139 | struct vibra_info *info = input_get_drvdata(input); |
140 | |
141 | info->speed = effect->u.rumble.strong_magnitude >> 8; |
142 | if (!info->speed) |
143 | info->speed = effect->u.rumble.weak_magnitude >> 9; |
144 | info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; |
145 | schedule_work(&info->play_work); |
146 | return 0; |
147 | } |
148 | |
149 | static void twl4030_vibra_close(struct input_dev *input) |
150 | { |
151 | struct vibra_info *info = input_get_drvdata(input); |
152 | |
153 | cancel_work_sync(&info->play_work); |
154 | |
155 | if (info->enabled) |
156 | vibra_disable(info); |
157 | } |
158 | |
159 | /*** Module ***/ |
160 | #ifdef CONFIG_PM_SLEEP |
161 | static int twl4030_vibra_suspend(struct device *dev) |
162 | { |
163 | struct platform_device *pdev = to_platform_device(dev); |
164 | struct vibra_info *info = platform_get_drvdata(pdev); |
165 | |
166 | if (info->enabled) |
167 | vibra_disable(info); |
168 | |
169 | return 0; |
170 | } |
171 | |
172 | static int twl4030_vibra_resume(struct device *dev) |
173 | { |
174 | vibra_disable_leds(); |
175 | return 0; |
176 | } |
177 | #endif |
178 | |
179 | static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops, |
180 | twl4030_vibra_suspend, twl4030_vibra_resume); |
181 | |
182 | static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata, |
183 | struct device_node *node) |
184 | { |
185 | if (pdata && pdata->coexist) |
186 | return true; |
187 | |
188 | if (of_find_node_by_name(node, "codec")) |
189 | return true; |
190 | |
191 | return false; |
192 | } |
193 | |
194 | static int twl4030_vibra_probe(struct platform_device *pdev) |
195 | { |
196 | struct twl4030_vibra_data *pdata = pdev->dev.platform_data; |
197 | struct device_node *twl4030_core_node = pdev->dev.parent->of_node; |
198 | struct vibra_info *info; |
199 | int ret; |
200 | |
201 | if (!pdata && !twl4030_core_node) { |
202 | dev_dbg(&pdev->dev, "platform_data not available\n"); |
203 | return -EINVAL; |
204 | } |
205 | |
206 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
207 | if (!info) |
208 | return -ENOMEM; |
209 | |
210 | info->dev = &pdev->dev; |
211 | info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); |
212 | INIT_WORK(&info->play_work, vibra_play_work); |
213 | |
214 | info->input_dev = devm_input_allocate_device(&pdev->dev); |
215 | if (info->input_dev == NULL) { |
216 | dev_err(&pdev->dev, "couldn't allocate input device\n"); |
217 | return -ENOMEM; |
218 | } |
219 | |
220 | input_set_drvdata(info->input_dev, info); |
221 | |
222 | info->input_dev->name = "twl4030:vibrator"; |
223 | info->input_dev->id.version = 1; |
224 | info->input_dev->dev.parent = pdev->dev.parent; |
225 | info->input_dev->close = twl4030_vibra_close; |
226 | __set_bit(FF_RUMBLE, info->input_dev->ffbit); |
227 | |
228 | ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); |
229 | if (ret < 0) { |
230 | dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); |
231 | return ret; |
232 | } |
233 | |
234 | ret = input_register_device(info->input_dev); |
235 | if (ret < 0) { |
236 | dev_dbg(&pdev->dev, "couldn't register input device\n"); |
237 | goto err_iff; |
238 | } |
239 | |
240 | vibra_disable_leds(); |
241 | |
242 | platform_set_drvdata(pdev, info); |
243 | return 0; |
244 | |
245 | err_iff: |
246 | input_ff_destroy(info->input_dev); |
247 | return ret; |
248 | } |
249 | |
250 | static struct platform_driver twl4030_vibra_driver = { |
251 | .probe = twl4030_vibra_probe, |
252 | .driver = { |
253 | .name = "twl4030-vibra", |
254 | .owner = THIS_MODULE, |
255 | .pm = &twl4030_vibra_pm_ops, |
256 | }, |
257 | }; |
258 | module_platform_driver(twl4030_vibra_driver); |
259 | |
260 | MODULE_ALIAS("platform:twl4030-vibra"); |
261 | MODULE_DESCRIPTION("TWL4030 Vibra driver"); |
262 | MODULE_LICENSE("GPL"); |
263 | MODULE_AUTHOR("Nokia Corporation"); |
264 |
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