Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Arizona haptics driver
0004  *
0005  * Copyright 2012 Wolfson Microelectronics plc
0006  *
0007  * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
0008  */
0009 
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/input.h>
0013 #include <linux/slab.h>
0014 
0015 #include <sound/soc.h>
0016 #include <sound/soc-dapm.h>
0017 
0018 #include <linux/mfd/arizona/core.h>
0019 #include <linux/mfd/arizona/pdata.h>
0020 #include <linux/mfd/arizona/registers.h>
0021 
0022 struct arizona_haptics {
0023     struct arizona *arizona;
0024     struct input_dev *input_dev;
0025     struct work_struct work;
0026 
0027     struct mutex mutex;
0028     u8 intensity;
0029 };
0030 
0031 static void arizona_haptics_work(struct work_struct *work)
0032 {
0033     struct arizona_haptics *haptics = container_of(work,
0034                                struct arizona_haptics,
0035                                work);
0036     struct arizona *arizona = haptics->arizona;
0037     struct snd_soc_component *component =
0038         snd_soc_dapm_to_component(arizona->dapm);
0039     int ret;
0040 
0041     if (!haptics->arizona->dapm) {
0042         dev_err(arizona->dev, "No DAPM context\n");
0043         return;
0044     }
0045 
0046     if (haptics->intensity) {
0047         ret = regmap_update_bits(arizona->regmap,
0048                      ARIZONA_HAPTICS_PHASE_2_INTENSITY,
0049                      ARIZONA_PHASE2_INTENSITY_MASK,
0050                      haptics->intensity);
0051         if (ret != 0) {
0052             dev_err(arizona->dev, "Failed to set intensity: %d\n",
0053                 ret);
0054             return;
0055         }
0056 
0057         /* This enable sequence will be a noop if already enabled */
0058         ret = regmap_update_bits(arizona->regmap,
0059                      ARIZONA_HAPTICS_CONTROL_1,
0060                      ARIZONA_HAP_CTRL_MASK,
0061                      1 << ARIZONA_HAP_CTRL_SHIFT);
0062         if (ret != 0) {
0063             dev_err(arizona->dev, "Failed to start haptics: %d\n",
0064                 ret);
0065             return;
0066         }
0067 
0068         ret = snd_soc_component_enable_pin(component, "HAPTICS");
0069         if (ret != 0) {
0070             dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
0071                 ret);
0072             return;
0073         }
0074 
0075         ret = snd_soc_dapm_sync(arizona->dapm);
0076         if (ret != 0) {
0077             dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
0078                 ret);
0079             return;
0080         }
0081     } else {
0082         /* This disable sequence will be a noop if already enabled */
0083         ret = snd_soc_component_disable_pin(component, "HAPTICS");
0084         if (ret != 0) {
0085             dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
0086                 ret);
0087             return;
0088         }
0089 
0090         ret = snd_soc_dapm_sync(arizona->dapm);
0091         if (ret != 0) {
0092             dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
0093                 ret);
0094             return;
0095         }
0096 
0097         ret = regmap_update_bits(arizona->regmap,
0098                      ARIZONA_HAPTICS_CONTROL_1,
0099                      ARIZONA_HAP_CTRL_MASK, 0);
0100         if (ret != 0) {
0101             dev_err(arizona->dev, "Failed to stop haptics: %d\n",
0102                 ret);
0103             return;
0104         }
0105     }
0106 }
0107 
0108 static int arizona_haptics_play(struct input_dev *input, void *data,
0109                 struct ff_effect *effect)
0110 {
0111     struct arizona_haptics *haptics = input_get_drvdata(input);
0112     struct arizona *arizona = haptics->arizona;
0113 
0114     if (!arizona->dapm) {
0115         dev_err(arizona->dev, "No DAPM context\n");
0116         return -EBUSY;
0117     }
0118 
0119     if (effect->u.rumble.strong_magnitude) {
0120         /* Scale the magnitude into the range the device supports */
0121         if (arizona->pdata.hap_act) {
0122             haptics->intensity =
0123                 effect->u.rumble.strong_magnitude >> 9;
0124             if (effect->direction < 0x8000)
0125                 haptics->intensity += 0x7f;
0126         } else {
0127             haptics->intensity =
0128                 effect->u.rumble.strong_magnitude >> 8;
0129         }
0130     } else {
0131         haptics->intensity = 0;
0132     }
0133 
0134     schedule_work(&haptics->work);
0135 
0136     return 0;
0137 }
0138 
0139 static void arizona_haptics_close(struct input_dev *input)
0140 {
0141     struct arizona_haptics *haptics = input_get_drvdata(input);
0142     struct snd_soc_component *component;
0143 
0144     cancel_work_sync(&haptics->work);
0145 
0146     if (haptics->arizona->dapm) {
0147         component = snd_soc_dapm_to_component(haptics->arizona->dapm);
0148         snd_soc_component_disable_pin(component, "HAPTICS");
0149     }
0150 }
0151 
0152 static int arizona_haptics_probe(struct platform_device *pdev)
0153 {
0154     struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
0155     struct arizona_haptics *haptics;
0156     int ret;
0157 
0158     haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
0159     if (!haptics)
0160         return -ENOMEM;
0161 
0162     haptics->arizona = arizona;
0163 
0164     ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
0165                  ARIZONA_HAP_ACT, arizona->pdata.hap_act);
0166     if (ret != 0) {
0167         dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
0168             ret);
0169         return ret;
0170     }
0171 
0172     INIT_WORK(&haptics->work, arizona_haptics_work);
0173 
0174     haptics->input_dev = devm_input_allocate_device(&pdev->dev);
0175     if (!haptics->input_dev) {
0176         dev_err(arizona->dev, "Failed to allocate input device\n");
0177         return -ENOMEM;
0178     }
0179 
0180     input_set_drvdata(haptics->input_dev, haptics);
0181 
0182     haptics->input_dev->name = "arizona:haptics";
0183     haptics->input_dev->close = arizona_haptics_close;
0184     __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
0185 
0186     ret = input_ff_create_memless(haptics->input_dev, NULL,
0187                       arizona_haptics_play);
0188     if (ret < 0) {
0189         dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
0190             ret);
0191         return ret;
0192     }
0193 
0194     ret = input_register_device(haptics->input_dev);
0195     if (ret < 0) {
0196         dev_err(arizona->dev, "couldn't register input device: %d\n",
0197             ret);
0198         return ret;
0199     }
0200 
0201     return 0;
0202 }
0203 
0204 static struct platform_driver arizona_haptics_driver = {
0205     .probe      = arizona_haptics_probe,
0206     .driver     = {
0207         .name   = "arizona-haptics",
0208     },
0209 };
0210 module_platform_driver(arizona_haptics_driver);
0211 
0212 MODULE_ALIAS("platform:arizona-haptics");
0213 MODULE_DESCRIPTION("Arizona haptics driver");
0214 MODULE_LICENSE("GPL");
0215 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");