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        struct snd_soc_component *component =
  41                snd_soc_dapm_to_component(arizona->dapm);
  42        int ret;
  43
  44        if (!haptics->arizona->dapm) {
  45                dev_err(arizona->dev, "No DAPM context\n");
  46                return;
  47        }
  48
  49        if (haptics->intensity) {
  50                ret = regmap_update_bits(arizona->regmap,
  51                                         ARIZONA_HAPTICS_PHASE_2_INTENSITY,
  52                                         ARIZONA_PHASE2_INTENSITY_MASK,
  53                                         haptics->intensity);
  54                if (ret != 0) {
  55                        dev_err(arizona->dev, "Failed to set intensity: %d\n",
  56                                ret);
  57                        return;
  58                }
  59
  60                /* This enable sequence will be a noop if already enabled */
  61                ret = regmap_update_bits(arizona->regmap,
  62                                         ARIZONA_HAPTICS_CONTROL_1,
  63                                         ARIZONA_HAP_CTRL_MASK,
  64                                         1 << ARIZONA_HAP_CTRL_SHIFT);
  65                if (ret != 0) {
  66                        dev_err(arizona->dev, "Failed to start haptics: %d\n",
  67                                ret);
  68                        return;
  69                }
  70
  71                ret = snd_soc_component_enable_pin(component, "HAPTICS");
  72                if (ret != 0) {
  73                        dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
  74                                ret);
  75                        return;
  76                }
  77
  78                ret = snd_soc_dapm_sync(arizona->dapm);
  79                if (ret != 0) {
  80                        dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
  81                                ret);
  82                        return;
  83                }
  84        } else {
  85                /* This disable sequence will be a noop if already enabled */
  86                ret = snd_soc_component_disable_pin(component, "HAPTICS");
  87                if (ret != 0) {
  88                        dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
  89                                ret);
  90                        return;
  91                }
  92
  93                ret = snd_soc_dapm_sync(arizona->dapm);
  94                if (ret != 0) {
  95                        dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
  96                                ret);
  97                        return;
  98                }
  99
 100                ret = regmap_update_bits(arizona->regmap,
 101                                         ARIZONA_HAPTICS_CONTROL_1,
 102                                         ARIZONA_HAP_CTRL_MASK, 0);
 103                if (ret != 0) {
 104                        dev_err(arizona->dev, "Failed to stop haptics: %d\n",
 105                                ret);
 106                        return;
 107                }
 108        }
 109}
 110
 111static int arizona_haptics_play(struct input_dev *input, void *data,
 112                                struct ff_effect *effect)
 113{
 114        struct arizona_haptics *haptics = input_get_drvdata(input);
 115        struct arizona *arizona = haptics->arizona;
 116
 117        if (!arizona->dapm) {
 118                dev_err(arizona->dev, "No DAPM context\n");
 119                return -EBUSY;
 120        }
 121
 122        if (effect->u.rumble.strong_magnitude) {
 123                /* Scale the magnitude into the range the device supports */
 124                if (arizona->pdata.hap_act) {
 125                        haptics->intensity =
 126                                effect->u.rumble.strong_magnitude >> 9;
 127                        if (effect->direction < 0x8000)
 128                                haptics->intensity += 0x7f;
 129                } else {
 130                        haptics->intensity =
 131                                effect->u.rumble.strong_magnitude >> 8;
 132                }
 133        } else {
 134                haptics->intensity = 0;
 135        }
 136
 137        schedule_work(&haptics->work);
 138
 139        return 0;
 140}
 141
 142static void arizona_haptics_close(struct input_dev *input)
 143{
 144        struct arizona_haptics *haptics = input_get_drvdata(input);
 145        struct snd_soc_component *component;
 146
 147        cancel_work_sync(&haptics->work);
 148
 149        if (haptics->arizona->dapm) {
 150                component = snd_soc_dapm_to_component(haptics->arizona->dapm);
 151                snd_soc_component_disable_pin(component, "HAPTICS");
 152        }
 153}
 154
 155static int arizona_haptics_probe(struct platform_device *pdev)
 156{
 157        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
 158        struct arizona_haptics *haptics;
 159        int ret;
 160
 161        haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
 162        if (!haptics)
 163                return -ENOMEM;
 164
 165        haptics->arizona = arizona;
 166
 167        ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
 168                                 ARIZONA_HAP_ACT, arizona->pdata.hap_act);
 169        if (ret != 0) {
 170                dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
 171                        ret);
 172                return ret;
 173        }
 174
 175        INIT_WORK(&haptics->work, arizona_haptics_work);
 176
 177        haptics->input_dev = devm_input_allocate_device(&pdev->dev);
 178        if (!haptics->input_dev) {
 179                dev_err(arizona->dev, "Failed to allocate input device\n");
 180                return -ENOMEM;
 181        }
 182
 183        input_set_drvdata(haptics->input_dev, haptics);
 184
 185        haptics->input_dev->name = "arizona:haptics";
 186        haptics->input_dev->close = arizona_haptics_close;
 187        __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
 188
 189        ret = input_ff_create_memless(haptics->input_dev, NULL,
 190                                      arizona_haptics_play);
 191        if (ret < 0) {
 192                dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
 193                        ret);
 194                return ret;
 195        }
 196
 197        ret = input_register_device(haptics->input_dev);
 198        if (ret < 0) {
 199                dev_err(arizona->dev, "couldn't register input device: %d\n",
 200                        ret);
 201                return ret;
 202        }
 203
 204        return 0;
 205}
 206
 207static struct platform_driver arizona_haptics_driver = {
 208        .probe          = arizona_haptics_probe,
 209        .driver         = {
 210                .name   = "arizona-haptics",
 211        },
 212};
 213module_platform_driver(arizona_haptics_driver);
 214
 215MODULE_ALIAS("platform:arizona-haptics");
 216MODULE_DESCRIPTION("Arizona haptics driver");
 217MODULE_LICENSE("GPL");
 218MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
 219