xref: /openbmc/linux/drivers/input/misc/drv2665.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
24d10da13SDan Murphy /*
34d10da13SDan Murphy  * DRV2665 haptics driver family
44d10da13SDan Murphy  *
54d10da13SDan Murphy  * Author: Dan Murphy <dmurphy@ti.com>
64d10da13SDan Murphy  *
74d10da13SDan Murphy  * Copyright: (C) 2015 Texas Instruments, Inc.
84d10da13SDan Murphy  */
94d10da13SDan Murphy 
104d10da13SDan Murphy #include <linux/i2c.h>
114d10da13SDan Murphy #include <linux/input.h>
124d10da13SDan Murphy #include <linux/module.h>
134d10da13SDan Murphy #include <linux/regmap.h>
144d10da13SDan Murphy #include <linux/slab.h>
154d10da13SDan Murphy #include <linux/delay.h>
164d10da13SDan Murphy #include <linux/regulator/consumer.h>
174d10da13SDan Murphy 
184d10da13SDan Murphy /* Contol registers */
194d10da13SDan Murphy #define DRV2665_STATUS	0x00
204d10da13SDan Murphy #define DRV2665_CTRL_1	0x01
214d10da13SDan Murphy #define DRV2665_CTRL_2	0x02
224d10da13SDan Murphy #define DRV2665_FIFO	0x0b
234d10da13SDan Murphy 
244d10da13SDan Murphy /* Status Register */
254d10da13SDan Murphy #define DRV2665_FIFO_FULL		BIT(0)
264d10da13SDan Murphy #define DRV2665_FIFO_EMPTY		BIT(1)
274d10da13SDan Murphy 
284d10da13SDan Murphy /* Control 1 Register */
294d10da13SDan Murphy #define DRV2665_25_VPP_GAIN		0x00
304d10da13SDan Murphy #define DRV2665_50_VPP_GAIN		0x01
314d10da13SDan Murphy #define DRV2665_75_VPP_GAIN		0x02
324d10da13SDan Murphy #define DRV2665_100_VPP_GAIN		0x03
334d10da13SDan Murphy #define DRV2665_DIGITAL_IN		0xfc
344d10da13SDan Murphy #define DRV2665_ANALOG_IN		BIT(2)
354d10da13SDan Murphy 
364d10da13SDan Murphy /* Control 2 Register */
374d10da13SDan Murphy #define DRV2665_BOOST_EN		BIT(1)
384d10da13SDan Murphy #define DRV2665_STANDBY			BIT(6)
394d10da13SDan Murphy #define DRV2665_DEV_RST			BIT(7)
404d10da13SDan Murphy #define DRV2665_5_MS_IDLE_TOUT		0x00
414d10da13SDan Murphy #define DRV2665_10_MS_IDLE_TOUT		0x04
424d10da13SDan Murphy #define DRV2665_15_MS_IDLE_TOUT		0x08
434d10da13SDan Murphy #define DRV2665_20_MS_IDLE_TOUT		0x0c
444d10da13SDan Murphy 
454d10da13SDan Murphy /**
464d10da13SDan Murphy  * struct drv2665_data -
476e9c6fcbSLee Jones  * @input_dev: Pointer to the input device
486e9c6fcbSLee Jones  * @client: Pointer to the I2C client
496e9c6fcbSLee Jones  * @regmap: Register map of the device
506e9c6fcbSLee Jones  * @work: Work item used to off load the enable/disable of the vibration
516e9c6fcbSLee Jones  * @regulator: Pointer to the regulator for the IC
524d10da13SDan Murphy  */
534d10da13SDan Murphy struct drv2665_data {
544d10da13SDan Murphy 	struct input_dev *input_dev;
554d10da13SDan Murphy 	struct i2c_client *client;
564d10da13SDan Murphy 	struct regmap *regmap;
574d10da13SDan Murphy 	struct work_struct work;
584d10da13SDan Murphy 	struct regulator *regulator;
594d10da13SDan Murphy };
604d10da13SDan Murphy 
614d10da13SDan Murphy /* 8kHz Sine wave to stream to the FIFO */
624d10da13SDan Murphy static const u8 drv2665_sine_wave_form[] = {
634d10da13SDan Murphy 	0x00, 0x10, 0x20, 0x2e, 0x3c, 0x48, 0x53, 0x5b, 0x61, 0x65, 0x66,
644d10da13SDan Murphy 	0x65, 0x61, 0x5b, 0x53, 0x48, 0x3c, 0x2e, 0x20, 0x10,
654d10da13SDan Murphy 	0x00, 0xf0, 0xe0, 0xd2, 0xc4, 0xb8, 0xad, 0xa5, 0x9f, 0x9b, 0x9a,
664d10da13SDan Murphy 	0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00,
674d10da13SDan Murphy };
684d10da13SDan Murphy 
69ec0843faSAxel Lin static const struct reg_default drv2665_reg_defs[] = {
704d10da13SDan Murphy 	{ DRV2665_STATUS, 0x02 },
714d10da13SDan Murphy 	{ DRV2665_CTRL_1, 0x28 },
724d10da13SDan Murphy 	{ DRV2665_CTRL_2, 0x40 },
734d10da13SDan Murphy 	{ DRV2665_FIFO, 0x00 },
744d10da13SDan Murphy };
754d10da13SDan Murphy 
drv2665_worker(struct work_struct * work)764d10da13SDan Murphy static void drv2665_worker(struct work_struct *work)
774d10da13SDan Murphy {
784d10da13SDan Murphy 	struct drv2665_data *haptics =
794d10da13SDan Murphy 				container_of(work, struct drv2665_data, work);
804d10da13SDan Murphy 	unsigned int read_buf;
814d10da13SDan Murphy 	int error;
824d10da13SDan Murphy 
834d10da13SDan Murphy 	error = regmap_read(haptics->regmap, DRV2665_STATUS, &read_buf);
844d10da13SDan Murphy 	if (error) {
854d10da13SDan Murphy 		dev_err(&haptics->client->dev,
864d10da13SDan Murphy 			"Failed to read status: %d\n", error);
874d10da13SDan Murphy 		return;
884d10da13SDan Murphy 	}
894d10da13SDan Murphy 
904d10da13SDan Murphy 	if (read_buf & DRV2665_FIFO_EMPTY) {
914d10da13SDan Murphy 		error = regmap_bulk_write(haptics->regmap,
924d10da13SDan Murphy 					  DRV2665_FIFO,
934d10da13SDan Murphy 					  drv2665_sine_wave_form,
944d10da13SDan Murphy 					  ARRAY_SIZE(drv2665_sine_wave_form));
954d10da13SDan Murphy 		if (error) {
964d10da13SDan Murphy 			dev_err(&haptics->client->dev,
974d10da13SDan Murphy 				"Failed to write FIFO: %d\n", error);
984d10da13SDan Murphy 			return;
994d10da13SDan Murphy 		}
1004d10da13SDan Murphy 	}
1014d10da13SDan Murphy }
1024d10da13SDan Murphy 
drv2665_haptics_play(struct input_dev * input,void * data,struct ff_effect * effect)1034d10da13SDan Murphy static int drv2665_haptics_play(struct input_dev *input, void *data,
1044d10da13SDan Murphy 				struct ff_effect *effect)
1054d10da13SDan Murphy {
1064d10da13SDan Murphy 	struct drv2665_data *haptics = input_get_drvdata(input);
1074d10da13SDan Murphy 
1084d10da13SDan Murphy 	schedule_work(&haptics->work);
1094d10da13SDan Murphy 
1104d10da13SDan Murphy 	return 0;
1114d10da13SDan Murphy }
1124d10da13SDan Murphy 
drv2665_close(struct input_dev * input)1134d10da13SDan Murphy static void drv2665_close(struct input_dev *input)
1144d10da13SDan Murphy {
1154d10da13SDan Murphy 	struct drv2665_data *haptics = input_get_drvdata(input);
1164d10da13SDan Murphy 	int error;
1174d10da13SDan Murphy 
1184d10da13SDan Murphy 	cancel_work_sync(&haptics->work);
1194d10da13SDan Murphy 
120037b3af6SFlorian Vaussard 	error = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
121037b3af6SFlorian Vaussard 				   DRV2665_STANDBY, DRV2665_STANDBY);
1224d10da13SDan Murphy 	if (error)
1234d10da13SDan Murphy 		dev_err(&haptics->client->dev,
1244d10da13SDan Murphy 			"Failed to enter standby mode: %d\n", error);
1254d10da13SDan Murphy }
1264d10da13SDan Murphy 
1278019ff6cSNariman Poushin static const struct reg_sequence drv2665_init_regs[] = {
1284d10da13SDan Murphy 	{ DRV2665_CTRL_2, 0 | DRV2665_10_MS_IDLE_TOUT },
1294d10da13SDan Murphy 	{ DRV2665_CTRL_1, DRV2665_25_VPP_GAIN },
1304d10da13SDan Murphy };
1314d10da13SDan Murphy 
drv2665_init(struct drv2665_data * haptics)1324d10da13SDan Murphy static int drv2665_init(struct drv2665_data *haptics)
1334d10da13SDan Murphy {
1344d10da13SDan Murphy 	int error;
1354d10da13SDan Murphy 
1364d10da13SDan Murphy 	error = regmap_register_patch(haptics->regmap,
1374d10da13SDan Murphy 				      drv2665_init_regs,
1384d10da13SDan Murphy 				      ARRAY_SIZE(drv2665_init_regs));
1394d10da13SDan Murphy 	if (error) {
1404d10da13SDan Murphy 		dev_err(&haptics->client->dev,
1414d10da13SDan Murphy 			"Failed to write init registers: %d\n",
1424d10da13SDan Murphy 			error);
1434d10da13SDan Murphy 		return error;
1444d10da13SDan Murphy 	}
1454d10da13SDan Murphy 
1464d10da13SDan Murphy 	return 0;
1474d10da13SDan Murphy }
1484d10da13SDan Murphy 
1494d10da13SDan Murphy static const struct regmap_config drv2665_regmap_config = {
1504d10da13SDan Murphy 	.reg_bits = 8,
1514d10da13SDan Murphy 	.val_bits = 8,
1524d10da13SDan Murphy 
1534d10da13SDan Murphy 	.max_register = DRV2665_FIFO,
1544d10da13SDan Murphy 	.reg_defaults = drv2665_reg_defs,
1554d10da13SDan Murphy 	.num_reg_defaults = ARRAY_SIZE(drv2665_reg_defs),
1564d10da13SDan Murphy 	.cache_type = REGCACHE_NONE,
1574d10da13SDan Murphy };
1584d10da13SDan Murphy 
drv2665_probe(struct i2c_client * client)159110c0a46SUwe Kleine-König static int drv2665_probe(struct i2c_client *client)
1604d10da13SDan Murphy {
1614d10da13SDan Murphy 	struct drv2665_data *haptics;
1624d10da13SDan Murphy 	int error;
1634d10da13SDan Murphy 
1644d10da13SDan Murphy 	haptics = devm_kzalloc(&client->dev, sizeof(*haptics), GFP_KERNEL);
1654d10da13SDan Murphy 	if (!haptics)
1664d10da13SDan Murphy 		return -ENOMEM;
1674d10da13SDan Murphy 
1684d10da13SDan Murphy 	haptics->regulator = devm_regulator_get(&client->dev, "vbat");
1694d10da13SDan Murphy 	if (IS_ERR(haptics->regulator)) {
1704d10da13SDan Murphy 		error = PTR_ERR(haptics->regulator);
1714d10da13SDan Murphy 		dev_err(&client->dev,
1724d10da13SDan Murphy 			"unable to get regulator, error: %d\n", error);
1734d10da13SDan Murphy 		return error;
1744d10da13SDan Murphy 	}
1754d10da13SDan Murphy 
1764d10da13SDan Murphy 	haptics->input_dev = devm_input_allocate_device(&client->dev);
1774d10da13SDan Murphy 	if (!haptics->input_dev) {
1784d10da13SDan Murphy 		dev_err(&client->dev, "Failed to allocate input device\n");
1794d10da13SDan Murphy 		return -ENOMEM;
1804d10da13SDan Murphy 	}
1814d10da13SDan Murphy 
1824d10da13SDan Murphy 	haptics->input_dev->name = "drv2665:haptics";
1834d10da13SDan Murphy 	haptics->input_dev->dev.parent = client->dev.parent;
1844d10da13SDan Murphy 	haptics->input_dev->close = drv2665_close;
1854d10da13SDan Murphy 	input_set_drvdata(haptics->input_dev, haptics);
1864d10da13SDan Murphy 	input_set_capability(haptics->input_dev, EV_FF, FF_RUMBLE);
1874d10da13SDan Murphy 
1884d10da13SDan Murphy 	error = input_ff_create_memless(haptics->input_dev, NULL,
1894d10da13SDan Murphy 					drv2665_haptics_play);
1904d10da13SDan Murphy 	if (error) {
1914d10da13SDan Murphy 		dev_err(&client->dev, "input_ff_create() failed: %d\n",
1924d10da13SDan Murphy 			error);
1934d10da13SDan Murphy 		return error;
1944d10da13SDan Murphy 	}
1954d10da13SDan Murphy 
1964d10da13SDan Murphy 	INIT_WORK(&haptics->work, drv2665_worker);
1974d10da13SDan Murphy 
1984d10da13SDan Murphy 	haptics->client = client;
1994d10da13SDan Murphy 	i2c_set_clientdata(client, haptics);
2004d10da13SDan Murphy 
2014d10da13SDan Murphy 	haptics->regmap = devm_regmap_init_i2c(client, &drv2665_regmap_config);
2024d10da13SDan Murphy 	if (IS_ERR(haptics->regmap)) {
2034d10da13SDan Murphy 		error = PTR_ERR(haptics->regmap);
2044d10da13SDan Murphy 		dev_err(&client->dev, "Failed to allocate register map: %d\n",
2054d10da13SDan Murphy 			error);
2064d10da13SDan Murphy 		return error;
2074d10da13SDan Murphy 	}
2084d10da13SDan Murphy 
2094d10da13SDan Murphy 	error = drv2665_init(haptics);
2104d10da13SDan Murphy 	if (error) {
2114d10da13SDan Murphy 		dev_err(&client->dev, "Device init failed: %d\n", error);
2124d10da13SDan Murphy 		return error;
2134d10da13SDan Murphy 	}
2144d10da13SDan Murphy 
2154d10da13SDan Murphy 	error = input_register_device(haptics->input_dev);
2164d10da13SDan Murphy 	if (error) {
2174d10da13SDan Murphy 		dev_err(&client->dev, "couldn't register input device: %d\n",
2184d10da13SDan Murphy 			error);
2194d10da13SDan Murphy 		return error;
2204d10da13SDan Murphy 	}
2214d10da13SDan Murphy 
2224d10da13SDan Murphy 	return 0;
2234d10da13SDan Murphy }
2244d10da13SDan Murphy 
drv2665_suspend(struct device * dev)225c7cd0194SJonathan Cameron static int drv2665_suspend(struct device *dev)
2264d10da13SDan Murphy {
2274d10da13SDan Murphy 	struct drv2665_data *haptics = dev_get_drvdata(dev);
2284d10da13SDan Murphy 	int ret = 0;
2294d10da13SDan Murphy 
2304d10da13SDan Murphy 	mutex_lock(&haptics->input_dev->mutex);
2314d10da13SDan Murphy 
232d69f0a43SAndrzej Pietrasiewicz 	if (input_device_enabled(haptics->input_dev)) {
2334d10da13SDan Murphy 		ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
234037b3af6SFlorian Vaussard 					 DRV2665_STANDBY, DRV2665_STANDBY);
2354d10da13SDan Murphy 		if (ret) {
2364d10da13SDan Murphy 			dev_err(dev, "Failed to set standby mode\n");
2374d10da13SDan Murphy 			regulator_disable(haptics->regulator);
2384d10da13SDan Murphy 			goto out;
2394d10da13SDan Murphy 		}
2404d10da13SDan Murphy 
2414d10da13SDan Murphy 		ret = regulator_disable(haptics->regulator);
2424d10da13SDan Murphy 		if (ret) {
2434d10da13SDan Murphy 			dev_err(dev, "Failed to disable regulator\n");
2444d10da13SDan Murphy 			regmap_update_bits(haptics->regmap,
2454d10da13SDan Murphy 					   DRV2665_CTRL_2,
2464d10da13SDan Murphy 					   DRV2665_STANDBY, 0);
2474d10da13SDan Murphy 		}
2484d10da13SDan Murphy 	}
2494d10da13SDan Murphy out:
2504d10da13SDan Murphy 	mutex_unlock(&haptics->input_dev->mutex);
2514d10da13SDan Murphy 	return ret;
2524d10da13SDan Murphy }
2534d10da13SDan Murphy 
drv2665_resume(struct device * dev)254c7cd0194SJonathan Cameron static int drv2665_resume(struct device *dev)
2554d10da13SDan Murphy {
2564d10da13SDan Murphy 	struct drv2665_data *haptics = dev_get_drvdata(dev);
2574d10da13SDan Murphy 	int ret = 0;
2584d10da13SDan Murphy 
2594d10da13SDan Murphy 	mutex_lock(&haptics->input_dev->mutex);
2604d10da13SDan Murphy 
261d69f0a43SAndrzej Pietrasiewicz 	if (input_device_enabled(haptics->input_dev)) {
2624d10da13SDan Murphy 		ret = regulator_enable(haptics->regulator);
2634d10da13SDan Murphy 		if (ret) {
2644d10da13SDan Murphy 			dev_err(dev, "Failed to enable regulator\n");
2654d10da13SDan Murphy 			goto out;
2664d10da13SDan Murphy 		}
2674d10da13SDan Murphy 
2684d10da13SDan Murphy 		ret = regmap_update_bits(haptics->regmap, DRV2665_CTRL_2,
2694d10da13SDan Murphy 					 DRV2665_STANDBY, 0);
2704d10da13SDan Murphy 		if (ret) {
2714d10da13SDan Murphy 			dev_err(dev, "Failed to unset standby mode\n");
2724d10da13SDan Murphy 			regulator_disable(haptics->regulator);
2734d10da13SDan Murphy 			goto out;
2744d10da13SDan Murphy 		}
2754d10da13SDan Murphy 
2764d10da13SDan Murphy 	}
2774d10da13SDan Murphy 
2784d10da13SDan Murphy out:
2794d10da13SDan Murphy 	mutex_unlock(&haptics->input_dev->mutex);
2804d10da13SDan Murphy 	return ret;
2814d10da13SDan Murphy }
2824d10da13SDan Murphy 
283c7cd0194SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(drv2665_pm_ops, drv2665_suspend, drv2665_resume);
2844d10da13SDan Murphy 
2854d10da13SDan Murphy static const struct i2c_device_id drv2665_id[] = {
2864d10da13SDan Murphy 	{ "drv2665", 0 },
2874d10da13SDan Murphy 	{ }
2884d10da13SDan Murphy };
2894d10da13SDan Murphy MODULE_DEVICE_TABLE(i2c, drv2665_id);
2904d10da13SDan Murphy 
2914d10da13SDan Murphy #ifdef CONFIG_OF
2924d10da13SDan Murphy static const struct of_device_id drv2665_of_match[] = {
2934d10da13SDan Murphy 	{ .compatible = "ti,drv2665", },
2944d10da13SDan Murphy 	{ }
2954d10da13SDan Murphy };
2964d10da13SDan Murphy MODULE_DEVICE_TABLE(of, drv2665_of_match);
2974d10da13SDan Murphy #endif
2984d10da13SDan Murphy 
2994d10da13SDan Murphy static struct i2c_driver drv2665_driver = {
300*d8bde56dSUwe Kleine-König 	.probe		= drv2665_probe,
3014d10da13SDan Murphy 	.driver		= {
3024d10da13SDan Murphy 		.name	= "drv2665-haptics",
3034d10da13SDan Murphy 		.of_match_table = of_match_ptr(drv2665_of_match),
304c7cd0194SJonathan Cameron 		.pm	= pm_sleep_ptr(&drv2665_pm_ops),
3054d10da13SDan Murphy 	},
3064d10da13SDan Murphy 	.id_table = drv2665_id,
3074d10da13SDan Murphy };
3084d10da13SDan Murphy module_i2c_driver(drv2665_driver);
3094d10da13SDan Murphy 
3104d10da13SDan Murphy MODULE_DESCRIPTION("TI DRV2665 haptics driver");
3114d10da13SDan Murphy MODULE_LICENSE("GPL");
3124d10da13SDan Murphy MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com>");
313