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 17 struct 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 26 static 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 42 static 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 47 static 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 58 static 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 69 static 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 78 static 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 136 static const struct of_device_id sc27xx_vibra_of_match[] = { 137 { .compatible = "sprd,sc2731-vibrator", }, 138 {} 139 }; 140 MODULE_DEVICE_TABLE(of, sc27xx_vibra_of_match); 141 142 static 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 150 module_platform_driver(sc27xx_vibra_driver); 151 152 MODULE_DESCRIPTION("Spreadtrum SC27xx Vibrator Driver"); 153 MODULE_LICENSE("GPL v2"); 154 MODULE_AUTHOR("Xiaotong Lu <xiaotong.lu@spreadtrum.com>"); 155