Commit | Line | Data |
---|---|---|
45615a9b XL |
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>"); |