linux/drivers/input/misc/twl4030-vibra.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * twl4030-vibra.c - TWL4030 Vibrator driver
   4 *
   5 * Copyright (C) 2008-2010 Nokia Corporation
   6 *
   7 * Written by Henrik Saari <henrik.saari@nokia.com>
   8 * Updates by Felipe Balbi <felipe.balbi@nokia.com>
   9 * Input by Jari Vanhala <ext-jari.vanhala@nokia.com>
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/jiffies.h>
  14#include <linux/platform_device.h>
  15#include <linux/of.h>
  16#include <linux/workqueue.h>
  17#include <linux/mfd/twl.h>
  18#include <linux/mfd/twl4030-audio.h>
  19#include <linux/input.h>
  20#include <linux/slab.h>
  21
  22/* MODULE ID2 */
  23#define LEDEN           0x00
  24
  25/* ForceFeedback */
  26#define EFFECT_DIR_180_DEG      0x8000 /* range is 0 - 0xFFFF */
  27
  28struct vibra_info {
  29        struct device           *dev;
  30        struct input_dev        *input_dev;
  31
  32        struct work_struct      play_work;
  33
  34        bool                    enabled;
  35        int                     speed;
  36        int                     direction;
  37
  38        bool                    coexist;
  39};
  40
  41static void vibra_disable_leds(void)
  42{
  43        u8 reg;
  44
  45        /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */
  46        twl_i2c_read_u8(TWL4030_MODULE_LED, &reg, LEDEN);
  47        reg &= ~0x03;
  48        twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg);
  49}
  50
  51/* Powers H-Bridge and enables audio clk */
  52static void vibra_enable(struct vibra_info *info)
  53{
  54        u8 reg;
  55
  56        twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
  57
  58        /* turn H-Bridge on */
  59        twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
  60                        &reg, TWL4030_REG_VIBRA_CTL);
  61        twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
  62                         (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
  63
  64        twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
  65
  66        info->enabled = true;
  67}
  68
  69static void vibra_disable(struct vibra_info *info)
  70{
  71        u8 reg;
  72
  73        /* Power down H-Bridge */
  74        twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
  75                        &reg, TWL4030_REG_VIBRA_CTL);
  76        twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
  77                         (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
  78
  79        twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
  80        twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
  81
  82        info->enabled = false;
  83}
  84
  85static void vibra_play_work(struct work_struct *work)
  86{
  87        struct vibra_info *info = container_of(work,
  88                        struct vibra_info, play_work);
  89        int dir;
  90        int pwm;
  91        u8 reg;
  92
  93        dir = info->direction;
  94        pwm = info->speed;
  95
  96        twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
  97                        &reg, TWL4030_REG_VIBRA_CTL);
  98        if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) {
  99
 100                if (!info->enabled)
 101                        vibra_enable(info);
 102
 103                /* set vibra rotation direction */
 104                twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
 105                                &reg, TWL4030_REG_VIBRA_CTL);
 106                reg = (dir) ? (reg | TWL4030_VIBRA_DIR) :
 107                        (reg & ~TWL4030_VIBRA_DIR);
 108                twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
 109                                 reg, TWL4030_REG_VIBRA_CTL);
 110
 111                /* set PWM, 1 = max, 255 = min */
 112                twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
 113                                 256 - pwm, TWL4030_REG_VIBRA_SET);
 114        } else {
 115                if (info->enabled)
 116                        vibra_disable(info);
 117        }
 118}
 119
 120/*** Input/ForceFeedback ***/
 121
 122static int vibra_play(struct input_dev *input, void *data,
 123                      struct ff_effect *effect)
 124{
 125        struct vibra_info *info = input_get_drvdata(input);
 126
 127        info->speed = effect->u.rumble.strong_magnitude >> 8;
 128        if (!info->speed)
 129                info->speed = effect->u.rumble.weak_magnitude >> 9;
 130        info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
 131        schedule_work(&info->play_work);
 132        return 0;
 133}
 134
 135static void twl4030_vibra_close(struct input_dev *input)
 136{
 137        struct vibra_info *info = input_get_drvdata(input);
 138
 139        cancel_work_sync(&info->play_work);
 140
 141        if (info->enabled)
 142                vibra_disable(info);
 143}
 144
 145/*** Module ***/
 146static int __maybe_unused twl4030_vibra_suspend(struct device *dev)
 147{
 148        struct platform_device *pdev = to_platform_device(dev);
 149        struct vibra_info *info = platform_get_drvdata(pdev);
 150
 151        if (info->enabled)
 152                vibra_disable(info);
 153
 154        return 0;
 155}
 156
 157static int __maybe_unused twl4030_vibra_resume(struct device *dev)
 158{
 159        vibra_disable_leds();
 160        return 0;
 161}
 162
 163static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
 164                         twl4030_vibra_suspend, twl4030_vibra_resume);
 165
 166static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
 167                              struct device_node *parent)
 168{
 169        struct device_node *node;
 170
 171        if (pdata && pdata->coexist)
 172                return true;
 173
 174        node = of_get_child_by_name(parent, "codec");
 175        if (node) {
 176                of_node_put(node);
 177                return true;
 178        }
 179
 180        return false;
 181}
 182
 183static int twl4030_vibra_probe(struct platform_device *pdev)
 184{
 185        struct twl4030_vibra_data *pdata = dev_get_platdata(&pdev->dev);
 186        struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
 187        struct vibra_info *info;
 188        int ret;
 189
 190        if (!pdata && !twl4030_core_node) {
 191                dev_dbg(&pdev->dev, "platform_data not available\n");
 192                return -EINVAL;
 193        }
 194
 195        info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
 196        if (!info)
 197                return -ENOMEM;
 198
 199        info->dev = &pdev->dev;
 200        info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
 201        INIT_WORK(&info->play_work, vibra_play_work);
 202
 203        info->input_dev = devm_input_allocate_device(&pdev->dev);
 204        if (info->input_dev == NULL) {
 205                dev_err(&pdev->dev, "couldn't allocate input device\n");
 206                return -ENOMEM;
 207        }
 208
 209        input_set_drvdata(info->input_dev, info);
 210
 211        info->input_dev->name = "twl4030:vibrator";
 212        info->input_dev->id.version = 1;
 213        info->input_dev->close = twl4030_vibra_close;
 214        __set_bit(FF_RUMBLE, info->input_dev->ffbit);
 215
 216        ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
 217        if (ret < 0) {
 218                dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
 219                return ret;
 220        }
 221
 222        ret = input_register_device(info->input_dev);
 223        if (ret < 0) {
 224                dev_dbg(&pdev->dev, "couldn't register input device\n");
 225                goto err_iff;
 226        }
 227
 228        vibra_disable_leds();
 229
 230        platform_set_drvdata(pdev, info);
 231        return 0;
 232
 233err_iff:
 234        input_ff_destroy(info->input_dev);
 235        return ret;
 236}
 237
 238static struct platform_driver twl4030_vibra_driver = {
 239        .probe          = twl4030_vibra_probe,
 240        .driver         = {
 241                .name   = "twl4030-vibra",
 242                .pm     = &twl4030_vibra_pm_ops,
 243        },
 244};
 245module_platform_driver(twl4030_vibra_driver);
 246
 247MODULE_ALIAS("platform:twl4030-vibra");
 248MODULE_DESCRIPTION("TWL4030 Vibra driver");
 249MODULE_LICENSE("GPL");
 250MODULE_AUTHOR("Nokia Corporation");
 251