aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Murphy <dmurphy@ti.com>2015-05-08 19:02:43 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-05-08 19:45:16 -0400
commit4d10da13467e223441d3b081eb70e91149ea5da9 (patch)
tree76c9e8478df137aa032b59151883dcfaadb4ed0d
parent8a4dda79a383ed9b09aa3af3ef74941bec2ee042 (diff)
Input: add TI drv2665 haptics driver
Add the TI drv2665 piezo haptic driver. This haptics IC requires the data to be streamed to the FIFO for continuous output. Datasheet can be found at: http://www.ti.com/product/drv2665 Signed-off-by: Dan Murphy <dmurphy@ti.com> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
-rw-r--r--Documentation/devicetree/bindings/input/ti,drv2665.txt17
-rw-r--r--drivers/input/misc/Kconfig11
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/drv2665.c322
4 files changed, 351 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/input/ti,drv2665.txt b/Documentation/devicetree/bindings/input/ti,drv2665.txt
new file mode 100644
index 000000000000..1ba97ac04305
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/ti,drv2665.txt
@@ -0,0 +1,17 @@
1* Texas Instruments - drv2665 Haptics driver
2
3Required properties:
4 - compatible - "ti,drv2665" - DRV2665
5 - reg - I2C slave address
6 - vbat-supply - Required supply regulator
7
8Example:
9
10haptics: haptics@59 {
11 compatible = "ti,drv2665";
12 reg = <0x59>;
13 vbat-supply = <&vbat>;
14};
15
16For more product information please see the link below:
17http://www.ti.com/product/drv2665
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7838f1a06856..e5c4de279001 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -775,6 +775,17 @@ config INPUT_DRV260X_HAPTICS
775 To compile this driver as a module, choose M here: the 775 To compile this driver as a module, choose M here: the
776 module will be called drv260x-haptics. 776 module will be called drv260x-haptics.
777 777
778config INPUT_DRV2665_HAPTICS
779 tristate "TI DRV2665 haptics support"
780 depends on INPUT && I2C
781 select INPUT_FF_MEMLESS
782 select REGMAP_I2C
783 help
784 Say Y to enable support for the TI DRV2665 haptics driver.
785
786 To compile this driver as a module, choose M here: the
787 module will be called drv2665-haptics.
788
778config INPUT_DRV2667_HAPTICS 789config INPUT_DRV2667_HAPTICS
779 tristate "TI DRV2667 haptics support" 790 tristate "TI DRV2667 haptics support"
780 depends on INPUT && I2C 791 depends on INPUT && I2C
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 78ba4c1b8532..d3179472474d 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
28obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o 28obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
29obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o 29obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
30obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o 30obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
31obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o
31obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o 32obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
32obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o 33obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
33obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o 34obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
diff --git a/drivers/input/misc/drv2665.c b/drivers/input/misc/drv2665.c
new file mode 100644
index 000000000000..0afaa33de07d
--- /dev/null
+++ b/drivers/input/misc/drv2665.c
@@ -0,0 +1,322 @@
1/*
2 * DRV2665 haptics driver family
3 *
4 * Author: Dan Murphy <dmurphy@ti.com>
5 *
6 * Copyright: (C) 2015 Texas Instruments, Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 */
17
18#include <linux/i2c.h>
19#include <linux/input.h>
20#include <linux/module.h>
21#include <linux/regmap.h>
22#include <linux/slab.h>
23#include <linux/delay.h>
24#include <linux/regulator/consumer.h>
25
26/* Contol registers */
27#define DRV2665_STATUS 0x00
28#define DRV2665_CTRL_1 0x01
29#define DRV2665_CTRL_2 0x02
30#define DRV2665_FIFO 0x0b
31
32/* Status Register */
33#define DRV2665_FIFO_FULL BIT(0)
34#define DRV2665_FIFO_EMPTY BIT(1)
35
36/* Control 1 Register */
37#define DRV2665_25_VPP_GAIN 0x00
38#define DRV2665_50_VPP_GAIN 0x01
39#define DRV2665_75_VPP_GAIN 0x02
40#define DRV2665_100_VPP_GAIN 0x03
41#define DRV2665_DIGITAL_IN 0xfc
42#define DRV2665_ANALOG_IN BIT(2)
43
44/* Control 2 Register */
45#define DRV2665_BOOST_EN BIT(1)
46#define DRV2665_STANDBY BIT(6)
47#define DRV2665_DEV_RST BIT(7)
48#define DRV2665_5_MS_IDLE_TOUT 0x00
49#define DRV2665_10_MS_IDLE_TOUT 0x04
50#define DRV2665_15_MS_IDLE_TOUT 0x08
51#define DRV2665_20_MS_IDLE_TOUT 0x0c
52
53/**
54 * struct drv2665_data -
55 * @input_dev - Pointer to the input device
56 * @client - Pointer to the I2C client
57 * @regmap - Register map of the device
58 * @work - Work item used to off load the enable/disable of the vibration
59 * @regulator - Pointer to the regulator for the IC
60 */
61struct drv2665_data {
62 struct input_dev *input_dev;
63 struct i2c_client *client;
64 struct regmap *regmap;
65 struct work_struct work;
66 struct regulator *regulator;
67};
68
69/* 8kHz Sine wave to stream to the FIFO */
70static const u8 drv2665_sine_wave_form[] = {
71 0x00, 0x10, 0x20, 0x2e, 0x3c, 0x48, 0x53, 0x5b, 0x61, 0x65, 0x66,
72 0x65, 0x61, 0x5b, 0x53, 0x48, 0x3c, 0x2e, 0x20, 0x10,
73 0x00, 0xf0, 0xe0, 0xd2, 0xc4, 0xb8, 0xad, 0xa5, 0x9f, 0x9b, 0x9a,
74 0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00,
75};
76
77static struct reg_default drv2665_reg_defs[] = {
78 { DRV2665_STATUS, 0x02 },
79 { DRV2665_CTRL_1, 0x28 },
80 { DRV2665_CTRL_2, 0x40 },
81 { DRV2665_FIFO, 0x00 },
82};
83
84static void drv2665_worker(struct work_struct *work)
85{
86 struct drv2665_data *haptics =
87 container_of(work, struct drv2665_data, work);
88 unsigned int read_buf;
89 int error;
90
91 error = regmap_read(haptics->regmap, DRV2665_STATUS, &read_buf);
92 if (error) {
93 dev_err(&haptics->client->dev,
94 "Failed to read status: %d\n", error);
95 return;
96 }
97
98 if (read_buf & DRV2665_FIFO_EMPTY) {
99 error = regmap_bulk_write(haptics->regmap,
100 DRV2665_FIFO,
101 drv2665_sine_wave_form,
102 ARRAY_SIZE(drv2665_sine_wave_form));
103 if (error) {
104 dev_err(&haptics->client->dev,
105 "Failed to write FIFO: %d\n", error);
106 return;
107 }
108 }
109}
110
111static int drv2665_haptics_play(struct input_dev *input, void *data,
112 struct ff_effect *effect)
113{
114 struct drv2665_data *haptics = input_get_drvdata(input);
115
116 schedule_work(&haptics->work);
117
118 return 0;
119}
120
121static void drv2665_close(struct input_dev *input)
122{
123 struct drv2665_data *haptics = input_get_drvdata(input);
124 int error;
125
126 cancel_work_sync(&haptics->work);
127
128 error = regmap_update_bits(haptics->regmap,
129 DRV2665_CTRL_2, DRV2665_STANDBY, 1);
130 if (error)
131 dev_err(&haptics->client->dev,
132 "Failed to enter standby mode: %d\n", error);
133}
134
135static const struct reg_default drv2665_init_regs[] = {
136 { DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT },
137 { DRV2665_CTRL_1, DRV2665_25_VPP_GAIN },
138};
139
140static int drv2665_init(struct drv2665_data *haptics)
141{
142 int error;
143
144 error = regmap_register_patch(haptics->regmap,
145 drv2665_init_regs,
146 ARRAY_SIZE(drv2665_init_regs));
147 if (error) {
148 dev_err(&haptics->client->dev,
149 "Failed to write init registers: %d\n",
150 error);
151 return error;
152 }
153
154 return 0;
155}
156
157static const struct regmap_config drv2665_regmap_config = {
158 .reg_bits = 8,
159 .val_bits = 8,
160
161 .max_register = DRV2665_FIFO,
162 .reg_defaults = drv2665_reg_defs,
163 .num_reg_defaults = ARRAY_SIZE(drv2665_reg_defs),
164 .cache_type = REGCACHE_NONE,
165};
166
167static int drv2665_probe(struct i2c_client *client,
168 const struct i2c_device_id *id)
169{
170 struct drv2665_data *haptics;
171 int error;
172
173 haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
174 if (!haptics)
175 return -ENOMEM;
176
177 haptics->regulator = devm_regulator_get(&client->dev, "vbat");
178 if (IS_ERR(haptics->regulator)) {
179 error = PTR_ERR(haptics->regulator);
180 dev_err(&client->dev,
181 "unable to get regulator, error: %d\n", error);
182 return error;
183 }
184
185 haptics->input_dev = devm_input_allocate_device(&client->dev);
186 if (!haptics->input_dev) {
187 dev_err(&client->dev, "Failed to allocate input device\n");
188 return -ENOMEM;
189 }
190
191 haptics->input_dev->name = "drv2665:haptics";
192 haptics->input_dev->dev.parent = client->dev.parent;
193 haptics->input_dev->close = drv2665_close;
194 input_set_drvdata(haptics->input_dev, haptics);
195 input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
196
197 error = input_ff_create_memless(haptics->input_dev, NULL,
198 drv2665_haptics_play);
199 if (error) {
200 dev_err(&client->dev, "input_ff_create() failed: %d\n",
201 error);
202 return error;
203 }
204
205 INIT_WORK(&haptics->work, drv2665_worker);
206
207 haptics->client = client;
208 i2c_set_clientdata(client, haptics);
209
210 haptics->regmap = devm_regmap_init_i2c(client, &drv2665_regmap_config);
211 if (IS_ERR(haptics->regmap)) {
212 error = PTR_ERR(haptics->regmap);
213 dev_err(&client->dev, "Failed to allocate register map: %d\n",
214 error);
215 return error;
216 }
217
218 error = drv2665_init(haptics);
219 if (error) {
220 dev_err(&client->dev, "Device init failed: %d\n", error);
221 return error;
222 }
223
224 error = input_register_device(haptics->input_dev);
225 if (error) {
226 dev_err(&client->dev, "couldn't register input device: %d\n",
227 error);
228 return error;
229 }
230
231 return 0;
232}
233
234static int __maybe_unused drv2665_suspend(struct device *dev)
235{
236 struct drv2665_data *haptics = dev_get_drvdata(dev);
237 int ret = 0;
238
239 mutex_lock(&haptics->input_dev->mutex);
240
241 if (haptics->input_dev->users) {
242 ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
243 DRV2665_STANDBY, 1);
244 if (ret) {
245 dev_err(dev, "Failed to set standby mode\n");
246 regulator_disable(haptics->regulator);
247 goto out;
248 }
249
250 ret = regulator_disable(haptics->regulator);
251 if (ret) {
252 dev_err(dev, "Failed to disable regulator\n");
253 regmap_update_bits(haptics->regmap,
254 DRV2665_CTRL_2,
255 DRV2665_STANDBY, 0);
256 }
257 }
258out:
259 mutex_unlock(&haptics->input_dev->mutex);
260 return ret;
261}
262
263static int __maybe_unused drv2665_resume(struct device *dev)
264{
265 struct drv2665_data *haptics = dev_get_drvdata(dev);
266 int ret = 0;
267
268 mutex_lock(&haptics->input_dev->mutex);
269
270 if (haptics->input_dev->users) {
271 ret = regulator_enable(haptics->regulator);
272 if (ret) {
273 dev_err(dev, "Failed to enable regulator\n");
274 goto out;
275 }
276
277 ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
278 DRV2665_STANDBY, 0);
279 if (ret) {
280 dev_err(dev, "Failed to unset standby mode\n");
281 regulator_disable(haptics->regulator);
282 goto out;
283 }
284
285 }
286
287out:
288 mutex_unlock(&haptics->input_dev->mutex);
289 return ret;
290}
291
292static SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume);
293
294static const struct i2c_device_id drv2665_id[] = {
295 { "drv2665", 0 },
296 { }
297};
298MODULE_DEVICE_TABLE(i2c, drv2665_id);
299
300#ifdef CONFIG_OF
301static const struct of_device_id drv2665_of_match[] = {
302 { .compatible = "ti,drv2665", },
303 { }
304};
305MODULE_DEVICE_TABLE(of, drv2665_of_match);
306#endif
307
308static struct i2c_driver drv2665_driver = {
309 .probe = drv2665_probe,
310 .driver = {
311 .name = "drv2665-haptics",
312 .owner = THIS_MODULE,
313 .of_match_table = of_match_ptr(drv2665_of_match),
314 .pm = &drv2665_pm_ops,
315 },
316 .id_table = drv2665_id,
317};
318module_i2c_driver(drv2665_driver);
319
320MODULE_DESCRIPTION("TI DRV2665 haptics driver");
321MODULE_LICENSE("GPL");
322MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");