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 793cb6dcfaSAxel Lin regulator_haptic_toggle(haptic, !!magnitude); 803cb6dcfaSAxel Lin 81d64cb71bSJaewon Kim return 0; 82d64cb71bSJaewon Kim } 83d64cb71bSJaewon Kim 84d64cb71bSJaewon Kim static void regulator_haptic_work(struct work_struct *work) 85d64cb71bSJaewon Kim { 86d64cb71bSJaewon Kim struct regulator_haptic *haptic = container_of(work, 87d64cb71bSJaewon Kim struct regulator_haptic, work); 88d64cb71bSJaewon Kim 89d64cb71bSJaewon Kim mutex_lock(&haptic->mutex); 90d64cb71bSJaewon Kim 913cb6dcfaSAxel Lin if (!haptic->suspended) 923cb6dcfaSAxel Lin regulator_haptic_set_voltage(haptic, haptic->magnitude); 93d64cb71bSJaewon Kim 94d64cb71bSJaewon Kim mutex_unlock(&haptic->mutex); 95d64cb71bSJaewon Kim } 96d64cb71bSJaewon Kim 97d64cb71bSJaewon Kim static int regulator_haptic_play_effect(struct input_dev *input, void *data, 98d64cb71bSJaewon Kim struct ff_effect *effect) 99d64cb71bSJaewon Kim { 100d64cb71bSJaewon Kim struct regulator_haptic *haptic = input_get_drvdata(input); 101d64cb71bSJaewon Kim 102d64cb71bSJaewon Kim haptic->magnitude = effect->u.rumble.strong_magnitude; 103d64cb71bSJaewon Kim if (!haptic->magnitude) 104d64cb71bSJaewon Kim haptic->magnitude = effect->u.rumble.weak_magnitude; 105d64cb71bSJaewon Kim 106d64cb71bSJaewon Kim schedule_work(&haptic->work); 107d64cb71bSJaewon Kim 108d64cb71bSJaewon Kim return 0; 109d64cb71bSJaewon Kim } 110d64cb71bSJaewon Kim 111d64cb71bSJaewon Kim static void regulator_haptic_close(struct input_dev *input) 112d64cb71bSJaewon Kim { 113d64cb71bSJaewon Kim struct regulator_haptic *haptic = input_get_drvdata(input); 114d64cb71bSJaewon Kim 115d64cb71bSJaewon Kim cancel_work_sync(&haptic->work); 116d64cb71bSJaewon Kim regulator_haptic_set_voltage(haptic, 0); 117d64cb71bSJaewon Kim } 118d64cb71bSJaewon Kim 119d64cb71bSJaewon Kim static int __maybe_unused 120d64cb71bSJaewon Kim regulator_haptic_parse_dt(struct device *dev, struct regulator_haptic *haptic) 121d64cb71bSJaewon Kim { 122d64cb71bSJaewon Kim struct device_node *node; 123d64cb71bSJaewon Kim int error; 124d64cb71bSJaewon Kim 125d64cb71bSJaewon Kim node = dev->of_node; 126d64cb71bSJaewon Kim if(!node) { 12798f27d96SColin Ian King dev_err(dev, "Missing device tree data\n"); 128d64cb71bSJaewon Kim return -EINVAL; 129d64cb71bSJaewon Kim } 130d64cb71bSJaewon Kim 131d64cb71bSJaewon Kim error = of_property_read_u32(node, "max-microvolt", &haptic->max_volt); 132d64cb71bSJaewon Kim if (error) { 133d64cb71bSJaewon Kim dev_err(dev, "cannot parse max-microvolt\n"); 134d64cb71bSJaewon Kim return error; 135d64cb71bSJaewon Kim } 136d64cb71bSJaewon Kim 137d64cb71bSJaewon Kim error = of_property_read_u32(node, "min-microvolt", &haptic->min_volt); 138d64cb71bSJaewon Kim if (error) { 139d64cb71bSJaewon Kim dev_err(dev, "cannot parse min-microvolt\n"); 140d64cb71bSJaewon Kim return error; 141d64cb71bSJaewon Kim } 142d64cb71bSJaewon Kim 143d64cb71bSJaewon Kim return 0; 144d64cb71bSJaewon Kim } 145d64cb71bSJaewon Kim 146d64cb71bSJaewon Kim static int regulator_haptic_probe(struct platform_device *pdev) 147d64cb71bSJaewon Kim { 148d64cb71bSJaewon Kim const struct regulator_haptic_data *pdata = dev_get_platdata(&pdev->dev); 149d64cb71bSJaewon Kim struct regulator_haptic *haptic; 150d64cb71bSJaewon Kim struct input_dev *input_dev; 151d64cb71bSJaewon Kim int error; 152d64cb71bSJaewon Kim 153d64cb71bSJaewon Kim haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); 154d64cb71bSJaewon Kim if (!haptic) 155d64cb71bSJaewon Kim return -ENOMEM; 156d64cb71bSJaewon Kim 157d64cb71bSJaewon Kim platform_set_drvdata(pdev, haptic); 158d64cb71bSJaewon Kim haptic->dev = &pdev->dev; 159d64cb71bSJaewon Kim mutex_init(&haptic->mutex); 160d64cb71bSJaewon Kim INIT_WORK(&haptic->work, regulator_haptic_work); 161d64cb71bSJaewon Kim 162d64cb71bSJaewon Kim if (pdata) { 163d64cb71bSJaewon Kim haptic->max_volt = pdata->max_volt; 164d64cb71bSJaewon Kim haptic->min_volt = pdata->min_volt; 165d64cb71bSJaewon Kim } else if (IS_ENABLED(CONFIG_OF)) { 166d64cb71bSJaewon Kim error = regulator_haptic_parse_dt(&pdev->dev, haptic); 167d64cb71bSJaewon Kim if (error) 168d64cb71bSJaewon Kim return error; 169d64cb71bSJaewon Kim } else { 170d64cb71bSJaewon Kim dev_err(&pdev->dev, "Missing platform data\n"); 171d64cb71bSJaewon Kim return -EINVAL; 172d64cb71bSJaewon Kim } 173d64cb71bSJaewon Kim 174d64cb71bSJaewon Kim haptic->regulator = devm_regulator_get_exclusive(&pdev->dev, "haptic"); 175d64cb71bSJaewon Kim if (IS_ERR(haptic->regulator)) { 176d64cb71bSJaewon Kim dev_err(&pdev->dev, "failed to get regulator\n"); 177d64cb71bSJaewon Kim return PTR_ERR(haptic->regulator); 178d64cb71bSJaewon Kim } 179d64cb71bSJaewon Kim 180d64cb71bSJaewon Kim input_dev = devm_input_allocate_device(&pdev->dev); 181d64cb71bSJaewon Kim if (!input_dev) 182d64cb71bSJaewon Kim return -ENOMEM; 183d64cb71bSJaewon Kim 184d64cb71bSJaewon Kim haptic->input_dev = input_dev; 185d64cb71bSJaewon Kim haptic->input_dev->name = "regulator-haptic"; 186d64cb71bSJaewon Kim haptic->input_dev->dev.parent = &pdev->dev; 187d64cb71bSJaewon Kim haptic->input_dev->close = regulator_haptic_close; 188d64cb71bSJaewon Kim input_set_drvdata(haptic->input_dev, haptic); 189d64cb71bSJaewon Kim input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); 190d64cb71bSJaewon Kim 191d64cb71bSJaewon Kim error = input_ff_create_memless(input_dev, NULL, 192d64cb71bSJaewon Kim regulator_haptic_play_effect); 193d64cb71bSJaewon Kim if (error) { 194d64cb71bSJaewon Kim dev_err(&pdev->dev, "failed to create force-feedback\n"); 195d64cb71bSJaewon Kim return error; 196d64cb71bSJaewon Kim } 197d64cb71bSJaewon Kim 198d64cb71bSJaewon Kim error = input_register_device(haptic->input_dev); 199d64cb71bSJaewon Kim if (error) { 200d64cb71bSJaewon Kim dev_err(&pdev->dev, "failed to register input device\n"); 201d64cb71bSJaewon Kim return error; 202d64cb71bSJaewon Kim } 203d64cb71bSJaewon Kim 204d64cb71bSJaewon Kim return 0; 205d64cb71bSJaewon Kim } 206d64cb71bSJaewon Kim 207d64cb71bSJaewon Kim static int __maybe_unused regulator_haptic_suspend(struct device *dev) 208d64cb71bSJaewon Kim { 209d64cb71bSJaewon Kim struct platform_device *pdev = to_platform_device(dev); 210d64cb71bSJaewon Kim struct regulator_haptic *haptic = platform_get_drvdata(pdev); 211d64cb71bSJaewon Kim int error; 212d64cb71bSJaewon Kim 213d64cb71bSJaewon Kim error = mutex_lock_interruptible(&haptic->mutex); 214d64cb71bSJaewon Kim if (error) 215d64cb71bSJaewon Kim return error; 216d64cb71bSJaewon Kim 217d64cb71bSJaewon Kim regulator_haptic_set_voltage(haptic, 0); 218d64cb71bSJaewon Kim 219d64cb71bSJaewon Kim haptic->suspended = true; 220d64cb71bSJaewon Kim 221d64cb71bSJaewon Kim mutex_unlock(&haptic->mutex); 222d64cb71bSJaewon Kim 223d64cb71bSJaewon Kim return 0; 224d64cb71bSJaewon Kim } 225d64cb71bSJaewon Kim 226d64cb71bSJaewon Kim static int __maybe_unused regulator_haptic_resume(struct device *dev) 227d64cb71bSJaewon Kim { 228d64cb71bSJaewon Kim struct platform_device *pdev = to_platform_device(dev); 229d64cb71bSJaewon Kim struct regulator_haptic *haptic = platform_get_drvdata(pdev); 230d64cb71bSJaewon Kim unsigned int magnitude; 231d64cb71bSJaewon Kim 232d64cb71bSJaewon Kim mutex_lock(&haptic->mutex); 233d64cb71bSJaewon Kim 234d64cb71bSJaewon Kim haptic->suspended = false; 235d64cb71bSJaewon Kim 236*6aa7de05SMark Rutland magnitude = READ_ONCE(haptic->magnitude); 2373cb6dcfaSAxel Lin if (magnitude) 238d64cb71bSJaewon Kim regulator_haptic_set_voltage(haptic, magnitude); 239d64cb71bSJaewon Kim 240d64cb71bSJaewon Kim mutex_unlock(&haptic->mutex); 241d64cb71bSJaewon Kim 242d64cb71bSJaewon Kim return 0; 243d64cb71bSJaewon Kim } 244d64cb71bSJaewon Kim 245d64cb71bSJaewon Kim static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, 246d64cb71bSJaewon Kim regulator_haptic_suspend, regulator_haptic_resume); 247d64cb71bSJaewon Kim 248245165deSFabian Frederick static const struct of_device_id regulator_haptic_dt_match[] = { 249d64cb71bSJaewon Kim { .compatible = "regulator-haptic" }, 250d64cb71bSJaewon Kim { /* sentinel */ }, 251d64cb71bSJaewon Kim }; 25273185771SLuis de Bethencourt MODULE_DEVICE_TABLE(of, regulator_haptic_dt_match); 253d64cb71bSJaewon Kim 254d64cb71bSJaewon Kim static struct platform_driver regulator_haptic_driver = { 255d64cb71bSJaewon Kim .probe = regulator_haptic_probe, 256d64cb71bSJaewon Kim .driver = { 257d64cb71bSJaewon Kim .name = "regulator-haptic", 258d64cb71bSJaewon Kim .of_match_table = regulator_haptic_dt_match, 259d64cb71bSJaewon Kim .pm = ®ulator_haptic_pm_ops, 260d64cb71bSJaewon Kim }, 261d64cb71bSJaewon Kim }; 262d64cb71bSJaewon Kim module_platform_driver(regulator_haptic_driver); 263d64cb71bSJaewon Kim 264d64cb71bSJaewon Kim MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); 265d64cb71bSJaewon Kim MODULE_AUTHOR("Hyunhee Kim <hyunhee.kim@samsung.com>"); 266d64cb71bSJaewon Kim MODULE_DESCRIPTION("Regulator haptic driver"); 267d64cb71bSJaewon Kim MODULE_LICENSE("GPL"); 268