1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * MAX8997-haptic controller driver |
4 | * |
5 | * Copyright (C) 2012 Samsung Electronics |
6 | * Donggeun Kim <dg77.kim@samsung.com> |
7 | * |
8 | * This program is not provided / owned by Maxim Integrated Products. |
9 | */ |
10 | |
11 | #include <linux/module.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/err.h> |
15 | #include <linux/pwm.h> |
16 | #include <linux/input.h> |
17 | #include <linux/mfd/max8997-private.h> |
18 | #include <linux/mfd/max8997.h> |
19 | #include <linux/regulator/consumer.h> |
20 | |
21 | /* Haptic configuration 2 register */ |
22 | #define MAX8997_MOTOR_TYPE_SHIFT 7 |
23 | #define MAX8997_ENABLE_SHIFT 6 |
24 | #define MAX8997_MODE_SHIFT 5 |
25 | |
26 | /* Haptic driver configuration register */ |
27 | #define MAX8997_CYCLE_SHIFT 6 |
28 | #define MAX8997_SIG_PERIOD_SHIFT 4 |
29 | #define MAX8997_SIG_DUTY_SHIFT 2 |
30 | #define MAX8997_PWM_DUTY_SHIFT 0 |
31 | |
32 | struct max8997_haptic { |
33 | struct device *dev; |
34 | struct i2c_client *client; |
35 | struct input_dev *input_dev; |
36 | struct regulator *regulator; |
37 | |
38 | struct work_struct work; |
39 | struct mutex mutex; |
40 | |
41 | bool enabled; |
42 | unsigned int level; |
43 | |
44 | struct pwm_device *pwm; |
45 | unsigned int pwm_period; |
46 | enum max8997_haptic_pwm_divisor pwm_divisor; |
47 | |
48 | enum max8997_haptic_motor_type type; |
49 | enum max8997_haptic_pulse_mode mode; |
50 | |
51 | unsigned int internal_mode_pattern; |
52 | unsigned int pattern_cycle; |
53 | unsigned int pattern_signal_period; |
54 | }; |
55 | |
56 | static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip) |
57 | { |
58 | int ret = 0; |
59 | |
60 | if (chip->mode == MAX8997_EXTERNAL_MODE) { |
61 | unsigned int duty = chip->pwm_period * chip->level / 100; |
62 | ret = pwm_config(pwm: chip->pwm, duty_ns: duty, period_ns: chip->pwm_period); |
63 | } else { |
64 | u8 duty_index = 0; |
65 | |
66 | duty_index = DIV_ROUND_UP(chip->level * 64, 100); |
67 | |
68 | switch (chip->internal_mode_pattern) { |
69 | case 0: |
70 | max8997_write_reg(i2c: chip->client, |
71 | reg: MAX8997_HAPTIC_REG_SIGPWMDC1, value: duty_index); |
72 | break; |
73 | case 1: |
74 | max8997_write_reg(i2c: chip->client, |
75 | reg: MAX8997_HAPTIC_REG_SIGPWMDC2, value: duty_index); |
76 | break; |
77 | case 2: |
78 | max8997_write_reg(i2c: chip->client, |
79 | reg: MAX8997_HAPTIC_REG_SIGPWMDC3, value: duty_index); |
80 | break; |
81 | case 3: |
82 | max8997_write_reg(i2c: chip->client, |
83 | reg: MAX8997_HAPTIC_REG_SIGPWMDC4, value: duty_index); |
84 | break; |
85 | default: |
86 | break; |
87 | } |
88 | } |
89 | return ret; |
90 | } |
91 | |
92 | static void max8997_haptic_configure(struct max8997_haptic *chip) |
93 | { |
94 | u8 value; |
95 | |
96 | value = chip->type << MAX8997_MOTOR_TYPE_SHIFT | |
97 | chip->enabled << MAX8997_ENABLE_SHIFT | |
98 | chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor; |
99 | max8997_write_reg(i2c: chip->client, reg: MAX8997_HAPTIC_REG_CONF2, value); |
100 | |
101 | if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) { |
102 | value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT | |
103 | chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT | |
104 | chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT | |
105 | chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT; |
106 | max8997_write_reg(i2c: chip->client, |
107 | reg: MAX8997_HAPTIC_REG_DRVCONF, value); |
108 | |
109 | switch (chip->internal_mode_pattern) { |
110 | case 0: |
111 | value = chip->pattern_cycle << 4; |
112 | max8997_write_reg(i2c: chip->client, |
113 | reg: MAX8997_HAPTIC_REG_CYCLECONF1, value); |
114 | value = chip->pattern_signal_period; |
115 | max8997_write_reg(i2c: chip->client, |
116 | reg: MAX8997_HAPTIC_REG_SIGCONF1, value); |
117 | break; |
118 | |
119 | case 1: |
120 | value = chip->pattern_cycle; |
121 | max8997_write_reg(i2c: chip->client, |
122 | reg: MAX8997_HAPTIC_REG_CYCLECONF1, value); |
123 | value = chip->pattern_signal_period; |
124 | max8997_write_reg(i2c: chip->client, |
125 | reg: MAX8997_HAPTIC_REG_SIGCONF2, value); |
126 | break; |
127 | |
128 | case 2: |
129 | value = chip->pattern_cycle << 4; |
130 | max8997_write_reg(i2c: chip->client, |
131 | reg: MAX8997_HAPTIC_REG_CYCLECONF2, value); |
132 | value = chip->pattern_signal_period; |
133 | max8997_write_reg(i2c: chip->client, |
134 | reg: MAX8997_HAPTIC_REG_SIGCONF3, value); |
135 | break; |
136 | |
137 | case 3: |
138 | value = chip->pattern_cycle; |
139 | max8997_write_reg(i2c: chip->client, |
140 | reg: MAX8997_HAPTIC_REG_CYCLECONF2, value); |
141 | value = chip->pattern_signal_period; |
142 | max8997_write_reg(i2c: chip->client, |
143 | reg: MAX8997_HAPTIC_REG_SIGCONF4, value); |
144 | break; |
145 | |
146 | default: |
147 | break; |
148 | } |
149 | } |
150 | } |
151 | |
152 | static void max8997_haptic_enable(struct max8997_haptic *chip) |
153 | { |
154 | int error; |
155 | |
156 | mutex_lock(&chip->mutex); |
157 | |
158 | error = max8997_haptic_set_duty_cycle(chip); |
159 | if (error) { |
160 | dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n" , error); |
161 | goto out; |
162 | } |
163 | |
164 | if (!chip->enabled) { |
165 | error = regulator_enable(regulator: chip->regulator); |
166 | if (error) { |
167 | dev_err(chip->dev, "Failed to enable regulator\n" ); |
168 | goto out; |
169 | } |
170 | max8997_haptic_configure(chip); |
171 | if (chip->mode == MAX8997_EXTERNAL_MODE) { |
172 | error = pwm_enable(pwm: chip->pwm); |
173 | if (error) { |
174 | dev_err(chip->dev, "Failed to enable PWM\n" ); |
175 | regulator_disable(regulator: chip->regulator); |
176 | goto out; |
177 | } |
178 | } |
179 | chip->enabled = true; |
180 | } |
181 | |
182 | out: |
183 | mutex_unlock(lock: &chip->mutex); |
184 | } |
185 | |
186 | static void max8997_haptic_disable(struct max8997_haptic *chip) |
187 | { |
188 | mutex_lock(&chip->mutex); |
189 | |
190 | if (chip->enabled) { |
191 | chip->enabled = false; |
192 | max8997_haptic_configure(chip); |
193 | if (chip->mode == MAX8997_EXTERNAL_MODE) |
194 | pwm_disable(pwm: chip->pwm); |
195 | regulator_disable(regulator: chip->regulator); |
196 | } |
197 | |
198 | mutex_unlock(lock: &chip->mutex); |
199 | } |
200 | |
201 | static void max8997_haptic_play_effect_work(struct work_struct *work) |
202 | { |
203 | struct max8997_haptic *chip = |
204 | container_of(work, struct max8997_haptic, work); |
205 | |
206 | if (chip->level) |
207 | max8997_haptic_enable(chip); |
208 | else |
209 | max8997_haptic_disable(chip); |
210 | } |
211 | |
212 | static int max8997_haptic_play_effect(struct input_dev *dev, void *data, |
213 | struct ff_effect *effect) |
214 | { |
215 | struct max8997_haptic *chip = input_get_drvdata(dev); |
216 | |
217 | chip->level = effect->u.rumble.strong_magnitude; |
218 | if (!chip->level) |
219 | chip->level = effect->u.rumble.weak_magnitude; |
220 | |
221 | schedule_work(work: &chip->work); |
222 | |
223 | return 0; |
224 | } |
225 | |
226 | static void max8997_haptic_close(struct input_dev *dev) |
227 | { |
228 | struct max8997_haptic *chip = input_get_drvdata(dev); |
229 | |
230 | cancel_work_sync(work: &chip->work); |
231 | max8997_haptic_disable(chip); |
232 | } |
233 | |
234 | static int max8997_haptic_probe(struct platform_device *pdev) |
235 | { |
236 | struct max8997_dev *iodev = dev_get_drvdata(dev: pdev->dev.parent); |
237 | const struct max8997_platform_data *pdata = |
238 | dev_get_platdata(dev: iodev->dev); |
239 | const struct max8997_haptic_platform_data *haptic_pdata = NULL; |
240 | struct max8997_haptic *chip; |
241 | struct input_dev *input_dev; |
242 | int error; |
243 | |
244 | if (pdata) |
245 | haptic_pdata = pdata->haptic_pdata; |
246 | |
247 | if (!haptic_pdata) { |
248 | dev_err(&pdev->dev, "no haptic platform data\n" ); |
249 | return -EINVAL; |
250 | } |
251 | |
252 | chip = kzalloc(size: sizeof(struct max8997_haptic), GFP_KERNEL); |
253 | input_dev = input_allocate_device(); |
254 | if (!chip || !input_dev) { |
255 | dev_err(&pdev->dev, "unable to allocate memory\n" ); |
256 | error = -ENOMEM; |
257 | goto err_free_mem; |
258 | } |
259 | |
260 | INIT_WORK(&chip->work, max8997_haptic_play_effect_work); |
261 | mutex_init(&chip->mutex); |
262 | |
263 | chip->client = iodev->haptic; |
264 | chip->dev = &pdev->dev; |
265 | chip->input_dev = input_dev; |
266 | chip->pwm_period = haptic_pdata->pwm_period; |
267 | chip->type = haptic_pdata->type; |
268 | chip->mode = haptic_pdata->mode; |
269 | chip->pwm_divisor = haptic_pdata->pwm_divisor; |
270 | |
271 | switch (chip->mode) { |
272 | case MAX8997_INTERNAL_MODE: |
273 | chip->internal_mode_pattern = |
274 | haptic_pdata->internal_mode_pattern; |
275 | chip->pattern_cycle = haptic_pdata->pattern_cycle; |
276 | chip->pattern_signal_period = |
277 | haptic_pdata->pattern_signal_period; |
278 | break; |
279 | |
280 | case MAX8997_EXTERNAL_MODE: |
281 | chip->pwm = pwm_get(dev: &pdev->dev, NULL); |
282 | if (IS_ERR(ptr: chip->pwm)) { |
283 | error = PTR_ERR(ptr: chip->pwm); |
284 | dev_err(&pdev->dev, |
285 | "unable to request PWM for haptic, error: %d\n" , |
286 | error); |
287 | goto err_free_mem; |
288 | } |
289 | |
290 | /* |
291 | * FIXME: pwm_apply_args() should be removed when switching to |
292 | * the atomic PWM API. |
293 | */ |
294 | pwm_apply_args(pwm: chip->pwm); |
295 | break; |
296 | |
297 | default: |
298 | dev_err(&pdev->dev, |
299 | "Invalid chip mode specified (%d)\n" , chip->mode); |
300 | error = -EINVAL; |
301 | goto err_free_mem; |
302 | } |
303 | |
304 | chip->regulator = regulator_get(dev: &pdev->dev, id: "inmotor" ); |
305 | if (IS_ERR(ptr: chip->regulator)) { |
306 | error = PTR_ERR(ptr: chip->regulator); |
307 | dev_err(&pdev->dev, |
308 | "unable to get regulator, error: %d\n" , |
309 | error); |
310 | goto err_free_pwm; |
311 | } |
312 | |
313 | input_dev->name = "max8997-haptic" ; |
314 | input_dev->id.version = 1; |
315 | input_dev->dev.parent = &pdev->dev; |
316 | input_dev->close = max8997_haptic_close; |
317 | input_set_drvdata(dev: input_dev, data: chip); |
318 | input_set_capability(dev: input_dev, EV_FF, FF_RUMBLE); |
319 | |
320 | error = input_ff_create_memless(dev: input_dev, NULL, |
321 | play_effect: max8997_haptic_play_effect); |
322 | if (error) { |
323 | dev_err(&pdev->dev, |
324 | "unable to create FF device, error: %d\n" , |
325 | error); |
326 | goto err_put_regulator; |
327 | } |
328 | |
329 | error = input_register_device(input_dev); |
330 | if (error) { |
331 | dev_err(&pdev->dev, |
332 | "unable to register input device, error: %d\n" , |
333 | error); |
334 | goto err_destroy_ff; |
335 | } |
336 | |
337 | platform_set_drvdata(pdev, data: chip); |
338 | return 0; |
339 | |
340 | err_destroy_ff: |
341 | input_ff_destroy(dev: input_dev); |
342 | err_put_regulator: |
343 | regulator_put(regulator: chip->regulator); |
344 | err_free_pwm: |
345 | if (chip->mode == MAX8997_EXTERNAL_MODE) |
346 | pwm_put(pwm: chip->pwm); |
347 | err_free_mem: |
348 | input_free_device(dev: input_dev); |
349 | kfree(objp: chip); |
350 | |
351 | return error; |
352 | } |
353 | |
354 | static int max8997_haptic_remove(struct platform_device *pdev) |
355 | { |
356 | struct max8997_haptic *chip = platform_get_drvdata(pdev); |
357 | |
358 | input_unregister_device(chip->input_dev); |
359 | regulator_put(regulator: chip->regulator); |
360 | |
361 | if (chip->mode == MAX8997_EXTERNAL_MODE) |
362 | pwm_put(pwm: chip->pwm); |
363 | |
364 | kfree(objp: chip); |
365 | |
366 | return 0; |
367 | } |
368 | |
369 | static int max8997_haptic_suspend(struct device *dev) |
370 | { |
371 | struct platform_device *pdev = to_platform_device(dev); |
372 | struct max8997_haptic *chip = platform_get_drvdata(pdev); |
373 | |
374 | max8997_haptic_disable(chip); |
375 | |
376 | return 0; |
377 | } |
378 | |
379 | static DEFINE_SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, |
380 | max8997_haptic_suspend, NULL); |
381 | |
382 | static const struct platform_device_id max8997_haptic_id[] = { |
383 | { "max8997-haptic" , 0 }, |
384 | { }, |
385 | }; |
386 | MODULE_DEVICE_TABLE(platform, max8997_haptic_id); |
387 | |
388 | static struct platform_driver max8997_haptic_driver = { |
389 | .driver = { |
390 | .name = "max8997-haptic" , |
391 | .pm = pm_sleep_ptr(&max8997_haptic_pm_ops), |
392 | }, |
393 | .probe = max8997_haptic_probe, |
394 | .remove = max8997_haptic_remove, |
395 | .id_table = max8997_haptic_id, |
396 | }; |
397 | module_platform_driver(max8997_haptic_driver); |
398 | |
399 | MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>" ); |
400 | MODULE_DESCRIPTION("max8997_haptic driver" ); |
401 | MODULE_LICENSE("GPL" ); |
402 | |