Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/misc/twl4030-vibra.c
15111 views
1
/*
2
* twl4030-vibra.c - TWL4030 Vibrator driver
3
*
4
* Copyright (C) 2008-2010 Nokia Corporation
5
*
6
* Written by Henrik Saari <[email protected]>
7
* Updates by Felipe Balbi <[email protected]>
8
* Input by Jari Vanhala <[email protected]>
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/workqueue.h>
30
#include <linux/i2c/twl.h>
31
#include <linux/mfd/twl4030-codec.h>
32
#include <linux/input.h>
33
#include <linux/slab.h>
34
35
/* MODULE ID2 */
36
#define LEDEN 0x00
37
38
/* ForceFeedback */
39
#define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */
40
41
struct vibra_info {
42
struct device *dev;
43
struct input_dev *input_dev;
44
45
struct workqueue_struct *workqueue;
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, &reg, 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_codec_enable_resource(TWL4030_CODEC_RES_POWER);
71
72
/* turn H-Bridge on */
73
twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
74
&reg, 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_codec_enable_resource(TWL4030_CODEC_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
&reg, 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_codec_disable_resource(TWL4030_CODEC_RES_APLL);
94
twl4030_codec_disable_resource(TWL4030_CODEC_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
&reg, 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
&reg, 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
queue_work(info->workqueue, &info->play_work);
146
return 0;
147
}
148
149
static int twl4030_vibra_open(struct input_dev *input)
150
{
151
struct vibra_info *info = input_get_drvdata(input);
152
153
info->workqueue = create_singlethread_workqueue("vibra");
154
if (info->workqueue == NULL) {
155
dev_err(&input->dev, "couldn't create workqueue\n");
156
return -ENOMEM;
157
}
158
return 0;
159
}
160
161
static void twl4030_vibra_close(struct input_dev *input)
162
{
163
struct vibra_info *info = input_get_drvdata(input);
164
165
cancel_work_sync(&info->play_work);
166
INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */
167
destroy_workqueue(info->workqueue);
168
info->workqueue = NULL;
169
170
if (info->enabled)
171
vibra_disable(info);
172
}
173
174
/*** Module ***/
175
#if CONFIG_PM
176
static int twl4030_vibra_suspend(struct device *dev)
177
{
178
struct platform_device *pdev = to_platform_device(dev);
179
struct vibra_info *info = platform_get_drvdata(pdev);
180
181
if (info->enabled)
182
vibra_disable(info);
183
184
return 0;
185
}
186
187
static int twl4030_vibra_resume(struct device *dev)
188
{
189
vibra_disable_leds();
190
return 0;
191
}
192
193
static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
194
twl4030_vibra_suspend, twl4030_vibra_resume);
195
#endif
196
197
static int __devinit twl4030_vibra_probe(struct platform_device *pdev)
198
{
199
struct twl4030_codec_vibra_data *pdata = pdev->dev.platform_data;
200
struct vibra_info *info;
201
int ret;
202
203
if (!pdata) {
204
dev_dbg(&pdev->dev, "platform_data not available\n");
205
return -EINVAL;
206
}
207
208
info = kzalloc(sizeof(*info), GFP_KERNEL);
209
if (!info)
210
return -ENOMEM;
211
212
info->dev = &pdev->dev;
213
info->coexist = pdata->coexist;
214
INIT_WORK(&info->play_work, vibra_play_work);
215
216
info->input_dev = input_allocate_device();
217
if (info->input_dev == NULL) {
218
dev_err(&pdev->dev, "couldn't allocate input device\n");
219
ret = -ENOMEM;
220
goto err_kzalloc;
221
}
222
223
input_set_drvdata(info->input_dev, info);
224
225
info->input_dev->name = "twl4030:vibrator";
226
info->input_dev->id.version = 1;
227
info->input_dev->dev.parent = pdev->dev.parent;
228
info->input_dev->open = twl4030_vibra_open;
229
info->input_dev->close = twl4030_vibra_close;
230
__set_bit(FF_RUMBLE, info->input_dev->ffbit);
231
232
ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
233
if (ret < 0) {
234
dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
235
goto err_ialloc;
236
}
237
238
ret = input_register_device(info->input_dev);
239
if (ret < 0) {
240
dev_dbg(&pdev->dev, "couldn't register input device\n");
241
goto err_iff;
242
}
243
244
vibra_disable_leds();
245
246
platform_set_drvdata(pdev, info);
247
return 0;
248
249
err_iff:
250
input_ff_destroy(info->input_dev);
251
err_ialloc:
252
input_free_device(info->input_dev);
253
err_kzalloc:
254
kfree(info);
255
return ret;
256
}
257
258
static int __devexit twl4030_vibra_remove(struct platform_device *pdev)
259
{
260
struct vibra_info *info = platform_get_drvdata(pdev);
261
262
/* this also free ff-memless and calls close if needed */
263
input_unregister_device(info->input_dev);
264
kfree(info);
265
platform_set_drvdata(pdev, NULL);
266
267
return 0;
268
}
269
270
static struct platform_driver twl4030_vibra_driver = {
271
.probe = twl4030_vibra_probe,
272
.remove = __devexit_p(twl4030_vibra_remove),
273
.driver = {
274
.name = "twl4030-vibra",
275
.owner = THIS_MODULE,
276
#ifdef CONFIG_PM
277
.pm = &twl4030_vibra_pm_ops,
278
#endif
279
},
280
};
281
282
static int __init twl4030_vibra_init(void)
283
{
284
return platform_driver_register(&twl4030_vibra_driver);
285
}
286
module_init(twl4030_vibra_init);
287
288
static void __exit twl4030_vibra_exit(void)
289
{
290
platform_driver_unregister(&twl4030_vibra_driver);
291
}
292
module_exit(twl4030_vibra_exit);
293
294
MODULE_ALIAS("platform:twl4030-vibra");
295
296
MODULE_DESCRIPTION("TWL4030 Vibra driver");
297
MODULE_LICENSE("GPL");
298
MODULE_AUTHOR("Nokia Corporation");
299
300