[alsa-devel] [PATCH 1/4] mfd: arizona: Allow the CODEC DAPM context to be accessed elsewhere
Some other device functions need to integrate with signal sources in the audio portion (primarily for haptics) so allow CODEC to export the DAPM context by pointing to it from the core driver.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- include/linux/mfd/arizona/core.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 6d03e31..4318259 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -79,6 +79,8 @@ enum arizona_type {
#define ARIZONA_NUM_IRQ 50
+struct snd_soc_dapm_context; + struct arizona { struct regmap *regmap; struct device *dev; @@ -99,6 +101,8 @@ struct arizona {
struct mutex clk_lock; int clk32k_ref; + + struct snd_soc_dapm_context *dapm; };
int arizona_clk32k_enable(struct arizona *arizona);
The Arizona CODECs contain a haptics module providing vibration feedback support. Implement basic support for this, providing simple start/stop and signal magnitude control.
Since the output path for haptics is routed through the CODEC audio routing it is modelled as a signal generator within ASoC, the haptics driver calls DAPM to start and stop the output drivers. An appropriate output path must be configured via ALSA to connect the haptics source to the correct output.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com ---
Note that this hasn't actually been tested yet (but it is pretty simple so should if nothing else be easy to fix up...), due to the cross tree issues I'd hope to get it into 3.8 though I appreciate it's a bit late - if not I'll go ahead and merge the MFD/ASoC bits to make life easier.
drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/arizona-haptics.c | 221 ++++++++++++++++++++++++++++++++++ include/linux/mfd/arizona/pdata.h | 6 + 4 files changed, 238 insertions(+) create mode 100644 drivers/input/misc/arizona-haptics.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 2a1647e..259ef31 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -72,6 +72,16 @@ config INPUT_AD714X_SPI To compile this driver as a module, choose M here: the module will be called ad714x-spi.
+config INPUT_ARIZONA_HAPTICS + tristate "Arizona haptics support" + depends on MFD_ARIZONA && SND_SOC + select INPUT_FF_MEMLESS + help + Say Y to enable support for the haptics module in Arizona CODECs. + + To compile this driver as a module, choose M here: the + module will be called arizona-haptics. + config INPUT_BMA150 tristate "BMA150/SMB380 acceleration sensor support" depends on I2C diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 1f874af..1f1e1b1 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o obj-$(CONFIG_INPUT_APANEL) += apanel.o +obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c new file mode 100644 index 0000000..faf7fa5 --- /dev/null +++ b/drivers/input/misc/arizona-haptics.c @@ -0,0 +1,221 @@ +/* + * Arizona haptics driver + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown broonie@opensource.wolfsonmicro.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/slab.h> + +#include <sound/soc.h> +#include <sound/soc-dapm.h> + +#include <linux/mfd/arizona/core.h> +#include <linux/mfd/arizona/pdata.h> +#include <linux/mfd/arizona/registers.h> + +struct arizona_haptics { + struct arizona *arizona; + struct input_dev *input_dev; +}; + +static int arizona_haptics_play(struct input_dev *input, void *data, + struct ff_effect *effect) +{ + struct arizona_haptics *haptics = input_get_drvdata(input); + struct arizona *arizona = haptics->arizona; + struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex; + u8 val; + int ret; + + if (!haptics->arizona->dapm) { + dev_err(arizona->dev, "No DAPM context\n"); + return -EBUSY; + } + + if (effect->u.rumble.strong_magnitude) { + /* Scale the magnitude into the range the device supports */ + if (arizona->pdata.hap_act) { + val = effect->u.rumble.strong_magnitude >> 9; + if (effect->direction < 0x8000) + val += 0x7f; + } else { + val = effect->u.rumble.strong_magnitude >> 8; + } + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_PHASE_2_INTENSITY, + ARIZONA_PHASE2_INTENSITY_MASK, val); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set intensity: %d\n", + ret); + return ret; + } + + /* This enable sequence will be a noop if already enabled */ + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_CTRL_MASK, + 1 << ARIZONA_HAP_CTRL_SHIFT); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start haptics: %d\n", + ret); + return ret; + } + + mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to start HAPTICS: %d\n", + ret); + mutex_unlock(dapm_mutex); + return ret; + } + + ret = snd_soc_dapm_sync(arizona->dapm); + if (ret != 0) { + dev_err(arizona->dev, "Failed to sync DAPM: %d\n", + ret); + mutex_unlock(dapm_mutex); + return ret; + } + + mutex_unlock(dapm_mutex); + + } else { + /* This disable sequence will be a noop if already enabled */ + mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME); + + ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS"); + if (ret != 0) { + dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n", + ret); + mutex_unlock(dapm_mutex); + return ret; + } + + ret = snd_soc_dapm_sync(arizona->dapm); + if (ret != 0) { + dev_err(arizona->dev, "Failed to sync DAPM: %d\n", + ret); + mutex_unlock(dapm_mutex); + return ret; + } + + mutex_unlock(dapm_mutex); + + ret = regmap_update_bits(arizona->regmap, + ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_CTRL_MASK, + 1 << ARIZONA_HAP_CTRL_SHIFT); + if (ret != 0) { + dev_err(arizona->dev, "Failed to stop haptics: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static void arizona_haptics_close(struct input_dev *input) +{ + struct arizona_haptics *haptics = input_get_drvdata(input); + + if (haptics->arizona->dapm) + snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS"); +} + +static int arizona_haptics_probe(struct platform_device *pdev) +{ + struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); + struct arizona_haptics *haptics; + int ret; + + haptics = kzalloc(sizeof(*haptics), GFP_KERNEL); + if (!haptics) + return -ENOMEM; + + haptics->arizona = arizona; + + ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1, + ARIZONA_HAP_ACT, arizona->pdata.hap_act); + if (ret != 0) { + dev_err(arizona->dev, "Failed to set haptics actuator: %d\n", + ret); + return ret; + } + + haptics->input_dev = input_allocate_device(); + if (haptics->input_dev == NULL) { + dev_err(arizona->dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + input_set_drvdata(haptics->input_dev, haptics); + + haptics->input_dev->name = "arizona:haptics"; + haptics->input_dev->dev.parent = pdev->dev.parent; + haptics->input_dev->close = arizona_haptics_close; + __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); + + ret = input_ff_create_memless(haptics->input_dev, NULL, + arizona_haptics_play); + if (ret < 0) { + dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n", + ret); + goto err_ialloc; + } + + ret = input_register_device(haptics->input_dev); + if (ret < 0) { + dev_err(arizona->dev, "couldn't register input device: %d\n", + ret); + goto err_iff; + } + + platform_set_drvdata(pdev, haptics); + + return 0; + +err_iff: + if (haptics->input_dev) + input_ff_destroy(haptics->input_dev); +err_ialloc: + input_free_device(haptics->input_dev); + + return ret; +} + +static int arizona_haptics_remove(struct platform_device *pdev) +{ + struct arizona_haptics *haptics = platform_get_drvdata(pdev); + + input_unregister_device(haptics->input_dev); + + return 0; +} + +static struct platform_driver arizona_haptics_driver = { + .probe = arizona_haptics_probe, + .remove = arizona_haptics_remove, + .driver = { + .name = "arizona-haptics", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(arizona_haptics_driver); + +MODULE_ALIAS("platform:arizona-haptics"); +MODULE_DESCRIPTION("Arizona haptics driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mark Brown broonie@opensource.wolfsonmicro.com"); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 7ab4429..8b1d1da 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -62,6 +62,9 @@
#define ARIZONA_MAX_OUTPUT 6
+#define ARIZONA_HAP_ACT_ERM 0 +#define ARIZONA_HAP_ACT_LRA 2 + #define ARIZONA_MAX_PDM_SPK 2
struct regulator_init_data; @@ -114,6 +117,9 @@ struct arizona_pdata {
/** PDM speaker format */ unsigned int spk_fmt[ARIZONA_MAX_PDM_SPK]; + + /** Haptic actuator type */ + unsigned int hap_act; };
#endif
Hi Mark,
On Mon, Nov 26, 2012 at 09:39:38PM +0000, Mark Brown wrote:
+static int arizona_haptics_play(struct input_dev *input, void *data,
struct ff_effect *effect)
+{
- struct arizona_haptics *haptics = input_get_drvdata(input);
- struct arizona *arizona = haptics->arizona;
- struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex;
- u8 val;
- int ret;
- if (!haptics->arizona->dapm) {
dev_err(arizona->dev, "No DAPM context\n");
return -EBUSY;
- }
- if (effect->u.rumble.strong_magnitude) {
/* Scale the magnitude into the range the device supports */
if (arizona->pdata.hap_act) {
val = effect->u.rumble.strong_magnitude >> 9;
if (effect->direction < 0x8000)
val += 0x7f;
} else {
val = effect->u.rumble.strong_magnitude >> 8;
}
ret = regmap_update_bits(arizona->regmap,
ARIZONA_HAPTICS_PHASE_2_INTENSITY,
ARIZONA_PHASE2_INTENSITY_MASK, val);
if (ret != 0) {
dev_err(arizona->dev, "Failed to set intensity: %d\n",
ret);
return ret;
}
/* This enable sequence will be a noop if already enabled */
ret = regmap_update_bits(arizona->regmap,
ARIZONA_HAPTICS_CONTROL_1,
ARIZONA_HAP_CTRL_MASK,
1 << ARIZONA_HAP_CTRL_SHIFT);
if (ret != 0) {
dev_err(arizona->dev, "Failed to start haptics: %d\n",
ret);
return ret;
}
mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
You cannot sleep in play() methods.
Thanks.
On Mon, Nov 26, 2012 at 03:11:22PM -0800, Dmitry Torokhov wrote:
mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
You cannot sleep in play() methods.
Sorry, I wasn't getting enough vibrations from the code :) I just sent a documentation patch.
Provide a haptics widget for use by the haptics driver and expose the DAPM context for it.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm5102.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index b18fd2e..99cb83f 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -860,6 +860,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"), SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"),
SND_SOC_DAPM_INPUT("IN1L"), SND_SOC_DAPM_INPUT("IN1R"), @@ -1094,6 +1095,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"), { name, "Noise Generator", "Noise Generator" }, \ { name, "Tone Generator 1", "Tone Generator 1" }, \ { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ { name, "AEC", "AEC Loopback" }, \ { name, "IN1L", "IN1L PGA" }, \ { name, "IN1R", "IN1R PGA" }, \ @@ -1387,9 +1389,20 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
codec->control_data = priv->core.arizona->regmap; + priv->core.arizona->dapm = &codec->dapm; + return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP); }
+static int wm5102_codec_remove(struct snd_soc_codec *codec) +{ + struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + #define WM5102_DIG_VU 0x0200
static unsigned int wm5102_digital_vu[] = { @@ -1414,6 +1427,7 @@ static unsigned int wm5102_digital_vu[] = {
static struct snd_soc_codec_driver soc_codec_dev_wm5102 = { .probe = wm5102_codec_probe, + .remove = wm5102_codec_remove,
.idle_bias_off = true,
Provide a haptics widget for use by the haptics driver and expose the DAPM context for it.
Signed-off-by: Mark Brown broonie@opensource.wolfsonmicro.com --- sound/soc/codecs/wm5110.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 8b81fa5..601d90a 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -371,6 +371,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"), SND_SOC_DAPM_SIGGEN("NOISE"), +SND_SOC_DAPM_SIGGEN("HAPTICS"),
SND_SOC_DAPM_INPUT("IN1L"), SND_SOC_DAPM_INPUT("IN1R"), @@ -639,6 +640,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT2R"), { name, "Noise Generator", "Noise Generator" }, \ { name, "Tone Generator 1", "Tone Generator 1" }, \ { name, "Tone Generator 2", "Tone Generator 2" }, \ + { name, "Haptics", "HAPTICS" }, \ { name, "AEC", "AEC Loopback" }, \ { name, "IN1L", "IN1L PGA" }, \ { name, "IN1R", "IN1R PGA" }, \ @@ -963,9 +965,20 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec) int i;
codec->control_data = priv->core.arizona->regmap; + priv->core.arizona->dapm = &codec->dapm; + return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP); }
+static int wm5110_codec_remove(struct snd_soc_codec *codec) +{ + struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec); + + priv->core.arizona->dapm = NULL; + + return 0; +} + #define WM5110_DIG_VU 0x0200
static unsigned int wm5110_digital_vu[] = { @@ -994,6 +1007,7 @@ static unsigned int wm5110_digital_vu[] = {
static struct snd_soc_codec_driver soc_codec_dev_wm5110 = { .probe = wm5110_codec_probe, + .remove = wm5110_codec_remove,
.idle_bias_off = true,
participants (2)
-
Dmitry Torokhov
-
Mark Brown