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