mirror of
https://github.com/torvalds/linux.git
synced 2024-11-14 16:12:02 +00:00
dbce1a7d5d
The DT of_device.h and of_platform.h date back to the separate of_platform_bus_type before it as merged into the regular platform bus. As part of that merge prepping Arm DT support 13 years ago, they "temporarily" include each other. They also include platform_device.h and of.h. As a result, there's a pretty much random mix of those include files used throughout the tree. In order to detangle these headers and replace the implicit includes with struct declarations, users need to explicitly include the correct includes. Signed-off-by: Rob Herring <robh@kernel.org> Link: https://lore.kernel.org/r/20230714174633.4058096-1-robh@kernel.org Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
200 lines
4.8 KiB
C
200 lines
4.8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* GPIO vibrator driver
|
|
*
|
|
* Copyright (C) 2019 Luca Weiss <luca@z3ntu.xyz>
|
|
*
|
|
* Based on PWM vibrator driver:
|
|
* Copyright (C) 2017 Collabora Ltd.
|
|
*
|
|
* Based on previous work from:
|
|
* Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
|
*
|
|
* Based on PWM beeper driver:
|
|
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
|
|
*/
|
|
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/input.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/property.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/slab.h>
|
|
|
|
struct gpio_vibrator {
|
|
struct input_dev *input;
|
|
struct gpio_desc *gpio;
|
|
struct regulator *vcc;
|
|
|
|
struct work_struct play_work;
|
|
bool running;
|
|
bool vcc_on;
|
|
};
|
|
|
|
static int gpio_vibrator_start(struct gpio_vibrator *vibrator)
|
|
{
|
|
struct device *pdev = vibrator->input->dev.parent;
|
|
int err;
|
|
|
|
if (!vibrator->vcc_on) {
|
|
err = regulator_enable(vibrator->vcc);
|
|
if (err) {
|
|
dev_err(pdev, "failed to enable regulator: %d\n", err);
|
|
return err;
|
|
}
|
|
vibrator->vcc_on = true;
|
|
}
|
|
|
|
gpiod_set_value_cansleep(vibrator->gpio, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpio_vibrator_stop(struct gpio_vibrator *vibrator)
|
|
{
|
|
gpiod_set_value_cansleep(vibrator->gpio, 0);
|
|
|
|
if (vibrator->vcc_on) {
|
|
regulator_disable(vibrator->vcc);
|
|
vibrator->vcc_on = false;
|
|
}
|
|
}
|
|
|
|
static void gpio_vibrator_play_work(struct work_struct *work)
|
|
{
|
|
struct gpio_vibrator *vibrator =
|
|
container_of(work, struct gpio_vibrator, play_work);
|
|
|
|
if (vibrator->running)
|
|
gpio_vibrator_start(vibrator);
|
|
else
|
|
gpio_vibrator_stop(vibrator);
|
|
}
|
|
|
|
static int gpio_vibrator_play_effect(struct input_dev *dev, void *data,
|
|
struct ff_effect *effect)
|
|
{
|
|
struct gpio_vibrator *vibrator = input_get_drvdata(dev);
|
|
int level;
|
|
|
|
level = effect->u.rumble.strong_magnitude;
|
|
if (!level)
|
|
level = effect->u.rumble.weak_magnitude;
|
|
|
|
vibrator->running = level;
|
|
schedule_work(&vibrator->play_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void gpio_vibrator_close(struct input_dev *input)
|
|
{
|
|
struct gpio_vibrator *vibrator = input_get_drvdata(input);
|
|
|
|
cancel_work_sync(&vibrator->play_work);
|
|
gpio_vibrator_stop(vibrator);
|
|
vibrator->running = false;
|
|
}
|
|
|
|
static int gpio_vibrator_probe(struct platform_device *pdev)
|
|
{
|
|
struct gpio_vibrator *vibrator;
|
|
int err;
|
|
|
|
vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
|
|
if (!vibrator)
|
|
return -ENOMEM;
|
|
|
|
vibrator->input = devm_input_allocate_device(&pdev->dev);
|
|
if (!vibrator->input)
|
|
return -ENOMEM;
|
|
|
|
vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
|
|
if (IS_ERR(vibrator->vcc))
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->vcc),
|
|
"Failed to request regulator\n");
|
|
|
|
vibrator->gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
|
|
if (IS_ERR(vibrator->gpio))
|
|
return dev_err_probe(&pdev->dev, PTR_ERR(vibrator->gpio),
|
|
"Failed to request main gpio\n");
|
|
|
|
INIT_WORK(&vibrator->play_work, gpio_vibrator_play_work);
|
|
|
|
vibrator->input->name = "gpio-vibrator";
|
|
vibrator->input->id.bustype = BUS_HOST;
|
|
vibrator->input->close = gpio_vibrator_close;
|
|
|
|
input_set_drvdata(vibrator->input, vibrator);
|
|
input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
|
|
|
|
err = input_ff_create_memless(vibrator->input, NULL,
|
|
gpio_vibrator_play_effect);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Couldn't create FF dev: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
err = input_register_device(vibrator->input);
|
|
if (err) {
|
|
dev_err(&pdev->dev, "Couldn't register input dev: %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
platform_set_drvdata(pdev, vibrator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_vibrator_suspend(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct gpio_vibrator *vibrator = platform_get_drvdata(pdev);
|
|
|
|
cancel_work_sync(&vibrator->play_work);
|
|
if (vibrator->running)
|
|
gpio_vibrator_stop(vibrator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_vibrator_resume(struct device *dev)
|
|
{
|
|
struct platform_device *pdev = to_platform_device(dev);
|
|
struct gpio_vibrator *vibrator = platform_get_drvdata(pdev);
|
|
|
|
if (vibrator->running)
|
|
gpio_vibrator_start(vibrator);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DEFINE_SIMPLE_DEV_PM_OPS(gpio_vibrator_pm_ops,
|
|
gpio_vibrator_suspend, gpio_vibrator_resume);
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id gpio_vibra_dt_match_table[] = {
|
|
{ .compatible = "gpio-vibrator" },
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(of, gpio_vibra_dt_match_table);
|
|
#endif
|
|
|
|
static struct platform_driver gpio_vibrator_driver = {
|
|
.probe = gpio_vibrator_probe,
|
|
.driver = {
|
|
.name = "gpio-vibrator",
|
|
.pm = pm_sleep_ptr(&gpio_vibrator_pm_ops),
|
|
.of_match_table = of_match_ptr(gpio_vibra_dt_match_table),
|
|
},
|
|
};
|
|
module_platform_driver(gpio_vibrator_driver);
|
|
|
|
MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xy>");
|
|
MODULE_DESCRIPTION("GPIO vibrator driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:gpio-vibrator");
|