1d64cb71bSJaewon Kim /* 2d64cb71bSJaewon Kim * Regulator haptic driver 3d64cb71bSJaewon Kim * 4d64cb71bSJaewon Kim * Copyright (c) 2014 Samsung Electronics Co., Ltd. 5d64cb71bSJaewon Kim * Author: Jaewon Kim <jaewon02.kim@samsung.com> 6d64cb71bSJaewon Kim * Author: Hyunhee Kim <hyunhee.kim@samsung.com> 7d64cb71bSJaewon Kim * 8d64cb71bSJaewon Kim * This program is free software; you can redistribute it and/or modify 9d64cb71bSJaewon Kim * it under the terms of the GNU General Public License version 2 as 10d64cb71bSJaewon Kim * published by the Free Software Foundation. 11d64cb71bSJaewon Kim */ 12d64cb71bSJaewon Kim 13d64cb71bSJaewon Kim #include <linux/input.h> 14d64cb71bSJaewon Kim #include <linux/module.h> 15d64cb71bSJaewon Kim #include <linux/of.h> 16d64cb71bSJaewon Kim #include <linux/platform_data/regulator-haptic.h> 17d64cb71bSJaewon Kim #include <linux/platform_device.h> 18d64cb71bSJaewon Kim #include <linux/regulator/consumer.h> 19d64cb71bSJaewon Kim #include <linux/slab.h> 20d64cb71bSJaewon Kim 21d64cb71bSJaewon Kim #define MAX_MAGNITUDE_SHIFT 16 22d64cb71bSJaewon Kim 23d64cb71bSJaewon Kim struct regulator_haptic { 24d64cb71bSJaewon Kim struct device *dev; 25d64cb71bSJaewon Kim struct input_dev *input_dev; 26d64cb71bSJaewon Kim struct regulator *regulator; 27d64cb71bSJaewon Kim 28d64cb71bSJaewon Kim struct work_struct work; 29d64cb71bSJaewon Kim struct mutex mutex; 30d64cb71bSJaewon Kim 31d64cb71bSJaewon Kim bool active; 32d64cb71bSJaewon Kim bool suspended; 33d64cb71bSJaewon Kim 34d64cb71bSJaewon Kim unsigned int max_volt; 35d64cb71bSJaewon Kim unsigned int min_volt; 36d64cb71bSJaewon Kim unsigned int magnitude; 37d64cb71bSJaewon Kim }; 38d64cb71bSJaewon Kim 39d64cb71bSJaewon Kim static int regulator_haptic_toggle(struct regulator_haptic *haptic, bool on) 40d64cb71bSJaewon Kim { 41d64cb71bSJaewon Kim int error; 42d64cb71bSJaewon Kim 43d64cb71bSJaewon Kim if (haptic->active != on) { 44d64cb71bSJaewon Kim 45d64cb71bSJaewon Kim error = on ? regulator_enable(haptic->regulator) : 46d64cb71bSJaewon Kim regulator_disable(haptic->regulator); 47d64cb71bSJaewon Kim if (error) { 48d64cb71bSJaewon Kim dev_err(haptic->dev, 49d64cb71bSJaewon Kim "failed to switch regulator %s: %d\n", 50d64cb71bSJaewon Kim on ? "on" : "off", error); 51d64cb71bSJaewon Kim return error; 52d64cb71bSJaewon Kim } 53d64cb71bSJaewon Kim 54d64cb71bSJaewon Kim haptic->active = on; 55d64cb71bSJaewon Kim } 56d64cb71bSJaewon Kim 57d64cb71bSJaewon Kim return 0; 58d64cb71bSJaewon Kim } 59d64cb71bSJaewon Kim 60d64cb71bSJaewon Kim static int regulator_haptic_set_voltage(struct regulator_haptic *haptic, 61d64cb71bSJaewon Kim unsigned int magnitude) 62d64cb71bSJaewon Kim { 63d64cb71bSJaewon Kim u64 volt_mag_multi; 64d64cb71bSJaewon Kim unsigned int intensity; 65d64cb71bSJaewon Kim int error; 66d64cb71bSJaewon Kim 67d64cb71bSJaewon Kim volt_mag_multi = (u64)(haptic->max_volt - haptic->min_volt) * magnitude; 68d64cb71bSJaewon Kim intensity = (unsigned int)(volt_mag_multi >> MAX_MAGNITUDE_SHIFT); 69d64cb71bSJaewon Kim 70d64cb71bSJaewon Kim error = regulator_set_voltage(haptic->regulator, 71d64cb71bSJaewon Kim intensity + haptic->min_volt, 72d64cb71bSJaewon Kim haptic->max_volt); 73d64cb71bSJaewon Kim if (error) { 74d64cb71bSJaewon Kim dev_err(haptic->dev, "cannot set regulator voltage to %d: %d\n", 75d64cb71bSJaewon Kim intensity + haptic->min_volt, error); 76d64cb71bSJaewon Kim return error; 77d64cb71bSJaewon Kim } 78d64cb71bSJaewon Kim 79d64cb71bSJaewon Kim return 0; 80d64cb71bSJaewon Kim } 81d64cb71bSJaewon Kim 82d64cb71bSJaewon Kim static void regulator_haptic_work(struct work_struct *work) 83d64cb71bSJaewon Kim { 84d64cb71bSJaewon Kim struct regulator_haptic *haptic = container_of(work, 85d64cb71bSJaewon Kim struct regulator_haptic, work); 86d64cb71bSJaewon Kim unsigned int magnitude; 87d64cb71bSJaewon Kim int error; 88d64cb71bSJaewon Kim 89d64cb71bSJaewon Kim mutex_lock(&haptic->mutex); 90d64cb71bSJaewon Kim 91d64cb71bSJaewon Kim if (haptic->suspended) 92d64cb71bSJaewon Kim goto out; 93d64cb71bSJaewon Kim 94d64cb71bSJaewon Kim magnitude = ACCESS_ONCE(haptic->magnitude); 95d64cb71bSJaewon Kim 96d64cb71bSJaewon Kim error = regulator_haptic_set_voltage(haptic, magnitude); 97d64cb71bSJaewon Kim if (error) 98d64cb71bSJaewon Kim goto out; 99d64cb71bSJaewon Kim 100d64cb71bSJaewon Kim regulator_haptic_toggle(haptic, magnitude != 0); 101d64cb71bSJaewon Kim 102d64cb71bSJaewon Kim out: 103d64cb71bSJaewon Kim mutex_unlock(&haptic->mutex); 104d64cb71bSJaewon Kim } 105d64cb71bSJaewon Kim 106d64cb71bSJaewon Kim static int regulator_haptic_play_effect(struct input_dev *input, void *data, 107d64cb71bSJaewon Kim struct ff_effect *effect) 108d64cb71bSJaewon Kim { 109d64cb71bSJaewon Kim struct regulator_haptic *haptic = input_get_drvdata(input); 110d64cb71bSJaewon Kim 111d64cb71bSJaewon Kim haptic->magnitude = effect->u.rumble.strong_magnitude; 112d64cb71bSJaewon Kim if (!haptic->magnitude) 113d64cb71bSJaewon Kim haptic->magnitude = effect->u.rumble.weak_magnitude; 114d64cb71bSJaewon Kim 115d64cb71bSJaewon Kim schedule_work(&haptic->work); 116d64cb71bSJaewon Kim 117d64cb71bSJaewon Kim return 0; 118d64cb71bSJaewon Kim } 119d64cb71bSJaewon Kim 120d64cb71bSJaewon Kim static void regulator_haptic_close(struct input_dev *input) 121d64cb71bSJaewon Kim { 122d64cb71bSJaewon Kim struct regulator_haptic *haptic = input_get_drvdata(input); 123d64cb71bSJaewon Kim 124d64cb71bSJaewon Kim cancel_work_sync(&haptic->work); 125d64cb71bSJaewon Kim regulator_haptic_set_voltage(haptic, 0); 126d64cb71bSJaewon Kim regulator_haptic_toggle(haptic, false); 127d64cb71bSJaewon Kim } 128d64cb71bSJaewon Kim 129d64cb71bSJaewon Kim static int __maybe_unused 130d64cb71bSJaewon Kim regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic) 131d64cb71bSJaewon Kim { 132d64cb71bSJaewon Kim struct device_node *node; 133d64cb71bSJaewon Kim int error; 134d64cb71bSJaewon Kim 135d64cb71bSJaewon Kim node = dev->of_node; 136d64cb71bSJaewon Kim if(!node) { 137d64cb71bSJaewon Kim dev_err(dev, "Missing dveice tree data\n"); 138d64cb71bSJaewon Kim return -EINVAL; 139d64cb71bSJaewon Kim } 140d64cb71bSJaewon Kim 141d64cb71bSJaewon Kim error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt); 142d64cb71bSJaewon Kim if (error) { 143d64cb71bSJaewon Kim dev_err(dev, "cannot parse max-microvolt\n"); 144d64cb71bSJaewon Kim return error; 145d64cb71bSJaewon Kim } 146d64cb71bSJaewon Kim 147d64cb71bSJaewon Kim error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt); 148d64cb71bSJaewon Kim if (error) { 149d64cb71bSJaewon Kim dev_err(dev, "cannot parse min-microvolt\n"); 150d64cb71bSJaewon Kim return error; 151d64cb71bSJaewon Kim } 152d64cb71bSJaewon Kim 153d64cb71bSJaewon Kim return 0; 154d64cb71bSJaewon Kim } 155d64cb71bSJaewon Kim 156d64cb71bSJaewon Kim static int regulator_haptic_probe(struct platform_device *pdev) 157d64cb71bSJaewon Kim { 158d64cb71bSJaewon Kim const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev); 159d64cb71bSJaewon Kim struct regulator_haptic *haptic; 160d64cb71bSJaewon Kim struct input_dev *input_dev; 161d64cb71bSJaewon Kim int error; 162d64cb71bSJaewon Kim 163d64cb71bSJaewon Kim haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); 164d64cb71bSJaewon Kim if (!haptic) 165d64cb71bSJaewon Kim return -ENOMEM; 166d64cb71bSJaewon Kim 167d64cb71bSJaewon Kim platform_set_drvdata(pdev, haptic); 168d64cb71bSJaewon Kim haptic->dev = &pdev->dev; 169d64cb71bSJaewon Kim mutex_init(&haptic->mutex); 170d64cb71bSJaewon Kim INIT_WORK(&haptic->work, regulator_haptic_work); 171d64cb71bSJaewon Kim 172d64cb71bSJaewon Kim if (pdata) { 173d64cb71bSJaewon Kim haptic->max_volt = pdata->max_volt; 174d64cb71bSJaewon Kim haptic->min_volt = pdata->min_volt; 175d64cb71bSJaewon Kim } else if (IS_ENABLED(CONFIG_OF)) { 176d64cb71bSJaewon Kim error = regulator_haptic_parse_dt(&pdev->dev, haptic); 177d64cb71bSJaewon Kim if (error) 178d64cb71bSJaewon Kim return error; 179d64cb71bSJaewon Kim } else { 180d64cb71bSJaewon Kim dev_err(&pdev->dev, "Missing platform data\n"); 181d64cb71bSJaewon Kim return -EINVAL; 182d64cb71bSJaewon Kim } 183d64cb71bSJaewon Kim 184d64cb71bSJaewon Kim haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic"); 185d64cb71bSJaewon Kim if (IS_ERR(haptic->regulator)) { 186d64cb71bSJaewon Kim dev_err(&pdev->dev, "failed to get regulator\n"); 187d64cb71bSJaewon Kim return PTR_ERR(haptic->regulator); 188d64cb71bSJaewon Kim } 189d64cb71bSJaewon Kim 190d64cb71bSJaewon Kim input_dev = devm_input_allocate_device(&pdev->dev); 191d64cb71bSJaewon Kim if (!input_dev) 192d64cb71bSJaewon Kim return -ENOMEM; 193d64cb71bSJaewon Kim 194d64cb71bSJaewon Kim haptic->input_dev = input_dev; 195d64cb71bSJaewon Kim haptic->input_dev->name = "regulator-haptic"; 196d64cb71bSJaewon Kim haptic->input_dev->dev.parent = &pdev->dev; 197d64cb71bSJaewon Kim haptic->input_dev->close = regulator_haptic_close; 198d64cb71bSJaewon Kim input_set_drvdata(haptic->input_dev, haptic); 199d64cb71bSJaewon Kim input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); 200d64cb71bSJaewon Kim 201d64cb71bSJaewon Kim error = input_ff_create_memless(input_dev, NULL, 202d64cb71bSJaewon Kim regulator_haptic_play_effect); 203d64cb71bSJaewon Kim if (error) { 204d64cb71bSJaewon Kim dev_err(&pdev->dev, "failed to create force-feedback\n"); 205d64cb71bSJaewon Kim return error; 206d64cb71bSJaewon Kim } 207d64cb71bSJaewon Kim 208d64cb71bSJaewon Kim error = input_register_device(haptic->input_dev); 209d64cb71bSJaewon Kim if (error) { 210d64cb71bSJaewon Kim dev_err(&pdev->dev, "failed to register input device\n"); 211d64cb71bSJaewon Kim return error; 212d64cb71bSJaewon Kim } 213d64cb71bSJaewon Kim 214d64cb71bSJaewon Kim return 0; 215d64cb71bSJaewon Kim } 216d64cb71bSJaewon Kim 217d64cb71bSJaewon Kim static int __maybe_unused regulator_haptic_suspend(struct device *dev) 218d64cb71bSJaewon Kim { 219d64cb71bSJaewon Kim struct platform_device *pdev = to_platform_device(dev); 220d64cb71bSJaewon Kim struct regulator_haptic *haptic = platform_get_drvdata(pdev); 221d64cb71bSJaewon Kim int error; 222d64cb71bSJaewon Kim 223d64cb71bSJaewon Kim error = mutex_lock_interruptible(&haptic->mutex); 224d64cb71bSJaewon Kim if (error) 225d64cb71bSJaewon Kim return error; 226d64cb71bSJaewon Kim 227d64cb71bSJaewon Kim regulator_haptic_set_voltage(haptic, 0); 228d64cb71bSJaewon Kim regulator_haptic_toggle(haptic, false); 229d64cb71bSJaewon Kim 230d64cb71bSJaewon Kim haptic->suspended = true; 231d64cb71bSJaewon Kim 232d64cb71bSJaewon Kim mutex_unlock(&haptic->mutex); 233d64cb71bSJaewon Kim 234d64cb71bSJaewon Kim return 0; 235d64cb71bSJaewon Kim } 236d64cb71bSJaewon Kim 237d64cb71bSJaewon Kim static int __maybe_unused regulator_haptic_resume(struct device *dev) 238d64cb71bSJaewon Kim { 239d64cb71bSJaewon Kim struct platform_device *pdev = to_platform_device(dev); 240d64cb71bSJaewon Kim struct regulator_haptic *haptic = platform_get_drvdata(pdev); 241d64cb71bSJaewon Kim unsigned int magnitude; 242d64cb71bSJaewon Kim 243d64cb71bSJaewon Kim mutex_lock(&haptic->mutex); 244d64cb71bSJaewon Kim 245d64cb71bSJaewon Kim haptic->suspended = false; 246d64cb71bSJaewon Kim 247d64cb71bSJaewon Kim magnitude = ACCESS_ONCE(haptic->magnitude); 248d64cb71bSJaewon Kim if (magnitude) { 249d64cb71bSJaewon Kim regulator_haptic_set_voltage(haptic, magnitude); 250d64cb71bSJaewon Kim regulator_haptic_toggle(haptic, true); 251d64cb71bSJaewon Kim } 252d64cb71bSJaewon Kim 253d64cb71bSJaewon Kim mutex_unlock(&haptic->mutex); 254d64cb71bSJaewon Kim 255d64cb71bSJaewon Kim return 0; 256d64cb71bSJaewon Kim } 257d64cb71bSJaewon Kim 258d64cb71bSJaewon Kim static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, 259d64cb71bSJaewon Kim regulator_haptic_suspend, regulator_haptic_resume); 260d64cb71bSJaewon Kim 261d64cb71bSJaewon Kim static struct of_device_id regulator_haptic_dt_match[] = { 262d64cb71bSJaewon Kim { .compatible = "regulator-haptic" }, 263d64cb71bSJaewon Kim { /* sentinel */ }, 264d64cb71bSJaewon Kim }; 265d64cb71bSJaewon Kim 266d64cb71bSJaewon Kim static struct platform_driver regulator_haptic_driver = { 267d64cb71bSJaewon Kim .probe = regulator_haptic_probe, 268d64cb71bSJaewon Kim .driver = { 269d64cb71bSJaewon Kim .name = "regulator-haptic", 270d64cb71bSJaewon Kim .of_match_table = regulator_haptic_dt_match, 271d64cb71bSJaewon Kim .pm = ®ulator_haptic_pm_ops, 272d64cb71bSJaewon Kim }, 273d64cb71bSJaewon Kim }; 274d64cb71bSJaewon Kim module_platform_driver(regulator_haptic_driver); 275d64cb71bSJaewon Kim 276d64cb71bSJaewon Kim MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); 277d64cb71bSJaewon Kim MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>"); 278d64cb71bSJaewon Kim MODULE_DESCRIPTION("Regulator haptic driver"); 279d64cb71bSJaewon Kim MODULE_LICENSE("GPL"); 280