Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: add new vibrator driver for various MSM SOCs

This patch adds a new vibrator driver that supports various Qualcomm
MSM SOCs. Driver was tested on a LG Nexus 5 (hammerhead) phone.

Signed-off-by: Brian Masney <masneyb@onstation.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Brian Masney and committed by
Dmitry Torokhov
0f681d09 a5c5e50c

+329
+36
Documentation/devicetree/bindings/input/msm-vibrator.txt
··· 1 + * Device tree bindings for the Qualcomm MSM vibrator 2 + 3 + Required properties: 4 + 5 + - compatible: Should be one of 6 + "qcom,msm8226-vibrator" 7 + "qcom,msm8974-vibrator" 8 + - reg: the base address and length of the IO memory for the registers. 9 + - pinctrl-names: set to default. 10 + - pinctrl-0: phandles pointing to pin configuration nodes. See 11 + Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt 12 + - clock-names: set to pwm 13 + - clocks: phandle of the clock. See 14 + Documentation/devicetree/bindings/clock/clock-bindings.txt 15 + - enable-gpios: GPIO that enables the vibrator. 16 + 17 + Optional properties: 18 + 19 + - vcc-supply: phandle to the regulator that provides power to the sensor. 20 + 21 + Example from a LG Nexus 5 (hammerhead) phone: 22 + 23 + vibrator@fd8c3450 { 24 + reg = <0xfd8c3450 0x400>; 25 + compatible = "qcom,msm8974-vibrator"; 26 + 27 + vcc-supply = <&pm8941_l19>; 28 + 29 + clocks = <&mmcc CAMSS_GP1_CLK>; 30 + clock-names = "pwm"; 31 + 32 + enable-gpios = <&msmgpio 60 GPIO_ACTIVE_HIGH>; 33 + 34 + pinctrl-names = "default"; 35 + pinctrl-0 = <&vibrator_pin>; 36 + };
+10
drivers/input/misc/Kconfig
··· 117 117 To compile this driver as a module, choose M here: the 118 118 module will be called e3x0_button. 119 119 120 + config INPUT_MSM_VIBRATOR 121 + tristate "Qualcomm MSM vibrator driver" 122 + select INPUT_FF_MEMLESS 123 + help 124 + Support for the vibrator that is found on various Qualcomm MSM 125 + SOCs. 126 + 127 + To compile this driver as a module, choose M here: the module 128 + will be called msm_vibrator. 129 + 120 130 config INPUT_PCSPKR 121 131 tristate "PC Speaker support" 122 132 depends on PCSPKR_PLATFORM
+1
drivers/input/misc/Makefile
··· 48 48 obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o 49 49 obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o 50 50 obj-$(CONFIG_INPUT_MMA8450) += mma8450.o 51 + obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o 51 52 obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o 52 53 obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o 53 54 obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+282
drivers/input/misc/msm-vibrator.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Qualcomm MSM vibrator driver 4 + * 5 + * Copyright (c) 2018 Brian Masney <masneyb@onstation.org> 6 + * 7 + * Based on qcom,pwm-vibrator.c from: 8 + * Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca> 9 + * 10 + * Based on msm_pwm_vibrator.c from downstream Android sources: 11 + * Copyright (C) 2009-2014 LGE, Inc. 12 + */ 13 + 14 + #include <linux/clk.h> 15 + #include <linux/err.h> 16 + #include <linux/gpio.h> 17 + #include <linux/input.h> 18 + #include <linux/io.h> 19 + #include <linux/module.h> 20 + #include <linux/of.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/regulator/consumer.h> 23 + 24 + #define REG_CMD_RCGR 0x00 25 + #define REG_CFG_RCGR 0x04 26 + #define REG_M 0x08 27 + #define REG_N 0x0C 28 + #define REG_D 0x10 29 + #define REG_CBCR 0x24 30 + #define MMSS_CC_M_DEFAULT 1 31 + 32 + struct msm_vibrator { 33 + struct input_dev *input; 34 + struct mutex mutex; 35 + struct work_struct worker; 36 + void __iomem *base; 37 + struct regulator *vcc; 38 + struct clk *clk; 39 + struct gpio_desc *enable_gpio; 40 + u16 magnitude; 41 + bool enabled; 42 + }; 43 + 44 + static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset, 45 + u32 value) 46 + { 47 + writel(value, vibrator->base + offset); 48 + } 49 + 50 + static int msm_vibrator_start(struct msm_vibrator *vibrator) 51 + { 52 + int d_reg_val, ret = 0; 53 + 54 + mutex_lock(&vibrator->mutex); 55 + 56 + if (!vibrator->enabled) { 57 + ret = clk_set_rate(vibrator->clk, 24000); 58 + if (ret) { 59 + dev_err(&vibrator->input->dev, 60 + "Failed to set clock rate: %d\n", ret); 61 + goto unlock; 62 + } 63 + 64 + ret = clk_prepare_enable(vibrator->clk); 65 + if (ret) { 66 + dev_err(&vibrator->input->dev, 67 + "Failed to enable clock: %d\n", ret); 68 + goto unlock; 69 + } 70 + 71 + ret = regulator_enable(vibrator->vcc); 72 + if (ret) { 73 + dev_err(&vibrator->input->dev, 74 + "Failed to enable regulator: %d\n", ret); 75 + clk_disable(vibrator->clk); 76 + goto unlock; 77 + } 78 + 79 + gpiod_set_value_cansleep(vibrator->enable_gpio, 1); 80 + 81 + vibrator->enabled = true; 82 + } 83 + 84 + d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff); 85 + msm_vibrator_write(vibrator, REG_CFG_RCGR, 86 + (2 << 12) | /* dual edge mode */ 87 + (0 << 8) | /* cxo */ 88 + (7 << 0)); 89 + msm_vibrator_write(vibrator, REG_M, 1); 90 + msm_vibrator_write(vibrator, REG_N, 128); 91 + msm_vibrator_write(vibrator, REG_D, d_reg_val); 92 + msm_vibrator_write(vibrator, REG_CMD_RCGR, 1); 93 + msm_vibrator_write(vibrator, REG_CBCR, 1); 94 + 95 + unlock: 96 + mutex_unlock(&vibrator->mutex); 97 + 98 + return ret; 99 + } 100 + 101 + static void msm_vibrator_stop(struct msm_vibrator *vibrator) 102 + { 103 + mutex_lock(&vibrator->mutex); 104 + 105 + if (vibrator->enabled) { 106 + gpiod_set_value_cansleep(vibrator->enable_gpio, 0); 107 + regulator_disable(vibrator->vcc); 108 + clk_disable(vibrator->clk); 109 + vibrator->enabled = false; 110 + } 111 + 112 + mutex_unlock(&vibrator->mutex); 113 + } 114 + 115 + static void msm_vibrator_worker(struct work_struct *work) 116 + { 117 + struct msm_vibrator *vibrator = container_of(work, 118 + struct msm_vibrator, 119 + worker); 120 + 121 + if (vibrator->magnitude) 122 + msm_vibrator_start(vibrator); 123 + else 124 + msm_vibrator_stop(vibrator); 125 + } 126 + 127 + static int msm_vibrator_play_effect(struct input_dev *dev, void *data, 128 + struct ff_effect *effect) 129 + { 130 + struct msm_vibrator *vibrator = input_get_drvdata(dev); 131 + 132 + mutex_lock(&vibrator->mutex); 133 + 134 + if (effect->u.rumble.strong_magnitude > 0) 135 + vibrator->magnitude = effect->u.rumble.strong_magnitude; 136 + else 137 + vibrator->magnitude = effect->u.rumble.weak_magnitude; 138 + 139 + mutex_unlock(&vibrator->mutex); 140 + 141 + schedule_work(&vibrator->worker); 142 + 143 + return 0; 144 + } 145 + 146 + static void msm_vibrator_close(struct input_dev *input) 147 + { 148 + struct msm_vibrator *vibrator = input_get_drvdata(input); 149 + 150 + cancel_work_sync(&vibrator->worker); 151 + msm_vibrator_stop(vibrator); 152 + } 153 + 154 + static int msm_vibrator_probe(struct platform_device *pdev) 155 + { 156 + struct msm_vibrator *vibrator; 157 + struct resource *res; 158 + int ret; 159 + 160 + vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL); 161 + if (!vibrator) 162 + return -ENOMEM; 163 + 164 + vibrator->input = devm_input_allocate_device(&pdev->dev); 165 + if (!vibrator->input) 166 + return -ENOMEM; 167 + 168 + vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc"); 169 + if (IS_ERR(vibrator->vcc)) { 170 + if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER) 171 + dev_err(&pdev->dev, "Failed to get regulator: %ld\n", 172 + PTR_ERR(vibrator->vcc)); 173 + return PTR_ERR(vibrator->vcc); 174 + } 175 + 176 + vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable", 177 + GPIOD_OUT_LOW); 178 + if (IS_ERR(vibrator->enable_gpio)) { 179 + if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER) 180 + dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n", 181 + PTR_ERR(vibrator->enable_gpio)); 182 + return PTR_ERR(vibrator->enable_gpio); 183 + } 184 + 185 + vibrator->clk = devm_clk_get(&pdev->dev, "pwm"); 186 + if (IS_ERR(vibrator->clk)) { 187 + if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER) 188 + dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n", 189 + PTR_ERR(vibrator->clk)); 190 + return PTR_ERR(vibrator->clk); 191 + } 192 + 193 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 194 + if (!res) { 195 + dev_err(&pdev->dev, "Failed to get platform resource\n"); 196 + return -ENODEV; 197 + } 198 + 199 + vibrator->base = devm_ioremap(&pdev->dev, res->start, 200 + resource_size(res)); 201 + if (!vibrator->base) { 202 + dev_err(&pdev->dev, "Failed to iomap resource: %ld\n", 203 + PTR_ERR(vibrator->base)); 204 + return -ENOMEM; 205 + } 206 + 207 + vibrator->enabled = false; 208 + mutex_init(&vibrator->mutex); 209 + INIT_WORK(&vibrator->worker, msm_vibrator_worker); 210 + 211 + vibrator->input->name = "msm-vibrator"; 212 + vibrator->input->id.bustype = BUS_HOST; 213 + vibrator->input->close = msm_vibrator_close; 214 + 215 + input_set_drvdata(vibrator->input, vibrator); 216 + input_set_capability(vibrator->input, EV_FF, FF_RUMBLE); 217 + 218 + ret = input_ff_create_memless(vibrator->input, NULL, 219 + msm_vibrator_play_effect); 220 + if (ret) { 221 + dev_err(&pdev->dev, "Failed to create ff memless: %d", ret); 222 + return ret; 223 + } 224 + 225 + ret = input_register_device(vibrator->input); 226 + if (ret) { 227 + dev_err(&pdev->dev, "Failed to register input device: %d", ret); 228 + return ret; 229 + } 230 + 231 + platform_set_drvdata(pdev, vibrator); 232 + 233 + return 0; 234 + } 235 + 236 + static int __maybe_unused msm_vibrator_suspend(struct device *dev) 237 + { 238 + struct platform_device *pdev = to_platform_device(dev); 239 + struct msm_vibrator *vibrator = platform_get_drvdata(pdev); 240 + 241 + cancel_work_sync(&vibrator->worker); 242 + 243 + if (vibrator->enabled) 244 + msm_vibrator_stop(vibrator); 245 + 246 + return 0; 247 + } 248 + 249 + static int __maybe_unused msm_vibrator_resume(struct device *dev) 250 + { 251 + struct platform_device *pdev = to_platform_device(dev); 252 + struct msm_vibrator *vibrator = platform_get_drvdata(pdev); 253 + 254 + if (vibrator->enabled) 255 + msm_vibrator_start(vibrator); 256 + 257 + return 0; 258 + } 259 + 260 + static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend, 261 + msm_vibrator_resume); 262 + 263 + static const struct of_device_id msm_vibrator_of_match[] = { 264 + { .compatible = "qcom,msm8226-vibrator" }, 265 + { .compatible = "qcom,msm8974-vibrator" }, 266 + {}, 267 + }; 268 + MODULE_DEVICE_TABLE(of, msm_vibrator_of_match); 269 + 270 + static struct platform_driver msm_vibrator_driver = { 271 + .probe = msm_vibrator_probe, 272 + .driver = { 273 + .name = "msm-vibrator", 274 + .pm = &msm_vibrator_pm_ops, 275 + .of_match_table = of_match_ptr(msm_vibrator_of_match), 276 + }, 277 + }; 278 + module_platform_driver(msm_vibrator_driver); 279 + 280 + MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>"); 281 + MODULE_DESCRIPTION("Qualcomm MSM vibrator driver"); 282 + MODULE_LICENSE("GPL");