aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorJaewon Kim <jaewon02.kim@samsung.com>2015-03-04 17:43:40 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-03-06 14:24:53 -0500
commite5abff1fe2e400bdabd14feb4e69e9ad661ba71a (patch)
treedf74337e24f2398db0f496cec25109d0e68f7f5d /drivers/input
parent0c7e67a928ac5328d30a0638adec771511dc7074 (diff)
Input: add haptic support for max77843
This patch adds support for haptic on max77843 MFD (Multi Function Device) with PMIC, MUIC, LED, CHARGER. This driver supports external pwm and LRA (Linear Resonant Actuator) motor. Signed-off-by: Jaewon Kim <jaewon02.kim@samsung.com> [Jim Davis <jim.epost@gmail.com>: should depend on REGULATOR not PWM] Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/misc/Kconfig12
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/max77843-haptic.c358
3 files changed, 371 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6deb8dae3205..ef542f7ad944 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -165,6 +165,18 @@ config INPUT_MAX77693_HAPTIC
165 To compile this driver as module, choose M here: the 165 To compile this driver as module, choose M here: the
166 module will be called max77693-haptic. 166 module will be called max77693-haptic.
167 167
168config INPUT_MAX77843_HAPTIC
169 tristate "MAXIM MAX77843 haptic controller support"
170 depends on MFD_MAX77843 && REGULATOR
171 select INPUT_FF_MEMLESS
172 help
173 This option enables support for the haptic controller on
174 MAXIM MAX77843 chip. The driver supports ff-memless interface
175 from input framework.
176
177 To compile this driver as module, choose M here: the
178 module will be called max77843-haptic.
179
168config INPUT_MAX8925_ONKEY 180config INPUT_MAX8925_ONKEY
169 tristate "MAX8925 ONKEY support" 181 tristate "MAX8925 ONKEY support"
170 depends on MFD_MAX8925 182 depends on MFD_MAX8925
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 403a1a54a76c..75b58841d5a7 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
39obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o 39obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
40obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o 40obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
41obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o 41obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o
42obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o
42obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o 43obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
43obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o 44obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
44obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o 45obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c
new file mode 100644
index 000000000000..dccbb465a055
--- /dev/null
+++ b/drivers/input/misc/max77843-haptic.c
@@ -0,0 +1,358 @@
1/*
2 * MAXIM MAX77693 Haptic device driver
3 *
4 * Copyright (C) 2015 Samsung Electronics
5 * Author: Jaewon Kim <jaewon02.kim@samsung.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13#include <linux/err.h>
14#include <linux/i2c.h>
15#include <linux/init.h>
16#include <linux/input.h>
17#include <linux/mfd/max77843-private.h>
18#include <linux/module.h>
19#include <linux/platform_device.h>
20#include <linux/pwm.h>
21#include <linux/regmap.h>
22#include <linux/regulator/consumer.h>
23#include <linux/slab.h>
24#include <linux/workqueue.h>
25
26#define MAX_MAGNITUDE_SHIFT 16
27
28enum max77843_haptic_motor_type {
29 MAX77843_HAPTIC_ERM = 0,
30 MAX77843_HAPTIC_LRA,
31};
32
33enum max77843_haptic_pwm_divisor {
34 MAX77843_HAPTIC_PWM_DIVISOR_32 = 0,
35 MAX77843_HAPTIC_PWM_DIVISOR_64,
36 MAX77843_HAPTIC_PWM_DIVISOR_128,
37 MAX77843_HAPTIC_PWM_DIVISOR_256,
38};
39
40struct max77843_haptic {
41 struct regmap *regmap_haptic;
42 struct device *dev;
43 struct input_dev *input_dev;
44 struct pwm_device *pwm_dev;
45 struct regulator *motor_reg;
46 struct work_struct work;
47 struct mutex mutex;
48
49 unsigned int magnitude;
50 unsigned int pwm_duty;
51
52 bool active;
53 bool suspended;
54
55 enum max77843_haptic_motor_type type;
56 enum max77843_haptic_pwm_divisor pwm_divisor;
57};
58
59static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic)
60{
61 int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2;
62 int error;
63
64 error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period);
65 if (error) {
66 dev_err(haptic->dev, "failed to configure pwm: %d\n", error);
67 return error;
68 }
69
70 return 0;
71}
72
73static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on)
74{
75 int error;
76
77 error = regmap_update_bits(haptic->regmap_haptic,
78 MAX77843_SYS_REG_MAINCTRL1,
79 MAX77843_MAINCTRL1_BIASEN_MASK,
80 on << MAINCTRL1_BIASEN_SHIFT);
81 if (error) {
82 dev_err(haptic->dev, "failed to %s bias: %d\n",
83 on ? "enable" : "disable", error);
84 return error;
85 }
86
87 return 0;
88}
89
90static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable)
91{
92 unsigned int value;
93 int error;
94
95 value = (haptic->type << MCONFIG_MODE_SHIFT) |
96 (enable << MCONFIG_MEN_SHIFT) |
97 (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT);
98
99 error = regmap_write(haptic->regmap_haptic,
100 MAX77843_HAP_REG_MCONFIG, value);
101 if (error) {
102 dev_err(haptic->dev,
103 "failed to update haptic config: %d\n", error);
104 return error;
105 }
106
107 return 0;
108}
109
110static int max77843_haptic_enable(struct max77843_haptic *haptic)
111{
112 int error;
113
114 if (haptic->active)
115 return 0;
116
117 error = pwm_enable(haptic->pwm_dev);
118 if (error) {
119 dev_err(haptic->dev,
120 "failed to enable pwm device: %d\n", error);
121 return error;
122 }
123
124 error = max77843_haptic_config(haptic, true);
125 if (error)
126 goto err_config;
127
128 haptic->active = true;
129
130 return 0;
131
132err_config:
133 pwm_disable(haptic->pwm_dev);
134
135 return error;
136}
137
138static int max77843_haptic_disable(struct max77843_haptic *haptic)
139{
140 int error;
141
142 if (!haptic->active)
143 return 0;
144
145 error = max77843_haptic_config(haptic, false);
146 if (error)
147 return error;
148
149 pwm_disable(haptic->pwm_dev);
150
151 haptic->active = false;
152
153 return 0;
154}
155
156static void max77843_haptic_play_work(struct work_struct *work)
157{
158 struct max77843_haptic *haptic =
159 container_of(work, struct max77843_haptic, work);
160 int error;
161
162 mutex_lock(&haptic->mutex);
163
164 if (haptic->suspended)
165 goto out_unlock;
166
167 if (haptic->magnitude) {
168 error = max77843_haptic_set_duty_cycle(haptic);
169 if (error) {
170 dev_err(haptic->dev,
171 "failed to set duty cycle: %d\n", error);
172 goto out_unlock;
173 }
174
175 error = max77843_haptic_enable(haptic);
176 if (error)
177 dev_err(haptic->dev,
178 "cannot enable haptic: %d\n", error);
179 } else {
180 error = max77843_haptic_disable(haptic);
181 if (error)
182 dev_err(haptic->dev,
183 "cannot disable haptic: %d\n", error);
184 }
185
186out_unlock:
187 mutex_unlock(&haptic->mutex);
188}
189
190static int max77843_haptic_play_effect(struct input_dev *dev, void *data,
191 struct ff_effect *effect)
192{
193 struct max77843_haptic *haptic = input_get_drvdata(dev);
194 u64 period_mag_multi;
195
196 haptic->magnitude = effect->u.rumble.strong_magnitude;
197 if (!haptic->magnitude)
198 haptic->magnitude = effect->u.rumble.weak_magnitude;
199
200 period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude;
201 haptic->pwm_duty = (unsigned int)(period_mag_multi >>
202 MAX_MAGNITUDE_SHIFT);
203
204 schedule_work(&haptic->work);
205
206 return 0;
207}
208
209static int max77843_haptic_open(struct input_dev *dev)
210{
211 struct max77843_haptic *haptic = input_get_drvdata(dev);
212 int error;
213
214 error = max77843_haptic_bias(haptic, true);
215 if (error)
216 return error;
217
218 error = regulator_enable(haptic->motor_reg);
219 if (error) {
220 dev_err(haptic->dev,
221 "failed to enable regulator: %d\n", error);
222 return error;
223 }
224
225 return 0;
226}
227
228static void max77843_haptic_close(struct input_dev *dev)
229{
230 struct max77843_haptic *haptic = input_get_drvdata(dev);
231 int error;
232
233 cancel_work_sync(&haptic->work);
234 max77843_haptic_disable(haptic);
235
236 error = regulator_disable(haptic->motor_reg);
237 if (error)
238 dev_err(haptic->dev,
239 "failed to disable regulator: %d\n", error);
240
241 max77843_haptic_bias(haptic, false);
242}
243
244static int max77843_haptic_probe(struct platform_device *pdev)
245{
246 struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent);
247 struct max77843_haptic *haptic;
248 int error;
249
250 haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);
251 if (!haptic)
252 return -ENOMEM;
253
254 haptic->regmap_haptic = max77843->regmap;
255 haptic->dev = &pdev->dev;
256 haptic->type = MAX77843_HAPTIC_LRA;
257 haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128;
258
259 INIT_WORK(&haptic->work, max77843_haptic_play_work);
260 mutex_init(&haptic->mutex);
261
262 haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL);
263 if (IS_ERR(haptic->pwm_dev)) {
264 dev_err(&pdev->dev, "failed to get pwm device\n");
265 return PTR_ERR(haptic->pwm_dev);
266 }
267
268 haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic");
269 if (IS_ERR(haptic->motor_reg)) {
270 dev_err(&pdev->dev, "failed to get regulator\n");
271 return PTR_ERR(haptic->motor_reg);
272 }
273
274 haptic->input_dev = devm_input_allocate_device(&pdev->dev);
275 if (!haptic->input_dev) {
276 dev_err(&pdev->dev, "failed to allocate input device\n");
277 return -ENOMEM;
278 }
279
280 haptic->input_dev->name = "max77843-haptic";
281 haptic->input_dev->id.version = 1;
282 haptic->input_dev->dev.parent = &pdev->dev;
283 haptic->input_dev->open = max77843_haptic_open;
284 haptic->input_dev->close = max77843_haptic_close;
285 input_set_drvdata(haptic->input_dev, haptic);
286 input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE);
287
288 error = input_ff_create_memless(haptic->input_dev, NULL,
289 max77843_haptic_play_effect);
290 if (error) {
291 dev_err(&pdev->dev, "failed to create force-feedback\n");
292 return error;
293 }
294
295 error = input_register_device(haptic->input_dev);
296 if (error) {
297 dev_err(&pdev->dev, "failed to register input device\n");
298 return error;
299 }
300
301 platform_set_drvdata(pdev, haptic);
302
303 return 0;
304}
305
306static int __maybe_unused max77843_haptic_suspend(struct device *dev)
307{
308 struct platform_device *pdev = to_platform_device(dev);
309 struct max77843_haptic *haptic = platform_get_drvdata(pdev);
310 int error;
311
312 error = mutex_lock_interruptible(&haptic->mutex);
313 if (error)
314 return error;
315
316 max77843_haptic_disable(haptic);
317
318 haptic->suspended = true;
319
320 mutex_unlock(&haptic->mutex);
321
322 return 0;
323}
324
325static int __maybe_unused max77843_haptic_resume(struct device *dev)
326{
327 struct platform_device *pdev = to_platform_device(dev);
328 struct max77843_haptic *haptic = platform_get_drvdata(pdev);
329 unsigned int magnitude;
330
331 mutex_lock(&haptic->mutex);
332
333 haptic->suspended = false;
334
335 magnitude = ACCESS_ONCE(haptic->magnitude);
336 if (magnitude)
337 max77843_haptic_enable(haptic);
338
339 mutex_unlock(&haptic->mutex);
340
341 return 0;
342}
343
344static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops,
345 max77843_haptic_suspend, max77843_haptic_resume);
346
347static struct platform_driver max77843_haptic_driver = {
348 .driver = {
349 .name = "max77843-haptic",
350 .pm = &max77843_haptic_pm_ops,
351 },
352 .probe = max77843_haptic_probe,
353};
354module_platform_driver(max77843_haptic_driver);
355
356MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
357MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver");
358MODULE_LICENSE("GPL");