1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Regulator haptic driver |
4 | * |
5 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. |
6 | * Author: Jaewon Kim <jaewon02.kim@samsung.com> |
7 | * Author: Hyunhee Kim <hyunhee.kim@samsung.com> |
8 | */ |
9 | |
10 | #include <linux/input.h> |
11 | #include <linux/module.h> |
12 | #include <linux/of.h> |
13 | #include <linux/platform_data/regulator-haptic.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/regulator/consumer.h> |
16 | #include <linux/slab.h> |
17 | |
18 | #define MAX_MAGNITUDE_SHIFT 16 |
19 | |
20 | struct regulator_haptic { |
21 | struct device *dev; |
22 | struct input_dev *input_dev; |
23 | struct regulator *regulator; |
24 | |
25 | struct work_struct work; |
26 | struct mutex mutex; |
27 | |
28 | bool active; |
29 | bool suspended; |
30 | |
31 | unsigned int max_volt; |
32 | unsigned int min_volt; |
33 | unsigned int magnitude; |
34 | }; |
35 | |
36 | static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on) |
37 | { |
38 | int error; |
39 | |
40 | if (haptic->active != on) { |
41 | |
42 | error = on ? regulator_enable(regulator: haptic->regulator) : |
43 | regulator_disable(regulator: haptic->regulator); |
44 | if (error) { |
45 | dev_err(haptic->dev, |
46 | "failed to switch regulator %s: %d\n" , |
47 | on ? "on" : "off" , error); |
48 | return error; |
49 | } |
50 | |
51 | haptic->active = on; |
52 | } |
53 | |
54 | return 0; |
55 | } |
56 | |
57 | static int regulator_haptic_set_voltage(struct regulator_haptic *haptic, |
58 | unsigned int magnitude) |
59 | { |
60 | u64 volt_mag_multi; |
61 | unsigned int intensity; |
62 | int error; |
63 | |
64 | volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude; |
65 | intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT); |
66 | |
67 | error = regulator_set_voltage(regulator: haptic->regulator, |
68 | min_uV: intensity + haptic->min_volt, |
69 | max_uV: haptic->max_volt); |
70 | if (error) { |
71 | dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n" , |
72 | intensity + haptic->min_volt, error); |
73 | return error; |
74 | } |
75 | |
76 | regulator_haptic_toggle(haptic, on: !!magnitude); |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static void regulator_haptic_work(struct work_struct *work) |
82 | { |
83 | struct regulator_haptic *haptic = container_of(work, |
84 | struct regulator_haptic, work); |
85 | |
86 | mutex_lock(&haptic->mutex); |
87 | |
88 | if (!haptic->suspended) |
89 | regulator_haptic_set_voltage(haptic, magnitude: haptic->magnitude); |
90 | |
91 | mutex_unlock(lock: &haptic->mutex); |
92 | } |
93 | |
94 | static int regulator_haptic_play_effect(struct input_dev *input, void *data, |
95 | struct ff_effect *effect) |
96 | { |
97 | struct regulator_haptic *haptic = input_get_drvdata(dev: input); |
98 | |
99 | haptic->magnitude = effect->u.rumble.strong_magnitude; |
100 | if (!haptic->magnitude) |
101 | haptic->magnitude = effect->u.rumble.weak_magnitude; |
102 | |
103 | schedule_work(work: &haptic->work); |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static void regulator_haptic_close(struct input_dev *input) |
109 | { |
110 | struct regulator_haptic *haptic = input_get_drvdata(dev: input); |
111 | |
112 | cancel_work_sync(work: &haptic->work); |
113 | regulator_haptic_set_voltage(haptic, magnitude: 0); |
114 | } |
115 | |
116 | static int __maybe_unused |
117 | regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic) |
118 | { |
119 | struct device_node *node; |
120 | int error; |
121 | |
122 | node = dev->of_node; |
123 | if(!node) { |
124 | dev_err(dev, "Missing device tree data\n" ); |
125 | return -EINVAL; |
126 | } |
127 | |
128 | error = of_property_read_u32(np: node, propname: "max-microvolt" , out_value: &haptic->max_volt); |
129 | if (error) { |
130 | dev_err(dev, "cannot parse max-microvolt\n" ); |
131 | return error; |
132 | } |
133 | |
134 | error = of_property_read_u32(np: node, propname: "min-microvolt" , out_value: &haptic->min_volt); |
135 | if (error) { |
136 | dev_err(dev, "cannot parse min-microvolt\n" ); |
137 | return error; |
138 | } |
139 | |
140 | return 0; |
141 | } |
142 | |
143 | static int regulator_haptic_probe(struct platform_device *pdev) |
144 | { |
145 | const struct regulator_haptic_data *pdata = dev_get_platdata(dev: &pdev->dev); |
146 | struct regulator_haptic *haptic; |
147 | struct input_dev *input_dev; |
148 | int error; |
149 | |
150 | haptic = devm_kzalloc(dev: &pdev->dev, size: sizeof(*haptic), GFP_KERNEL); |
151 | if (!haptic) |
152 | return -ENOMEM; |
153 | |
154 | platform_set_drvdata(pdev, data: haptic); |
155 | haptic->dev = &pdev->dev; |
156 | mutex_init(&haptic->mutex); |
157 | INIT_WORK(&haptic->work, regulator_haptic_work); |
158 | |
159 | if (pdata) { |
160 | haptic->max_volt = pdata->max_volt; |
161 | haptic->min_volt = pdata->min_volt; |
162 | } else if (IS_ENABLED(CONFIG_OF)) { |
163 | error = regulator_haptic_parse_dt(dev: &pdev->dev, haptic); |
164 | if (error) |
165 | return error; |
166 | } else { |
167 | dev_err(&pdev->dev, "Missing platform data\n" ); |
168 | return -EINVAL; |
169 | } |
170 | |
171 | haptic->regulator = devm_regulator_get_exclusive(dev: &pdev->dev, id: "haptic" ); |
172 | if (IS_ERR(ptr: haptic->regulator)) { |
173 | dev_err(&pdev->dev, "failed to get regulator\n" ); |
174 | return PTR_ERR(ptr: haptic->regulator); |
175 | } |
176 | |
177 | input_dev = devm_input_allocate_device(&pdev->dev); |
178 | if (!input_dev) |
179 | return -ENOMEM; |
180 | |
181 | haptic->input_dev = input_dev; |
182 | haptic->input_dev->name = "regulator-haptic" ; |
183 | haptic->input_dev->dev.parent = &pdev->dev; |
184 | haptic->input_dev->close = regulator_haptic_close; |
185 | input_set_drvdata(dev: haptic->input_dev, data: haptic); |
186 | input_set_capability(dev: haptic->input_dev, EV_FF, FF_RUMBLE); |
187 | |
188 | error = input_ff_create_memless(dev: input_dev, NULL, |
189 | play_effect: regulator_haptic_play_effect); |
190 | if (error) { |
191 | dev_err(&pdev->dev, "failed to create force-feedback\n" ); |
192 | return error; |
193 | } |
194 | |
195 | error = input_register_device(haptic->input_dev); |
196 | if (error) { |
197 | dev_err(&pdev->dev, "failed to register input device\n" ); |
198 | return error; |
199 | } |
200 | |
201 | return 0; |
202 | } |
203 | |
204 | static int regulator_haptic_suspend(struct device *dev) |
205 | { |
206 | struct platform_device *pdev = to_platform_device(dev); |
207 | struct regulator_haptic *haptic = platform_get_drvdata(pdev); |
208 | int error; |
209 | |
210 | error = mutex_lock_interruptible(&haptic->mutex); |
211 | if (error) |
212 | return error; |
213 | |
214 | regulator_haptic_set_voltage(haptic, magnitude: 0); |
215 | |
216 | haptic->suspended = true; |
217 | |
218 | mutex_unlock(lock: &haptic->mutex); |
219 | |
220 | return 0; |
221 | } |
222 | |
223 | static int regulator_haptic_resume(struct device *dev) |
224 | { |
225 | struct platform_device *pdev = to_platform_device(dev); |
226 | struct regulator_haptic *haptic = platform_get_drvdata(pdev); |
227 | unsigned int magnitude; |
228 | |
229 | mutex_lock(&haptic->mutex); |
230 | |
231 | haptic->suspended = false; |
232 | |
233 | magnitude = READ_ONCE(haptic->magnitude); |
234 | if (magnitude) |
235 | regulator_haptic_set_voltage(haptic, magnitude); |
236 | |
237 | mutex_unlock(lock: &haptic->mutex); |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static DEFINE_SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, |
243 | regulator_haptic_suspend, regulator_haptic_resume); |
244 | |
245 | static const struct of_device_id regulator_haptic_dt_match[] = { |
246 | { .compatible = "regulator-haptic" }, |
247 | { /* sentinel */ }, |
248 | }; |
249 | MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match); |
250 | |
251 | static struct platform_driver regulator_haptic_driver = { |
252 | .probe = regulator_haptic_probe, |
253 | .driver = { |
254 | .name = "regulator-haptic" , |
255 | .of_match_table = regulator_haptic_dt_match, |
256 | .pm = pm_sleep_ptr(®ulator_haptic_pm_ops), |
257 | }, |
258 | }; |
259 | module_platform_driver(regulator_haptic_driver); |
260 | |
261 | MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>" ); |
262 | MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>" ); |
263 | MODULE_DESCRIPTION("Regulator haptic driver" ); |
264 | MODULE_LICENSE("GPL" ); |
265 | |