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