Commit | Line | Data |
---|---|---|
11205bb6 AM |
1 | /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. |
2 | * | |
3 | * This program is free software; you can redistribute it and/or modify | |
4 | * it under the terms of the GNU General Public License version 2 and | |
5 | * only version 2 as published by the Free Software Foundation. | |
6 | * | |
7 | * This program is distributed in the hope that it will be useful, | |
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | * GNU General Public License for more details. | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
11205bb6 AM |
14 | #include <linux/kernel.h> |
15 | #include <linux/errno.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/input.h> | |
18 | #include <linux/slab.h> | |
21014b80 | 19 | #include <linux/regmap.h> |
11205bb6 AM |
20 | |
21 | #define VIB_DRV 0x4A | |
22 | ||
23 | #define VIB_DRV_SEL_MASK 0xf8 | |
24 | #define VIB_DRV_SEL_SHIFT 0x03 | |
25 | #define VIB_DRV_EN_MANUAL_MASK 0xfc | |
26 | ||
27 | #define VIB_MAX_LEVEL_mV (3100) | |
28 | #define VIB_MIN_LEVEL_mV (1200) | |
29 | #define VIB_MAX_LEVELS (VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV) | |
30 | ||
31 | #define MAX_FF_SPEED 0xff | |
32 | ||
33 | /** | |
34 | * struct pm8xxx_vib - structure to hold vibrator data | |
35 | * @vib_input_dev: input device supporting force feedback | |
36 | * @work: work structure to set the vibration parameters | |
21014b80 | 37 | * @regmap: regmap for register read/write |
11205bb6 AM |
38 | * @speed: speed of vibration set from userland |
39 | * @active: state of vibrator | |
40 | * @level: level of vibration to set in the chip | |
41 | * @reg_vib_drv: VIB_DRV register value | |
42 | */ | |
43 | struct pm8xxx_vib { | |
44 | struct input_dev *vib_input_dev; | |
45 | struct work_struct work; | |
21014b80 | 46 | struct regmap *regmap; |
11205bb6 AM |
47 | int speed; |
48 | int level; | |
49 | bool active; | |
50 | u8 reg_vib_drv; | |
51 | }; | |
52 | ||
11205bb6 AM |
53 | /** |
54 | * pm8xxx_vib_set - handler to start/stop vibration | |
55 | * @vib: pointer to vibrator structure | |
56 | * @on: state to set | |
57 | */ | |
58 | static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on) | |
59 | { | |
60 | int rc; | |
21014b80 | 61 | unsigned int val = vib->reg_vib_drv; |
11205bb6 AM |
62 | |
63 | if (on) | |
64 | val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK); | |
65 | else | |
66 | val &= ~VIB_DRV_SEL_MASK; | |
67 | ||
21014b80 | 68 | rc = regmap_write(vib->regmap, VIB_DRV, val); |
11205bb6 AM |
69 | if (rc < 0) |
70 | return rc; | |
71 | ||
72 | vib->reg_vib_drv = val; | |
73 | return 0; | |
74 | } | |
75 | ||
76 | /** | |
77 | * pm8xxx_work_handler - worker to set vibration level | |
78 | * @work: pointer to work_struct | |
79 | */ | |
80 | static void pm8xxx_work_handler(struct work_struct *work) | |
81 | { | |
82 | struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work); | |
83 | int rc; | |
21014b80 | 84 | unsigned int val; |
11205bb6 | 85 | |
21014b80 | 86 | rc = regmap_read(vib->regmap, VIB_DRV, &val); |
11205bb6 AM |
87 | if (rc < 0) |
88 | return; | |
89 | ||
90 | /* | |
91 | * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so | |
92 | * scale the level to fit into these ranges. | |
93 | */ | |
94 | if (vib->speed) { | |
95 | vib->active = true; | |
96 | vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) + | |
97 | VIB_MIN_LEVEL_mV; | |
98 | vib->level /= 100; | |
99 | } else { | |
100 | vib->active = false; | |
101 | vib->level = VIB_MIN_LEVEL_mV / 100; | |
102 | } | |
103 | ||
104 | pm8xxx_vib_set(vib, vib->active); | |
105 | } | |
106 | ||
107 | /** | |
108 | * pm8xxx_vib_close - callback of input close callback | |
109 | * @dev: input device pointer | |
110 | * | |
111 | * Turns off the vibrator. | |
112 | */ | |
113 | static void pm8xxx_vib_close(struct input_dev *dev) | |
114 | { | |
115 | struct pm8xxx_vib *vib = input_get_drvdata(dev); | |
116 | ||
117 | cancel_work_sync(&vib->work); | |
118 | if (vib->active) | |
119 | pm8xxx_vib_set(vib, false); | |
120 | } | |
121 | ||
122 | /** | |
123 | * pm8xxx_vib_play_effect - function to handle vib effects. | |
124 | * @dev: input device pointer | |
125 | * @data: data of effect | |
126 | * @effect: effect to play | |
127 | * | |
128 | * Currently this driver supports only rumble effects. | |
129 | */ | |
130 | static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data, | |
131 | struct ff_effect *effect) | |
132 | { | |
133 | struct pm8xxx_vib *vib = input_get_drvdata(dev); | |
134 | ||
135 | vib->speed = effect->u.rumble.strong_magnitude >> 8; | |
136 | if (!vib->speed) | |
137 | vib->speed = effect->u.rumble.weak_magnitude >> 9; | |
138 | ||
139 | schedule_work(&vib->work); | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
5298cc4c | 144 | static int pm8xxx_vib_probe(struct platform_device *pdev) |
11205bb6 AM |
145 | { |
146 | struct pm8xxx_vib *vib; | |
147 | struct input_dev *input_dev; | |
148 | int error; | |
21014b80 | 149 | unsigned int val; |
11205bb6 | 150 | |
12a5a8fd SB |
151 | vib = devm_kzalloc(&pdev->dev, sizeof(*vib), GFP_KERNEL); |
152 | if (!vib) | |
153 | return -ENOMEM; | |
154 | ||
21014b80 SB |
155 | vib->regmap = dev_get_regmap(pdev->dev.parent, NULL); |
156 | if (!vib->regmap) | |
157 | return -ENODEV; | |
158 | ||
12a5a8fd SB |
159 | input_dev = devm_input_allocate_device(&pdev->dev); |
160 | if (!input_dev) | |
161 | return -ENOMEM; | |
11205bb6 AM |
162 | |
163 | INIT_WORK(&vib->work, pm8xxx_work_handler); | |
11205bb6 AM |
164 | vib->vib_input_dev = input_dev; |
165 | ||
166 | /* operate in manual mode */ | |
21014b80 | 167 | error = regmap_read(vib->regmap, VIB_DRV, &val); |
11205bb6 | 168 | if (error < 0) |
12a5a8fd SB |
169 | return error; |
170 | ||
11205bb6 | 171 | val &= ~VIB_DRV_EN_MANUAL_MASK; |
21014b80 | 172 | error = regmap_write(vib->regmap, VIB_DRV, val); |
11205bb6 | 173 | if (error < 0) |
12a5a8fd | 174 | return error; |
11205bb6 AM |
175 | |
176 | vib->reg_vib_drv = val; | |
177 | ||
178 | input_dev->name = "pm8xxx_vib_ffmemless"; | |
179 | input_dev->id.version = 1; | |
11205bb6 AM |
180 | input_dev->close = pm8xxx_vib_close; |
181 | input_set_drvdata(input_dev, vib); | |
182 | input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE); | |
183 | ||
184 | error = input_ff_create_memless(input_dev, NULL, | |
185 | pm8xxx_vib_play_effect); | |
186 | if (error) { | |
187 | dev_err(&pdev->dev, | |
188 | "couldn't register vibrator as FF device\n"); | |
12a5a8fd | 189 | return error; |
11205bb6 AM |
190 | } |
191 | ||
192 | error = input_register_device(input_dev); | |
193 | if (error) { | |
194 | dev_err(&pdev->dev, "couldn't register input device\n"); | |
12a5a8fd | 195 | return error; |
11205bb6 AM |
196 | } |
197 | ||
198 | platform_set_drvdata(pdev, vib); | |
199 | return 0; | |
11205bb6 AM |
200 | } |
201 | ||
202 | #ifdef CONFIG_PM_SLEEP | |
203 | static int pm8xxx_vib_suspend(struct device *dev) | |
204 | { | |
205 | struct pm8xxx_vib *vib = dev_get_drvdata(dev); | |
206 | ||
207 | /* Turn off the vibrator */ | |
208 | pm8xxx_vib_set(vib, false); | |
209 | ||
210 | return 0; | |
211 | } | |
212 | #endif | |
213 | ||
214 | static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL); | |
215 | ||
877e1f15 SB |
216 | static const struct of_device_id pm8xxx_vib_id_table[] = { |
217 | { .compatible = "qcom,pm8058-vib" }, | |
218 | { .compatible = "qcom,pm8921-vib" }, | |
219 | { } | |
220 | }; | |
221 | MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table); | |
222 | ||
11205bb6 AM |
223 | static struct platform_driver pm8xxx_vib_driver = { |
224 | .probe = pm8xxx_vib_probe, | |
11205bb6 AM |
225 | .driver = { |
226 | .name = "pm8xxx-vib", | |
227 | .owner = THIS_MODULE, | |
228 | .pm = &pm8xxx_vib_pm_ops, | |
877e1f15 | 229 | .of_match_table = pm8xxx_vib_id_table, |
11205bb6 AM |
230 | }, |
231 | }; | |
840a746b | 232 | module_platform_driver(pm8xxx_vib_driver); |
11205bb6 AM |
233 | |
234 | MODULE_ALIAS("platform:pm8xxx_vib"); | |
235 | MODULE_DESCRIPTION("PMIC8xxx vibrator driver based on ff-memless framework"); | |
236 | MODULE_LICENSE("GPL v2"); | |
237 | MODULE_AUTHOR("Amy Maloche <amaloche@codeaurora.org>"); |