summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorKristoffer KARLSSON <kristoffer.karlsson@stericsson.com>2011-12-28 15:50:18 +0100
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:05:04 +0200
commit55032f144947cc59166332b148cdef23a3d577c0 (patch)
tree77a5b201697a651672f70881fe9944769d092e73 /sound
parent87b4797ece28bb0dedfdaadfc5550dbcb79f4a32 (diff)
ASoC: Ux500: Add support for mapping reg to amic
Added support for configuring which regulator is connected to which analog microphone so that the correct regulator can be automatically powered up/down when each analog microphone path is connected/disconnected. ST-Ericsson Linux next: NA ST-Ericsson ID: 350064 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: Ia400b552b37fd48d3cb9856663a599ddb70b861c Signed-off-by: Kristoffer KARLSSON <kristoffer.karlsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/43929 Reviewed-by: QATOOLS Reviewed-by: QABUILD Reviewed-by: Roger NILSSON1 <roger.xr.nilsson@stericsson.com> Reviewed-by: Linus WALLEIJ <linus.walleij@stericsson.com>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/ab8500_audio.c7
-rw-r--r--sound/soc/ux500/ux500_ab8500.c229
2 files changed, 198 insertions, 38 deletions
diff --git a/sound/soc/codecs/ab8500_audio.c b/sound/soc/codecs/ab8500_audio.c
index 33a40e627d9..0741b5aa6ff 100644
--- a/sound/soc/codecs/ab8500_audio.c
+++ b/sound/soc/codecs/ab8500_audio.c
@@ -1005,7 +1005,8 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
/* Microphone 1 path */
- SND_SOC_DAPM_INPUT("MIC1 Input"),
+ SND_SOC_DAPM_INPUT("MIC1A Input"),
+ SND_SOC_DAPM_INPUT("MIC1B Input"),
SND_SOC_DAPM_MUX("Mic 1A or 1B Select Capture Route",
SND_SOC_NOPM, 0, 0, dapm_mic1ab_select),
@@ -1245,8 +1246,8 @@ static const struct snd_soc_dapm_route dapm_routes[] = {
/* Microphone 1 path */
- {"Mic 1A or 1B Select Capture Route", "Mic 1A", "MIC1 Input"},
- {"Mic 1A or 1B Select Capture Route", "Mic 1B", "MIC1 Input"},
+ {"Mic 1A or 1B Select Capture Route", "Mic 1A", "MIC1A Input"},
+ {"Mic 1A or 1B Select Capture Route", "Mic 1B", "MIC1B Input"},
{"Mic 1", "Capture Switch", "Mic 1A or 1B Select Capture Route"},
diff --git a/sound/soc/ux500/ux500_ab8500.c b/sound/soc/ux500/ux500_ab8500.c
index 6556e8e232d..3b1480a9d76 100644
--- a/sound/soc/ux500/ux500_ab8500.c
+++ b/sound/soc/ux500/ux500_ab8500.c
@@ -107,6 +107,22 @@ static bool reg_enabled[4] = {
false,
false
};
+static int reg_claim[4];
+enum amic_idx { AMIC_1A, AMIC_1B, AMIC_2 };
+struct amic_conf {
+ enum regulator_idx reg_id;
+ bool enabled;
+ char *name;
+};
+static struct amic_conf amic_info[3] = {
+ { REGULATOR_AMIC1, false, "amic1a" },
+ { REGULATOR_AMIC1, false, "amic1b" },
+ { REGULATOR_AMIC2, false, "amic2" }
+};
+static DEFINE_MUTEX(amic_conf_lock);
+
+static const char *enum_amic_reg_conf[2] = { "v-amic1", "v-amic2" };
+static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_amicconf, enum_amic_reg_conf);
/* Slot configuration */
static unsigned int tx_slots = DEF_TX_SLOTS;
@@ -131,7 +147,7 @@ static int enable_regulator(enum regulator_idx idx)
if (ret != 0) {
pr_err("%s: Failure to enable regulator '%s' (ret = %d)\n",
__func__, reg_info[idx].supply, ret);
- return ret;
+ return -EIO;
};
reg_enabled[idx] = true;
@@ -202,6 +218,42 @@ err_get:
return status;
}
+static int claim_amic_regulator(enum amic_idx amic_id)
+{
+ enum regulator_idx reg_id = amic_info[amic_id].reg_id;
+ int ret = 0;
+
+ reg_claim[reg_id]++;
+ if (reg_claim[reg_id] > 1)
+ goto cleanup;
+
+ ret = enable_regulator(reg_id);
+ if (ret < 0) {
+ pr_err("%s: Failed to claim %s for %s (ret = %d)!",
+ __func__, reg_info[reg_id].supply,
+ amic_info[amic_id].name, ret);
+ reg_claim[reg_id]--;
+ }
+
+cleanup:
+ amic_info[amic_id].enabled = (ret == 0);
+
+ return ret;
+}
+
+static void release_amic_regulator(enum amic_idx amic_id)
+{
+ enum regulator_idx reg_id = amic_info[amic_id].reg_id;
+
+ reg_claim[reg_id]--;
+ if (reg_claim[reg_id] <= 0) {
+ disable_regulator(reg_id);
+ reg_claim[reg_id] = 0;
+ }
+
+ amic_info[amic_id].enabled = false;
+}
+
/* Power/clock control */
static int ux500_ab8500_power_control_inc(void)
@@ -219,15 +271,13 @@ static int ux500_ab8500_power_control_inc(void)
if (ab8500_power_count == 1) {
/* Turn on audio-regulator */
ret = enable_regulator(REGULATOR_AUDIO);
- if (ret) {
- ret = -EIO;
+ if (ret < 0)
goto out;
- }
/* Enable audio-clock */
ret = clk_set_parent(clk_ptr_intclk,
(master_clock_sel == 0) ? clk_ptr_sysclk : clk_ptr_ulpclk);
- if (ret) {
+ if (ret != 0) {
pr_err("%s: ERROR: Setting master-clock to %s failed (ret = %d)!",
__func__,
(master_clock_sel == 0) ? "SYSCLK" : "ULPCLK",
@@ -239,7 +289,7 @@ static int ux500_ab8500_power_control_inc(void)
__func__,
(master_clock_sel == 0) ? "SYSCLK" : "ULPCLK");
ret = clk_enable(clk_ptr_audioclk);
- if (ret) {
+ if (ret != 0) {
pr_err("%s: ERROR: clk_enable failed (ret = %d)!", __func__, ret);
ret = -EIO;
ab8500_power_count = 0;
@@ -339,19 +389,104 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
ux500_ab8500_power_control_dec();
cleanup:
- if (ret < 0) {
+ if (ret < 0)
pr_err("%s: Unable to configure ANC! (ret = %d)\n",
- __func__, ret);
- return 0;
- }
+ __func__, ret);
- return 1;
+ return (ret < 0) ? 0 : 1;
}
static const struct snd_kcontrol_new anc_status_control = \
SOC_ENUM_EXT("ANC Status", soc_enum_ancstate,
anc_status_control_get, anc_status_control_put);
+static int amic_reg_control_get(struct snd_ctl_elem_value *ucontrol,
+ enum amic_idx amic_id)
+{
+ ucontrol->value.integer.value[0] =
+ (amic_info[amic_id].reg_id == REGULATOR_AMIC2);
+
+ return 0;
+}
+
+static int amic_reg_control_put(struct snd_ctl_elem_value *ucontrol,
+ enum amic_idx amic_id)
+{
+ enum regulator_idx old_reg_id, new_reg_id;
+ int ret = 0;
+
+ if (ucontrol->value.integer.value[0] == 0)
+ new_reg_id = REGULATOR_AMIC1;
+ else
+ new_reg_id = REGULATOR_AMIC2;
+
+ mutex_lock(&amic_conf_lock);
+
+ old_reg_id = amic_info[amic_id].reg_id;
+ if (old_reg_id == new_reg_id)
+ goto cleanup;
+
+ if (!amic_info[amic_id].enabled) {
+ amic_info[amic_id].reg_id = new_reg_id;
+ goto cleanup;
+ }
+
+ release_amic_regulator(amic_id);
+ amic_info[amic_id].reg_id = new_reg_id;
+ ret = claim_amic_regulator(amic_id);
+
+cleanup:
+ mutex_unlock(&amic_conf_lock);
+
+ return (ret < 0) ? 0 : 1;
+}
+
+static int amic1a_reg_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return amic_reg_control_get(ucontrol, AMIC_1A);
+}
+
+static int amic1a_reg_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return amic_reg_control_put(ucontrol, AMIC_1A);
+}
+
+static int amic1b_reg_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return amic_reg_control_get(ucontrol, AMIC_1B);
+}
+
+static int amic1b_reg_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return amic_reg_control_put(ucontrol, AMIC_1B);
+}
+
+static int amic2_reg_control_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return amic_reg_control_get(ucontrol, AMIC_2);
+}
+
+static int amic2_reg_control_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return amic_reg_control_put(ucontrol, AMIC_2);
+}
+
+static const struct snd_kcontrol_new mic1a_regulator_control = \
+ SOC_ENUM_EXT("Mic 1A Regulator", soc_enum_amicconf,
+ amic1a_reg_control_get, amic1a_reg_control_put);
+static const struct snd_kcontrol_new mic1b_regulator_control = \
+ SOC_ENUM_EXT("Mic 1B Regulator", soc_enum_amicconf,
+ amic1b_reg_control_get, amic1b_reg_control_put);
+static const struct snd_kcontrol_new mic2_regulator_control = \
+ SOC_ENUM_EXT("Mic 2 Regulator", soc_enum_amicconf,
+ amic2_reg_control_get, amic2_reg_control_put);
+
/* DAPM-events */
static int dapm_audioreg_event(struct snd_soc_dapm_widget *w,
@@ -365,31 +500,38 @@ static int dapm_audioreg_event(struct snd_soc_dapm_widget *w,
return 0;
}
-
-static int dapm_mic1reg_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
+static int dapm_amicreg_event(enum amic_idx amic_id, int event)
{
int ret = 0;
+ mutex_lock(&amic_conf_lock);
+
if (SND_SOC_DAPM_EVENT_ON(event))
- ret = enable_regulator(REGULATOR_AMIC1);
- else
- disable_regulator(REGULATOR_AMIC1);
+ ret = claim_amic_regulator(amic_id);
+ else if (amic_info[amic_id].enabled)
+ release_amic_regulator(amic_id);
+
+ mutex_unlock(&amic_conf_lock);
return ret;
}
-static int dapm_mic2reg_event(struct snd_soc_dapm_widget *w,
+static int dapm_amic1areg_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- int ret = 0;
+ return dapm_amicreg_event(AMIC_1A, event);
+}
- if (SND_SOC_DAPM_EVENT_ON(event))
- ret = enable_regulator(REGULATOR_AMIC2);
- else
- disable_regulator(REGULATOR_AMIC2);
+static int dapm_amic1breg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return dapm_amicreg_event(AMIC_1B, event);
+}
- return ret;
+static int dapm_amic2reg_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ return dapm_amicreg_event(AMIC_2, event);
}
static int dapm_dmicreg_event(struct snd_soc_dapm_widget *w,
@@ -408,13 +550,20 @@ static int dapm_dmicreg_event(struct snd_soc_dapm_widget *w,
/* DAPM-widgets */
static const struct snd_soc_dapm_widget ux500_ab8500_dapm_widgets[] = {
- SND_SOC_DAPM_SUPPLY("AUDIO Regulator", SND_SOC_NOPM, 0, 0, dapm_audioreg_event,
+ SND_SOC_DAPM_SUPPLY("AUDIO Regulator",
+ SND_SOC_NOPM, 0, 0, dapm_audioreg_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("AMIC1 Regulator", SND_SOC_NOPM, 0, 0, dapm_mic1reg_event,
+ SND_SOC_DAPM_SUPPLY("AMIC1A Regulator",
+ SND_SOC_NOPM, 0, 0, dapm_amic1areg_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("AMIC2 Regulator", SND_SOC_NOPM, 0, 0, dapm_mic2reg_event,
+ SND_SOC_DAPM_SUPPLY("AMIC1B Regulator",
+ SND_SOC_NOPM, 0, 0, dapm_amic1breg_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
- SND_SOC_DAPM_SUPPLY("DMIC Regulator", SND_SOC_NOPM, 0, 0, dapm_dmicreg_event,
+ SND_SOC_DAPM_SUPPLY("AMIC2 Regulator",
+ SND_SOC_NOPM, 0, 0, dapm_amic2reg_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("DMIC Regulator",
+ SND_SOC_NOPM, 0, 0, dapm_dmicreg_event,
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
};
@@ -426,11 +575,10 @@ static const struct snd_soc_dapm_route ux500_ab8500_dapm_intercon[] = {
{"DAC", NULL, "AUDIO Regulator"},
{"ADC", NULL, "AUDIO Regulator"},
- /* Power AMIC1-regulator when MIC1 is enabled */
- {"MIC1 Enable", NULL, "AMIC1 Regulator"},
-
- /* Power AMIC2-regulator when MIC1 is enabled */
- {"MIC2 Enable", NULL, "AMIC2 Regulator"},
+ /* Power configured regulator when an analog mic is enabled */
+ {"MIC1A Input", NULL, "AMIC1A Regulator"},
+ {"MIC1B Input", NULL, "AMIC1B Regulator"},
+ {"MIC2 Input", NULL, "AMIC2 Regulator"},
/* Power DMIC-regulator when any digital mic is enabled */
{"DMic 1", NULL, "DMIC Regulator"},
@@ -642,8 +790,16 @@ int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd)
}
/* Add controls */
- snd_ctl_add(codec->card->snd_card, snd_ctl_new1(&mclk_input_control, codec));
- snd_ctl_add(codec->card->snd_card, snd_ctl_new1(&anc_status_control, codec));
+ snd_ctl_add(codec->card->snd_card, snd_ctl_new1(
+ &mclk_input_control, codec));
+ snd_ctl_add(codec->card->snd_card, snd_ctl_new1(
+ &anc_status_control, codec));
+ snd_ctl_add(codec->card->snd_card, snd_ctl_new1(
+ &mic1a_regulator_control, codec));
+ snd_ctl_add(codec->card->snd_card, snd_ctl_new1(
+ &mic1b_regulator_control, codec));
+ snd_ctl_add(codec->card->snd_card, snd_ctl_new1(
+ &mic2_regulator_control, codec));
/* Get references to clock-nodes */
clk_ptr_sysclk = NULL;
@@ -690,6 +846,9 @@ int ux500_ab8500_machine_codec_init(struct snd_soc_pcm_runtime *rtd)
ab8500_power_count = 0;
+ reg_claim[REGULATOR_AMIC1] = 0;
+ reg_claim[REGULATOR_AMIC2] = 0;
+
/* Add DAPM-widgets */
ret = add_widgets(codec);
if (ret < 0) {