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,
 101                                         1 << ARIZONA_HAP_CTRL_SHIFT);
 102                if (ret != 0) {
 103                        dev_err(arizona->dev, "Failed to stop haptics: %d\n",
 104                                ret);
 105                        return;
 106                }
 107        }
 108}
 109
 110static int arizona_haptics_play(struct input_dev *input, void *data,
 111                                struct ff_effect *effect)
 112{
 113        struct arizona_haptics *haptics = input_get_drvdata(input);
 114        struct arizona *arizona = haptics->arizona;
 115
 116        if (!arizona->dapm) {
 117                dev_err(arizona->dev, "No DAPM context\n");
 118                return -EBUSY;
 119        }
 120
 121        if (effect->u.rumble.strong_magnitude) {
 122                /* Scale the magnitude into the range the device supports */
 123                if (arizona->pdata.hap_act) {
 124                        haptics->intensity =
 125                                effect->u.rumble.strong_magnitude >> 9;
 126                        if (effect->direction < 0x8000)
 127                                haptics->intensity += 0x7f;
 128                } else {
 129                        haptics->intensity =
 130                                effect->u.rumble.strong_magnitude >> 8;
 131                }
 132        } else {
 133                haptics->intensity = 0;
 134        }
 135
 136        schedule_work(&haptics->work);
 137
 138        return 0;
 139}
 140
 141static void arizona_haptics_close(struct input_dev *input)
 142{
 143        struct arizona_haptics *haptics = input_get_drvdata(input);
 144
 145        cancel_work_sync(&haptics->work);
 146
 147        if (haptics->arizona->dapm)
 148                snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
 149}
 150
 151static int arizona_haptics_probe(struct platform_device *pdev)
 152{
 153        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
 154        struct arizona_haptics *haptics;
 155        int ret;
 156
 157        haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
 158        if (!haptics)
 159                return -ENOMEM;
 160
 161        haptics->arizona = arizona;
 162
 163        ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
 164                                 ARIZONA_HAP_ACT, arizona->pdata.hap_act);
 165        if (ret != 0) {
 166                dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
 167                        ret);
 168                return ret;
 169        }
 170
 171        INIT_WORK(&haptics->work, arizona_haptics_work);
 172
 173        haptics->input_dev = input_allocate_device();
 174        if (haptics->input_dev == NULL) {
 175                dev_err(arizona->dev, "Failed to allocate input device\n");
 176                return -ENOMEM;
 177        }
 178
 179        input_set_drvdata(haptics->input_dev, haptics);
 180
 181        haptics->input_dev->name = "arizona:haptics";
 182        haptics->input_dev->dev.parent = pdev->dev.parent;
 183        haptics->input_dev->close = arizona_haptics_close;
 184        __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
 185
 186        ret = input_ff_create_memless(haptics->input_dev, NULL,
 187                                      arizona_haptics_play);
 188        if (ret < 0) {
 189                dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
 190                        ret);
 191                goto err_ialloc;
 192        }
 193
 194        ret = input_register_device(haptics->input_dev);
 195        if (ret < 0) {
 196                dev_err(arizona->dev, "couldn't register input device: %d\n",
 197                        ret);
 198                goto err_iff;
 199        }
 200
 201        platform_set_drvdata(pdev, haptics);
 202
 203        return 0;
 204
 205err_iff:
 206        if (haptics->input_dev)
 207                input_ff_destroy(haptics->input_dev);
 208err_ialloc:
 209        input_free_device(haptics->input_dev);
 210
 211        return ret;
 212}
 213
 214static int arizona_haptics_remove(struct platform_device *pdev)
 215{
 216        struct arizona_haptics *haptics = platform_get_drvdata(pdev);
 217
 218        input_unregister_device(haptics->input_dev);
 219
 220        return 0;
 221}
 222
 223static struct platform_driver arizona_haptics_driver = {
 224        .probe          = arizona_haptics_probe,
 225        .remove         = arizona_haptics_remove,
 226        .driver         = {
 227                .name   = "arizona-haptics",
 228        },
 229};
 230module_platform_driver(arizona_haptics_driver);
 231
 232MODULE_ALIAS("platform:arizona-haptics");
 233MODULE_DESCRIPTION("Arizona haptics driver");
 234MODULE_LICENSE("GPL");
 235MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 236