diff options
author | Mikko Sarmanne <mikko.sarmanne@symbio.com> | 2011-01-18 09:04:21 +0200 |
---|---|---|
committer | Robert Marklund <robert.marklund@stericsson.com> | 2011-05-13 10:40:21 +0200 |
commit | 1cee473c82603a504c0c9a24e982b80656c1d085 (patch) | |
tree | 5f06f2afa366323709fd8dcfb121800967df7275 /sound | |
parent | f8d768b7871f869dd703d1abcc3f99a8eeb0a537 (diff) |
Enable headset mic on V2 HW
Enables headset mic recording on V2 hardware by updating regulator
handling in the AB8500 ASoC driver.
ST-Ericsson ID: ER281760
Change-Id: I1047f66a4a1fd8177e408330a8c34c27022bbbfa
Signed-off-by: Mikko Sarmanne <mikko.sarmanne@symbio.com>
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/12883
Tested-by: Josefin LOFSTEDT <josefin.lofstedt@stericsson.com>
Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/ab8500.c | 37 | ||||
-rw-r--r-- | sound/soc/ux500/u8500.c | 9 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_ab8500.c | 208 | ||||
-rw-r--r-- | sound/soc/ux500/ux500_ab8500.h | 14 |
4 files changed, 156 insertions, 112 deletions
diff --git a/sound/soc/codecs/ab8500.c b/sound/soc/codecs/ab8500.c index ecec5a461f9..c0222d09cf2 100644 --- a/sound/soc/codecs/ab8500.c +++ b/sound/soc/codecs/ab8500.c @@ -30,6 +30,7 @@ #include <linux/mfd/ab8500.h> #include <linux/mfd/abx500.h> #include "ab8500.h" +#include "../ux500/ux500_ab8500.h" /* To convert register definition shifts to masks */ #define BMASK(bsft) (1 << (bsft)) @@ -42,11 +43,14 @@ #define GPIO27_DIR_OUTPUT 0x04 #define GPIO29_DIR_OUTPUT 0x10 #define GPIO31_DIR_OUTPUT 0x40 +#define GPIO35_DIR_OUTPUT 0x04 /* Macrocell register definitions */ #define AB8500_CTRL3_REG 0x0200 #define AB8500_SYSULPCLK_CTRL1_REG 0x020B #define AB8500_GPIO_DIR4_REG 0x1013 +#define AB8500_GPIO_DIR5_REG 0x1014 +#define AB8500_GPIO_OUT5_REG 0x1024 /* * AB8500 register cache & default register settings @@ -1617,6 +1621,38 @@ static void configure_audio_macrocell(struct snd_soc_codec *codec) data = ab8500_read_reg(codec, AB8500_MISC, AB8500_GPIO_DIR4_REG); data |= GPIO27_DIR_OUTPUT | GPIO29_DIR_OUTPUT | GPIO31_DIR_OUTPUT; ab8500_write_reg(codec, AB8500_MISC, AB8500_GPIO_DIR4_REG, data); + + data = ab8500_read_reg(codec, AB8500_MISC, AB8500_GPIO_DIR5_REG); + data |= GPIO35_DIR_OUTPUT; + ab8500_write_reg(codec, AB8500_MISC, AB8500_GPIO_DIR5_REG, data); + data = ab8500_read_reg(codec, AB8500_MISC, AB8500_GPIO_OUT5_REG); + data |= GPIO35_DIR_OUTPUT; + ab8500_write_reg(codec, AB8500_MISC, AB8500_GPIO_OUT5_REG, data); +} + +static int ab8500_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: + if (codec->bias_level == SND_SOC_BIAS_STANDBY) + enable_regulator("v-audio"); + break; + case SND_SOC_BIAS_STANDBY: + if (codec->bias_level == SND_SOC_BIAS_PREPARE) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(100)); + disable_regulator("v-audio"); + } + break; + case SND_SOC_BIAS_OFF: + break; + } + codec->bias_level = level; + + return 0; } static int ab8500_codec_probe(struct snd_soc_codec *codec) @@ -1677,6 +1713,7 @@ struct snd_soc_codec_driver ab8500_codec_drv = { .resume = ab8500_codec_resume, .read = ab8500_audio_read_reg, .write = ab8500_audio_write_reg, + .set_bias_level = ab8500_set_bias_level, .reg_cache_size = ARRAY_SIZE(ab8500_reg_cache), .reg_word_size = sizeof(u8), .reg_cache_default = ab8500_reg_cache, diff --git a/sound/soc/ux500/u8500.c b/sound/soc/ux500/u8500.c index e4208951821..ff1a6655504 100644 --- a/sound/soc/ux500/u8500.c +++ b/sound/soc/ux500/u8500.c @@ -166,15 +166,6 @@ static int __init u8500_soc_init(void) platform_device_register(&cg29xx_codec); #endif - #ifdef CONFIG_SND_SOC_UX500_AB8500 - pr_debug("%s: Calling init-function for AB8500 machine driver.\n", - __func__); - ret = ux500_ab8500_soc_machine_drv_init(); - if (ret) - pr_err("%s: ux500_ab8500_soc_machine_drv_init failed (%d).\n", - __func__, ret); - #endif - pr_debug("%s: Register device to generate a probe for Ux500-pcm platform.\n", __func__); platform_device_register(&ux500_pcm); diff --git a/sound/soc/ux500/ux500_ab8500.c b/sound/soc/ux500/ux500_ab8500.c index d651f88aa55..d94c2c13059 100644 --- a/sound/soc/ux500/ux500_ab8500.c +++ b/sound/soc/ux500/ux500_ab8500.c @@ -21,6 +21,7 @@ #include <linux/regulator/consumer.h> #include <sound/pcm.h> #include <sound/pcm_params.h> +#include <sound/soc-dapm.h> #include <mach/hardware.h> #include "ux500_pcm.h" #include "ux500_msp_dai.h" @@ -46,13 +47,87 @@ static unsigned int tx_slots = DEF_TX_SLOTS; static unsigned int rx_slots = DEF_RX_SLOTS; /* List the regulators that are to be controlled.. */ -static struct regulator_bulk_data ab8500_regus[4] = { +static struct regulator_bulk_data ab8500_regus[5] = { { .supply = "v-dmic" }, { .supply = "v-audio" }, { .supply = "v-amic1" }, - { .supply = "v-amic2" } + { .supply = "v-amic2" }, + { .supply = "vcc-N2158" } }; +static int create_regulators(struct device *dev) +{ + int i, status = 0; + + pr_debug("%s: Enter.\n", __func__); + + for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) + ab8500_regus[i].consumer = NULL; + + for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { + ab8500_regus[i].consumer = regulator_get( + dev, ab8500_regus[i].supply); + if (IS_ERR(ab8500_regus[i].consumer)) { + status = PTR_ERR(ab8500_regus[i].consumer); + pr_err("%s: Failed to get supply '%s' (%d)\n", + __func__, ab8500_regus[i].supply, status); + ab8500_regus[i].consumer = NULL; + goto err_get; + } + } + + return 0; + +err_get: + + for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { + if (ab8500_regus[i].consumer) { + regulator_put(ab8500_regus[i].consumer); + ab8500_regus[i].consumer = NULL; + } + } + + return status; +} + +int enable_regulator(const char *name) +{ + int i, status; + + for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { + if (strcmp(name, ab8500_regus[i].supply) != 0) + continue; + + status = regulator_enable(ab8500_regus[i].consumer); + + if (status != 0) { + pr_err("%s: Failure with regulator %s (%d)\n", + __func__, name, status); + return status; + } else { + pr_debug("%s: Enabled regulator %s.\n", + __func__, name); + return 0; + } + } + + return -EINVAL; +} + +void disable_regulator(const char *name) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { + if (strcmp(name, ab8500_regus[i].supply) == 0) { + regulator_disable(ab8500_regus[i].consumer); + pr_debug("%s: Disabled regulator %s.\n", + __func__, name); + return; + } + } +} + int ux500_ab8500_startup(struct snd_pcm_substream *substream) { pr_info("%s: Enter\n", __func__); @@ -139,83 +214,54 @@ int ux500_ab8500_hw_params( return ret; } -static int create_regulators(void) +static int regulator_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) { - int i, status = 0; - - pr_info("%s: Enter.\n", __func__); - - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) - ab8500_regus[i].consumer = NULL; - - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - ab8500_regus[i].consumer = regulator_get(NULL, - ab8500_regus[i].supply); - if (IS_ERR(ab8500_regus[i].consumer)) { - status = PTR_ERR(ab8500_regus[i].consumer); - pr_err("%s: Failed to get supply '%s' (%d)\n", - __func__, ab8500_regus[i].supply, status); - ab8500_regus[i].consumer = NULL; - goto err_get; - } - } - + if (SND_SOC_DAPM_EVENT_ON(event)) + enable_regulator(w->name); + else + disable_regulator(w->name); return 0; - -err_get: - - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - if (ab8500_regus[i].consumer) { - regulator_put(ab8500_regus[i].consumer); - ab8500_regus[i].consumer = NULL; - } - } - - return status; } -static int enable_regulator(const char *name) -{ - int i, status; +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_MIC("v-dmic", regulator_event), + SND_SOC_DAPM_MIC("v-amic1", regulator_event), + SND_SOC_DAPM_MIC("v-amic2", regulator_event), + SND_SOC_DAPM_MIC("vcc-N2158", regulator_event), +}; - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - if (strcmp(name, ab8500_regus[i].supply) != 0) - continue; +static const struct snd_soc_dapm_route dapm_routes[] = { + {"MIC1A", NULL, "v-amic1"}, - status = regulator_enable(ab8500_regus[i].consumer); + {"MIC1B", NULL, "vcc-N2158"}, + {"vcc-N2158", NULL, "v-amic1"}, - if (status != 0) { - pr_err("%s: Failure with regulator %s (%d)\n", - __func__, name, status); - return status; - } else { - pr_info("%s: Enabled regulator %s.\n", - __func__, name); - return 0; - } - } + {"MIC2", NULL, "v-amic2"}, - return -EINVAL; -} + {"DMIC1", NULL, "v-dmic"}, + {"DMIC2", NULL, "v-dmic"}, + {"DMIC3", NULL, "v-dmic"}, + {"DMIC4", NULL, "v-dmic"}, + {"DMIC5", NULL, "v-dmic"}, + {"DMIC6", NULL, "v-dmic"}, +}; -static void disable_regulator(const char *name) +int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd) { - int i; + struct snd_soc_codec *codec = rtd->codec; + int status = 0; - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) { - if (strcmp(name, ab8500_regus[i].supply) == 0) { - regulator_disable(ab8500_regus[i].consumer); - return; - } - } -} + pr_info("%s: Enter.\n", __func__); -int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd) -{ - pr_info("%s Enter.\n", __func__); + status = create_regulators(codec->dev); + if (status < 0) { + pr_err("%s: Failed to instantiate regulators (%d).\n", + __func__, status); + return status; + } /* - int status = 0; status = ab8500_accessory_init(rtd->codec); if (status < 0) { pr_err("%s: Failed to initialize accessories (%d).\n", @@ -224,45 +270,23 @@ int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd) } */ - /* TODO: Add required DAPM routes to control regulators on demand */ - - return 0; -} - -int ux500_ab8500_soc_machine_drv_init(void) -{ - int i; - int status = 0; - - pr_info("%s: Enter.\n", __func__); - - status = create_regulators(); - if (status < 0) { - pr_err("%s: Failed to instantiate regulators (%d).\n", - __func__, status); - return status; - } - - /* TODO: Enable only regulators really needed at this point.. */ - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) - status += enable_regulator(ab8500_regus[i].supply); + snd_soc_dapm_new_controls(codec, dapm_widgets, + ARRAY_SIZE(dapm_widgets)); + snd_soc_dapm_add_routes(codec, dapm_routes, + ARRAY_SIZE(dapm_routes)); + snd_soc_dapm_sync(codec); return status; } void ux500_ab8500_soc_machine_drv_cleanup(void) { - int i; - pr_info("%s: Enter.\n", __func__); /* ab8500_accessory_cleanup(); */ - /* Roll out all the regulators */ - for (i = 0; i < ARRAY_SIZE(ab8500_regus); ++i) - disable_regulator(ab8500_regus[i].supply); regulator_bulk_free(ARRAY_SIZE(ab8500_regus), ab8500_regus); } diff --git a/sound/soc/ux500/ux500_ab8500.h b/sound/soc/ux500/ux500_ab8500.h index 058ea8c4ef2..43cd73e2e29 100644 --- a/sound/soc/ux500/ux500_ab8500.h +++ b/sound/soc/ux500/ux500_ab8500.h @@ -16,19 +16,11 @@ extern struct snd_soc_ops ux500_ab8500_ops[]; -struct snd_soc_pcm_runtime; - -int ux500_ab8500_startup(struct snd_pcm_substream *substream); - -void ux500_ab8500_shutdown(struct snd_pcm_substream *substream); - -int ux500_ab8500_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params); - -int ux500_ab8500_soc_machine_drv_init(void); +int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *runtime); void ux500_ab8500_soc_machine_drv_cleanup(void); -int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *runtime); +int enable_regulator(const char *name); +void disable_regulator(const char *name); #endif |