1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2018 Spreadtrum Communications Inc. 4 */ 5 6#include <linux/module.h> 7#include <linux/of_address.h> 8#include <linux/platform_device.h> 9#include <linux/regmap.h> 10#include <linux/input.h> 11#include <linux/workqueue.h> 12 13#define CUR_DRV_CAL_SEL GENMASK(13, 12) 14#define SLP_LDOVIBR_PD_EN BIT(9) 15#define LDO_VIBR_PD BIT(8) 16 17struct vibra_info { 18 struct input_dev *input_dev; 19 struct work_struct play_work; 20 struct regmap *regmap; 21 u32 base; 22 u32 strength; 23 bool enabled; 24}; 25 26static void sc27xx_vibra_set(struct vibra_info *info, bool on) 27{ 28 if (on) { 29 regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, 0); 30 regmap_update_bits(info->regmap, info->base, 31 SLP_LDOVIBR_PD_EN, 0); 32 info->enabled = true; 33 } else { 34 regmap_update_bits(info->regmap, info->base, LDO_VIBR_PD, 35 LDO_VIBR_PD); 36 regmap_update_bits(info->regmap, info->base, 37 SLP_LDOVIBR_PD_EN, SLP_LDOVIBR_PD_EN); 38 info->enabled = false; 39 } 40} 41 42static int sc27xx_vibra_hw_init(struct vibra_info *info) 43{ 44 return regmap_update_bits(info->regmap, info->base, CUR_DRV_CAL_SEL, 0); 45} 46 47static void sc27xx_vibra_play_work(struct work_struct *work) 48{ 49 struct vibra_info *info = container_of(work, struct vibra_info, 50 play_work); 51 52 if (info->strength && !info->enabled) 53 sc27xx_vibra_set(info, true); 54 else if (info->strength == 0 && info->enabled) 55 sc27xx_vibra_set(info, false); 56} 57 58static int sc27xx_vibra_play(struct input_dev *input, void *data, 59 struct ff_effect *effect) 60{ 61 struct vibra_info *info = input_get_drvdata(input); 62 63 info->strength = effect->u.rumble.weak_magnitude; 64 schedule_work(&info->play_work); 65 66 return 0; 67} 68 69static void sc27xx_vibra_close(struct input_dev *input) 70{ 71 struct vibra_info *info = input_get_drvdata(input); 72 73 cancel_work_sync(&info->play_work); 74 if (info->enabled) 75 sc27xx_vibra_set(info, false); 76} 77 78static int sc27xx_vibra_probe(struct platform_device *pdev) 79{ 80 struct vibra_info *info; 81 int error; 82 83 info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); 84 if (!info) 85 return -ENOMEM; 86 87 info->regmap = dev_get_regmap(pdev->dev.parent, NULL); 88 if (!info->regmap) { 89 dev_err(&pdev->dev, "failed to get vibrator regmap.\n"); 90 return -ENODEV; 91 } 92 93 error = device_property_read_u32(&pdev->dev, "reg", &info->base); 94 if (error) { 95 dev_err(&pdev->dev, "failed to get vibrator base address.\n"); 96 return error; 97 } 98 99 info->input_dev = devm_input_allocate_device(&pdev->dev); 100 if (!info->input_dev) { 101 dev_err(&pdev->dev, "failed to allocate input device.\n"); 102 return -ENOMEM; 103 } 104 105 info->input_dev->name = "sc27xx:vibrator"; 106 info->input_dev->id.version = 0; 107 info->input_dev->close = sc27xx_vibra_close; 108 109 input_set_drvdata(info->input_dev, info); 110 input_set_capability(info->input_dev, EV_FF, FF_RUMBLE); 111 INIT_WORK(&info->play_work, sc27xx_vibra_play_work); 112 info->enabled = false; 113 114 error = sc27xx_vibra_hw_init(info); 115 if (error) { 116 dev_err(&pdev->dev, "failed to initialize the vibrator.\n"); 117 return error; 118 } 119 120 error = input_ff_create_memless(info->input_dev, NULL, 121 sc27xx_vibra_play); 122 if (error) { 123 dev_err(&pdev->dev, "failed to register vibrator to FF.\n"); 124 return error; 125 } 126 127 error = input_register_device(info->input_dev); 128 if (error) { 129 dev_err(&pdev->dev, "failed to register input device.\n"); 130 return error; 131 } 132 133 return 0; 134} 135 136static const struct of_device_id sc27xx_vibra_of_match[] = { 137 { .compatible = "sprd,sc2731-vibrator", }, 138 {} 139}; 140MODULE_DEVICE_TABLE(of, sc27xx_vibra_of_match); 141 142static struct platform_driver sc27xx_vibra_driver = { 143 .driver = { 144 .name = "sc27xx-vibrator", 145 .of_match_table = sc27xx_vibra_of_match, 146 }, 147 .probe = sc27xx_vibra_probe, 148}; 149 150module_platform_driver(sc27xx_vibra_driver); 151 152MODULE_DESCRIPTION("Spreadtrum SC27xx Vibrator Driver"); 153MODULE_LICENSE("GPL v2"); 154MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>"); 155