summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorMikko Sarmanne <mikko.sarmanne@symbio.com>2011-01-18 09:04:21 +0200
committerRobert Marklund <robert.marklund@stericsson.com>2011-05-13 10:40:21 +0200
commit1cee473c82603a504c0c9a24e982b80656c1d085 (patch)
tree5f06f2afa366323709fd8dcfb121800967df7275 /sound
parentf8d768b7871f869dd703d1abcc3f99a8eeb0a537 (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.c37
-rw-r--r--sound/soc/ux500/u8500.c9
-rw-r--r--sound/soc/ux500/ux500_ab8500.c208
-rw-r--r--sound/soc/ux500/ux500_ab8500.h14
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