linux/drivers/input/misc/arizona-haptics.c
<<
>>
Prefs
   1/*
   2 * Arizona haptics driver
   3 *
   4 * Copyright 2012 Wolfson Microelectronics plc
   5 *
   6 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License version 2 as
  10 * published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/platform_device.h>
  15#include <linux/input.h>
  16#include <linux/slab.h>
  17
  18#include <sound/soc.h>
  19#include <sound/soc-dapm.h>
  20
  21#include <linux/mfd/arizona/core.h>
  22#include <linux/mfd/arizona/pdata.h>
  23#include <linux/mfd/arizona/registers.h>
  24
  25struct arizona_haptics {
  26        struct arizona *arizona;
  27        struct input_dev *input_dev;
  28        struct work_struct work;
  29
  30        struct mutex mutex;
  31        u8 intensity;
  32};
  33
  34static void arizona_haptics_work(struct work_struct *work)
  35{
  36        struct arizona_haptics *haptics = container_of(work,
  37                                                       struct arizona_haptics,
  38                                                       work);
  39        struct arizona *arizona = haptics->arizona;
  40        int ret;
  41
  42        if (!haptics->arizona->dapm) {
  43                dev_err(arizona->dev, "No DAPM context\n");
  44                return;
  45        }
  46
  47        if (haptics->intensity) {
  48                ret = regmap_update_bits(arizona->regmap,
  49                                         ARIZONA_HAPTICS_PHASE_2_INTENSITY,
  50                                         ARIZONA_PHASE2_INTENSITY_MASK,
  51                                         haptics->intensity);
  52                if (ret != 0) {
  53                        dev_err(arizona->dev, "Failed to set intensity: %d\n",
  54                                ret);
  55                        return;
  56                }
  57
  58                /* This enable sequence will be a noop if already enabled */
  59                ret = regmap_update_bits(arizona->regmap,
  60                                         ARIZONA_HAPTICS_CONTROL_1,
  61                                         ARIZONA_HAP_CTRL_MASK,
  62                                         1 << ARIZONA_HAP_CTRL_SHIFT);
  63                if (ret != 0) {
  64                        dev_err(arizona->dev, "Failed to start haptics: %d\n",
  65                                ret);
  66                        return;
  67                }
  68
  69                ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
  70                if (ret != 0) {
  71                        dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
  72                                ret);
  73                        return;
  74                }
  75
  76                ret = snd_soc_dapm_sync(arizona->dapm);
  77                if (ret != 0) {
  78                        dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
  79                                ret);
  80                        return;
  81                }
  82        } else {
  83                /* This disable sequence will be a noop if already enabled */
  84                ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
  85                if (ret != 0) {
  86                        dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
  87                                ret);
  88                        return;
  89                }
  90
  91                ret = snd_soc_dapm_sync(arizona->dapm);
  92                if (ret != 0) {
  93                        dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
  94                                ret);
  95                        return;
  96                }
  97
  98                ret = regmap_update_bits(arizona->regmap,
  99                                         ARIZONA_HAPTICS_CONTROL_1,
 100                                         ARIZONA_HAP_CTRL_MASK, 0);
 101                if (ret != 0) {
 102                        dev_err(arizona->dev, "Failed to stop haptics: %d\n",
 103                                ret);
 104                        return;
 105                }
 106        }
 107}
 108
 109static int arizona_haptics_play(struct input_dev *input, void *data,
 110                                struct ff_effect *effect)
 111{
 112        struct arizona_haptics *haptics = input_get_drvdata(input);
 113        struct arizona *arizona = haptics->arizona;
 114
 115        if (!arizona->dapm) {
 116                dev_err(arizona->dev, "No DAPM context\n");
 117                return -EBUSY;
 118        }
 119
 120        if (effect->u.rumble.strong_magnitude) {
 121                /* Scale the magnitude into the range the device supports */
 122                if (arizona->pdata.hap_act) {
 123                        haptics->intensity =
 124                                effect->u.rumble.strong_magnitude >> 9;
 125                        if (effect->direction < 0x8000)
 126                                haptics->intensity += 0x7f;
 127                } else {
 128                        haptics->intensity =
 129                                effect->u.rumble.strong_magnitude >> 8;
 130                }
 131        } else {
 132                haptics->intensity = 0;
 133        }
 134
 135        schedule_work(&haptics->work);
 136
 137        return 0;
 138}
 139
 140static void arizona_haptics_close(struct input_dev *input)
 141{
 142        struct arizona_haptics *haptics = input_get_drvdata(input);
 143
 144        cancel_work_sync(&haptics->work);
 145
 146        if (haptics->arizona->dapm)
 147                snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
 148}
 149
 150static int arizona_haptics_probe(struct platform_device *pdev)
 151{
 152        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
 153        struct arizona_haptics *haptics;
 154        int ret;
 155
 156        haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
 157        if (!haptics)
 158                return -ENOMEM;
 159
 160        haptics->arizona = arizona;
 161
 162        ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
 163                                 ARIZONA_HAP_ACT, arizona->pdata.hap_act);
 164        if (ret != 0) {
 165                dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
 166                        ret);
 167                return ret;
 168        }
 169
 170        INIT_WORK(&haptics->work, arizona_haptics_work);
 171
 172        haptics->input_dev = devm_input_allocate_device(&pdev->dev);
 173        if (!haptics->input_dev) {
 174                dev_err(arizona->dev, "Failed to allocate input device\n");
 175                return -ENOMEM;
 176        }
 177
 178        input_set_drvdata(haptics->input_dev, haptics);
 179
 180        haptics->input_dev->name = "arizona:haptics";
 181        haptics->input_dev->close = arizona_haptics_close;
 182        __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
 183
 184        ret = input_ff_create_memless(haptics->input_dev, NULL,
 185                                      arizona_haptics_play);
 186        if (ret < 0) {
 187                dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
 188                        ret);
 189                return ret;
 190        }
 191
 192        ret = input_register_device(haptics->input_dev);
 193        if (ret < 0) {
 194                dev_err(arizona->dev, "couldn't register input device: %d\n",
 195                        ret);
 196                return ret;
 197        }
 198
 199        platform_set_drvdata(pdev, haptics);
 200
 201        return 0;
 202}
 203
 204static struct platform_driver arizona_haptics_driver = {
 205        .probe          = arizona_haptics_probe,
 206        .driver         = {
 207                .name   = "arizona-haptics",
 208        },
 209};
 210module_platform_driver(arizona_haptics_driver);
 211
 212MODULE_ALIAS("platform:arizona-haptics");
 213MODULE_DESCRIPTION("Arizona haptics driver");
 214MODULE_LICENSE("GPL");
 215MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 216