257 lines · 6.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Regulator haptic driver * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * Author: Jaewon Kim <jaewon02.kim@samsung.com> * Author: Hyunhee Kim <hyunhee.kim@samsung.com> */ #include <linux/input.h>#include <linux/module.h>#include <linux/of.h>#include <linux/platform_data/regulator-haptic.h>#include <linux/platform_device.h>#include <linux/regulator/consumer.h>#include <linux/slab.h>#include <linux/string_choices.h> #define MAX_MAGNITUDE_SHIFT	16 struct regulator_haptic {	struct device *dev;	struct input_dev *input_dev;	struct regulator *regulator; 	struct work_struct work;	struct mutex mutex; 	bool active;	bool suspended; 	unsigned int max_volt;	unsigned int min_volt;	unsigned int magnitude;}; static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on){	int error; 	if (haptic->active != on) { 		error = on ? regulator_enable(haptic->regulator) :			     regulator_disable(haptic->regulator);		if (error) {			dev_err(haptic->dev,				"failed to switch regulator %s: %d\n",				str_on_off(on), error);			return error;		} 		haptic->active = on;	} 	return 0;} static int regulator_haptic_set_voltage(struct regulator_haptic *haptic,					 unsigned int magnitude){	u64 volt_mag_multi;	unsigned int intensity;	int error; 	volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude;	intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT); 	error = regulator_set_voltage(haptic->regulator,				      intensity + haptic->min_volt,				      haptic->max_volt);	if (error) {		dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n",			intensity + haptic->min_volt, error);		return error;	} 	regulator_haptic_toggle(haptic, !!magnitude); 	return 0;} static void regulator_haptic_work(struct work_struct *work){	struct regulator_haptic *haptic = container_of(work,					struct regulator_haptic, work); 	guard(mutex)(&haptic->mutex); 	if (!haptic->suspended)		regulator_haptic_set_voltage(haptic, haptic->magnitude);} static int regulator_haptic_play_effect(struct input_dev *input, void *data,					struct ff_effect *effect){	struct regulator_haptic *haptic = input_get_drvdata(input); 	haptic->magnitude = effect->u.rumble.strong_magnitude;	if (!haptic->magnitude)		haptic->magnitude = effect->u.rumble.weak_magnitude; 	schedule_work(&haptic->work); 	return 0;} static void regulator_haptic_close(struct input_dev *input){	struct regulator_haptic *haptic = input_get_drvdata(input); 	cancel_work_sync(&haptic->work);	regulator_haptic_set_voltage(haptic, 0);} static int __maybe_unusedregulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic){	struct device_node *node;	int error; 	node = dev->of_node;	if(!node) {		dev_err(dev, "Missing device tree data\n");		return -EINVAL;	} 	error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt);	if (error) {		dev_err(dev, "cannot parse max-microvolt\n");		return error;	} 	error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt);	if (error) {		dev_err(dev, "cannot parse min-microvolt\n");		return error;	} 	return 0;} static int regulator_haptic_probe(struct platform_device *pdev){	const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev);	struct regulator_haptic *haptic;	struct input_dev *input_dev;	int error; 	haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL);	if (!haptic)		return -ENOMEM; 	platform_set_drvdata(pdev, haptic);	haptic->dev = &pdev->dev;	mutex_init(&haptic->mutex);	INIT_WORK(&haptic->work, regulator_haptic_work); 	if (pdata) {		haptic->max_volt = pdata->max_volt;		haptic->min_volt = pdata->min_volt;	} else if (IS_ENABLED(CONFIG_OF)) {		error = regulator_haptic_parse_dt(&pdev->dev, haptic);		if (error)			return error;	} else {		dev_err(&pdev->dev, "Missing platform data\n");		return -EINVAL;	} 	haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic");	if (IS_ERR(haptic->regulator)) {		dev_err(&pdev->dev, "failed to get regulator\n");		return PTR_ERR(haptic->regulator);	} 	input_dev = devm_input_allocate_device(&pdev->dev);	if (!input_dev)		return	-ENOMEM; 	haptic->input_dev = input_dev;	haptic->input_dev->name = "regulator-haptic";	haptic->input_dev->dev.parent = &pdev->dev;	haptic->input_dev->close = regulator_haptic_close;	input_set_drvdata(haptic->input_dev, haptic);	input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); 	error = input_ff_create_memless(input_dev, NULL,					regulator_haptic_play_effect);	if (error) {		dev_err(&pdev->dev, "failed to create force-feedback\n");		return error;	} 	error = input_register_device(haptic->input_dev);	if (error) {		dev_err(&pdev->dev, "failed to register input device\n");		return error;	} 	return 0;} static int regulator_haptic_suspend(struct device *dev){	struct platform_device *pdev = to_platform_device(dev);	struct regulator_haptic *haptic = platform_get_drvdata(pdev); 	scoped_guard(mutex_intr, &haptic->mutex) {		regulator_haptic_set_voltage(haptic, 0);		haptic->suspended = true; 		return 0;	} 	return -EINTR;} static int regulator_haptic_resume(struct device *dev){	struct platform_device *pdev = to_platform_device(dev);	struct regulator_haptic *haptic = platform_get_drvdata(pdev);	unsigned int magnitude; 	guard(mutex)(&haptic->mutex); 	haptic->suspended = false; 	magnitude = READ_ONCE(haptic->magnitude);	if (magnitude)		regulator_haptic_set_voltage(haptic, magnitude); 	return 0;} static DEFINE_SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops,		regulator_haptic_suspend, regulator_haptic_resume); static const struct of_device_id regulator_haptic_dt_match[] = {	{ .compatible = "regulator-haptic" },	{ /* sentinel */ },};MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match); static struct platform_driver regulator_haptic_driver = {	.probe		= regulator_haptic_probe,	.driver		= {		.name		= "regulator-haptic",		.of_match_table = regulator_haptic_dt_match,		.pm		= pm_sleep_ptr(&regulator_haptic_pm_ops),	},};module_platform_driver(regulator_haptic_driver); MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>");MODULE_DESCRIPTION("Regulator haptic driver");MODULE_LICENSE("GPL");