1 // SPDX-License-Identifier: GPL-2.0-only
3 * Arizona haptics driver
5 * Copyright 2012 Wolfson Microelectronics plc
7 * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/input.h>
13 #include <linux/slab.h>
15 #include <sound/soc.h>
16 #include <sound/soc-dapm.h>
18 #include <linux/mfd/arizona/core.h>
19 #include <linux/mfd/arizona/pdata.h>
20 #include <linux/mfd/arizona/registers.h>
22 struct arizona_haptics
{
23 struct arizona
*arizona
;
24 struct input_dev
*input_dev
;
25 struct work_struct work
;
31 static void arizona_haptics_work(struct work_struct
*work
)
33 struct arizona_haptics
*haptics
= container_of(work
,
34 struct arizona_haptics
,
36 struct arizona
*arizona
= haptics
->arizona
;
37 struct snd_soc_component
*component
=
38 snd_soc_dapm_to_component(arizona
->dapm
);
41 if (!haptics
->arizona
->dapm
) {
42 dev_err(arizona
->dev
, "No DAPM context\n");
46 if (haptics
->intensity
) {
47 ret
= regmap_update_bits(arizona
->regmap
,
48 ARIZONA_HAPTICS_PHASE_2_INTENSITY
,
49 ARIZONA_PHASE2_INTENSITY_MASK
,
52 dev_err(arizona
->dev
, "Failed to set intensity: %d\n",
57 /* This enable sequence will be a noop if already enabled */
58 ret
= regmap_update_bits(arizona
->regmap
,
59 ARIZONA_HAPTICS_CONTROL_1
,
60 ARIZONA_HAP_CTRL_MASK
,
61 1 << ARIZONA_HAP_CTRL_SHIFT
);
63 dev_err(arizona
->dev
, "Failed to start haptics: %d\n",
68 ret
= snd_soc_component_enable_pin(component
, "HAPTICS");
70 dev_err(arizona
->dev
, "Failed to start HAPTICS: %d\n",
75 ret
= snd_soc_dapm_sync(arizona
->dapm
);
77 dev_err(arizona
->dev
, "Failed to sync DAPM: %d\n",
82 /* This disable sequence will be a noop if already enabled */
83 ret
= snd_soc_component_disable_pin(component
, "HAPTICS");
85 dev_err(arizona
->dev
, "Failed to disable HAPTICS: %d\n",
90 ret
= snd_soc_dapm_sync(arizona
->dapm
);
92 dev_err(arizona
->dev
, "Failed to sync DAPM: %d\n",
97 ret
= regmap_update_bits(arizona
->regmap
,
98 ARIZONA_HAPTICS_CONTROL_1
,
99 ARIZONA_HAP_CTRL_MASK
, 0);
101 dev_err(arizona
->dev
, "Failed to stop haptics: %d\n",
108 static int arizona_haptics_play(struct input_dev
*input
, void *data
,
109 struct ff_effect
*effect
)
111 struct arizona_haptics
*haptics
= input_get_drvdata(input
);
112 struct arizona
*arizona
= haptics
->arizona
;
114 if (!arizona
->dapm
) {
115 dev_err(arizona
->dev
, "No DAPM context\n");
119 if (effect
->u
.rumble
.strong_magnitude
) {
120 /* Scale the magnitude into the range the device supports */
121 if (arizona
->pdata
.hap_act
) {
123 effect
->u
.rumble
.strong_magnitude
>> 9;
124 if (effect
->direction
< 0x8000)
125 haptics
->intensity
+= 0x7f;
128 effect
->u
.rumble
.strong_magnitude
>> 8;
131 haptics
->intensity
= 0;
134 schedule_work(&haptics
->work
);
139 static void arizona_haptics_close(struct input_dev
*input
)
141 struct arizona_haptics
*haptics
= input_get_drvdata(input
);
142 struct snd_soc_component
*component
;
144 cancel_work_sync(&haptics
->work
);
146 if (haptics
->arizona
->dapm
) {
147 component
= snd_soc_dapm_to_component(haptics
->arizona
->dapm
);
148 snd_soc_component_disable_pin(component
, "HAPTICS");
152 static int arizona_haptics_probe(struct platform_device
*pdev
)
154 struct arizona
*arizona
= dev_get_drvdata(pdev
->dev
.parent
);
155 struct arizona_haptics
*haptics
;
158 haptics
= devm_kzalloc(&pdev
->dev
, sizeof(*haptics
), GFP_KERNEL
);
162 haptics
->arizona
= arizona
;
164 ret
= regmap_update_bits(arizona
->regmap
, ARIZONA_HAPTICS_CONTROL_1
,
165 ARIZONA_HAP_ACT
, arizona
->pdata
.hap_act
);
167 dev_err(arizona
->dev
, "Failed to set haptics actuator: %d\n",
172 INIT_WORK(&haptics
->work
, arizona_haptics_work
);
174 haptics
->input_dev
= devm_input_allocate_device(&pdev
->dev
);
175 if (!haptics
->input_dev
) {
176 dev_err(arizona
->dev
, "Failed to allocate input device\n");
180 input_set_drvdata(haptics
->input_dev
, haptics
);
182 haptics
->input_dev
->name
= "arizona:haptics";
183 haptics
->input_dev
->close
= arizona_haptics_close
;
184 __set_bit(FF_RUMBLE
, haptics
->input_dev
->ffbit
);
186 ret
= input_ff_create_memless(haptics
->input_dev
, NULL
,
187 arizona_haptics_play
);
189 dev_err(arizona
->dev
, "input_ff_create_memless() failed: %d\n",
194 ret
= input_register_device(haptics
->input_dev
);
196 dev_err(arizona
->dev
, "couldn't register input device: %d\n",
204 static struct platform_driver arizona_haptics_driver
= {
205 .probe
= arizona_haptics_probe
,
207 .name
= "arizona-haptics",
210 module_platform_driver(arizona_haptics_driver
);
212 MODULE_ALIAS("platform:arizona-haptics");
213 MODULE_DESCRIPTION("Arizona haptics driver");
214 MODULE_LICENSE("GPL");
215 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");