]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/blame - drivers/input/misc/msm-vibrator.c
Merge branch 'work.vboxsf' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[mirror_ubuntu-hirsute-kernel.git] / drivers / input / misc / msm-vibrator.c
CommitLineData
0f681d09
BM
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Qualcomm MSM vibrator driver
4 *
5 * Copyright (c) 2018 Brian Masney <masneyb@onstation.org>
6 *
7 * Based on qcom,pwm-vibrator.c from:
8 * Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca>
9 *
10 * Based on msm_pwm_vibrator.c from downstream Android sources:
11 * Copyright (C) 2009-2014 LGE, Inc.
12 */
13
14#include <linux/clk.h>
15#include <linux/err.h>
741c73ed 16#include <linux/gpio/consumer.h>
0f681d09
BM
17#include <linux/input.h>
18#include <linux/io.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/platform_device.h>
22#include <linux/regulator/consumer.h>
23
24#define REG_CMD_RCGR 0x00
25#define REG_CFG_RCGR 0x04
26#define REG_M 0x08
27#define REG_N 0x0C
28#define REG_D 0x10
29#define REG_CBCR 0x24
30#define MMSS_CC_M_DEFAULT 1
31
32struct msm_vibrator {
33 struct input_dev *input;
34 struct mutex mutex;
35 struct work_struct worker;
36 void __iomem *base;
37 struct regulator *vcc;
38 struct clk *clk;
39 struct gpio_desc *enable_gpio;
40 u16 magnitude;
41 bool enabled;
42};
43
44static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset,
45 u32 value)
46{
47 writel(value, vibrator->base + offset);
48}
49
50static int msm_vibrator_start(struct msm_vibrator *vibrator)
51{
52 int d_reg_val, ret = 0;
53
54 mutex_lock(&vibrator->mutex);
55
56 if (!vibrator->enabled) {
57 ret = clk_set_rate(vibrator->clk, 24000);
58 if (ret) {
59 dev_err(&vibrator->input->dev,
60 "Failed to set clock rate: %d\n", ret);
61 goto unlock;
62 }
63
64 ret = clk_prepare_enable(vibrator->clk);
65 if (ret) {
66 dev_err(&vibrator->input->dev,
67 "Failed to enable clock: %d\n", ret);
68 goto unlock;
69 }
70
71 ret = regulator_enable(vibrator->vcc);
72 if (ret) {
73 dev_err(&vibrator->input->dev,
74 "Failed to enable regulator: %d\n", ret);
75 clk_disable(vibrator->clk);
76 goto unlock;
77 }
78
79 gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
80
81 vibrator->enabled = true;
82 }
83
84 d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff);
85 msm_vibrator_write(vibrator, REG_CFG_RCGR,
86 (2 << 12) | /* dual edge mode */
87 (0 << 8) | /* cxo */
88 (7 << 0));
89 msm_vibrator_write(vibrator, REG_M, 1);
90 msm_vibrator_write(vibrator, REG_N, 128);
91 msm_vibrator_write(vibrator, REG_D, d_reg_val);
92 msm_vibrator_write(vibrator, REG_CMD_RCGR, 1);
93 msm_vibrator_write(vibrator, REG_CBCR, 1);
94
95unlock:
96 mutex_unlock(&vibrator->mutex);
97
98 return ret;
99}
100
101static void msm_vibrator_stop(struct msm_vibrator *vibrator)
102{
103 mutex_lock(&vibrator->mutex);
104
105 if (vibrator->enabled) {
106 gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
107 regulator_disable(vibrator->vcc);
108 clk_disable(vibrator->clk);
109 vibrator->enabled = false;
110 }
111
112 mutex_unlock(&vibrator->mutex);
113}
114
115static void msm_vibrator_worker(struct work_struct *work)
116{
117 struct msm_vibrator *vibrator = container_of(work,
118 struct msm_vibrator,
119 worker);
120
121 if (vibrator->magnitude)
122 msm_vibrator_start(vibrator);
123 else
124 msm_vibrator_stop(vibrator);
125}
126
127static int msm_vibrator_play_effect(struct input_dev *dev, void *data,
128 struct ff_effect *effect)
129{
130 struct msm_vibrator *vibrator = input_get_drvdata(dev);
131
132 mutex_lock(&vibrator->mutex);
133
134 if (effect->u.rumble.strong_magnitude > 0)
135 vibrator->magnitude = effect->u.rumble.strong_magnitude;
136 else
137 vibrator->magnitude = effect->u.rumble.weak_magnitude;
138
139 mutex_unlock(&vibrator->mutex);
140
141 schedule_work(&vibrator->worker);
142
143 return 0;
144}
145
146static void msm_vibrator_close(struct input_dev *input)
147{
148 struct msm_vibrator *vibrator = input_get_drvdata(input);
149
150 cancel_work_sync(&vibrator->worker);
151 msm_vibrator_stop(vibrator);
152}
153
154static int msm_vibrator_probe(struct platform_device *pdev)
155{
156 struct msm_vibrator *vibrator;
157 struct resource *res;
158 int ret;
159
160 vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
161 if (!vibrator)
162 return -ENOMEM;
163
164 vibrator->input = devm_input_allocate_device(&pdev->dev);
165 if (!vibrator->input)
166 return -ENOMEM;
167
168 vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
169 if (IS_ERR(vibrator->vcc)) {
170 if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER)
171 dev_err(&pdev->dev, "Failed to get regulator: %ld\n",
172 PTR_ERR(vibrator->vcc));
173 return PTR_ERR(vibrator->vcc);
174 }
175
176 vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable",
177 GPIOD_OUT_LOW);
178 if (IS_ERR(vibrator->enable_gpio)) {
179 if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER)
180 dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n",
181 PTR_ERR(vibrator->enable_gpio));
182 return PTR_ERR(vibrator->enable_gpio);
183 }
184
185 vibrator->clk = devm_clk_get(&pdev->dev, "pwm");
186 if (IS_ERR(vibrator->clk)) {
187 if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER)
188 dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n",
189 PTR_ERR(vibrator->clk));
190 return PTR_ERR(vibrator->clk);
191 }
192
193 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
194 if (!res) {
195 dev_err(&pdev->dev, "Failed to get platform resource\n");
196 return -ENODEV;
197 }
198
199 vibrator->base = devm_ioremap(&pdev->dev, res->start,
200 resource_size(res));
201 if (!vibrator->base) {
320f07b4 202 dev_err(&pdev->dev, "Failed to iomap resource.\n");
0f681d09
BM
203 return -ENOMEM;
204 }
205
206 vibrator->enabled = false;
207 mutex_init(&vibrator->mutex);
208 INIT_WORK(&vibrator->worker, msm_vibrator_worker);
209
210 vibrator->input->name = "msm-vibrator";
211 vibrator->input->id.bustype = BUS_HOST;
212 vibrator->input->close = msm_vibrator_close;
213
214 input_set_drvdata(vibrator->input, vibrator);
215 input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
216
217 ret = input_ff_create_memless(vibrator->input, NULL,
218 msm_vibrator_play_effect);
219 if (ret) {
220 dev_err(&pdev->dev, "Failed to create ff memless: %d", ret);
221 return ret;
222 }
223
224 ret = input_register_device(vibrator->input);
225 if (ret) {
226 dev_err(&pdev->dev, "Failed to register input device: %d", ret);
227 return ret;
228 }
229
230 platform_set_drvdata(pdev, vibrator);
231
232 return 0;
233}
234
235static int __maybe_unused msm_vibrator_suspend(struct device *dev)
236{
237 struct platform_device *pdev = to_platform_device(dev);
238 struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
239
240 cancel_work_sync(&vibrator->worker);
241
242 if (vibrator->enabled)
243 msm_vibrator_stop(vibrator);
244
245 return 0;
246}
247
248static int __maybe_unused msm_vibrator_resume(struct device *dev)
249{
250 struct platform_device *pdev = to_platform_device(dev);
251 struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
252
253 if (vibrator->enabled)
254 msm_vibrator_start(vibrator);
255
256 return 0;
257}
258
259static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend,
260 msm_vibrator_resume);
261
262static const struct of_device_id msm_vibrator_of_match[] = {
263 { .compatible = "qcom,msm8226-vibrator" },
264 { .compatible = "qcom,msm8974-vibrator" },
265 {},
266};
267MODULE_DEVICE_TABLE(of, msm_vibrator_of_match);
268
269static struct platform_driver msm_vibrator_driver = {
270 .probe = msm_vibrator_probe,
271 .driver = {
272 .name = "msm-vibrator",
273 .pm = &msm_vibrator_pm_ops,
274 .of_match_table = of_match_ptr(msm_vibrator_of_match),
275 },
276};
277module_platform_driver(msm_vibrator_driver);
278
279MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
280MODULE_DESCRIPTION("Qualcomm MSM vibrator driver");
281MODULE_LICENSE("GPL");