|
@@ -79,6 +79,8 @@ struct twl6040_data {
|
|
|
int codec_powered;
|
|
|
int pll;
|
|
|
int non_lp;
|
|
|
+ int hs_power_mode;
|
|
|
+ int hs_power_mode_locked;
|
|
|
unsigned int clk_in;
|
|
|
unsigned int sysclk;
|
|
|
struct snd_pcm_hw_constraint_list *sysclk_constraints;
|
|
@@ -651,15 +653,26 @@ static int twl6040_power_mode_event(struct snd_soc_dapm_widget *w,
|
|
|
{
|
|
|
struct snd_soc_codec *codec = w->codec;
|
|
|
struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
|
|
+ int ret = 0;
|
|
|
|
|
|
- if (SND_SOC_DAPM_EVENT_ON(event))
|
|
|
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
|
|
|
priv->non_lp++;
|
|
|
- else
|
|
|
+ if (!strcmp(w->name, "Earphone Driver")) {
|
|
|
+ /* Earphone doesn't support low power mode */
|
|
|
+ priv->hs_power_mode_locked = 1;
|
|
|
+ ret = headset_power_mode(codec, 1);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
priv->non_lp--;
|
|
|
+ if (!strcmp(w->name, "Earphone Driver")) {
|
|
|
+ priv->hs_power_mode_locked = 0;
|
|
|
+ ret = headset_power_mode(codec, priv->hs_power_mode);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
msleep(1);
|
|
|
|
|
|
- return 0;
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static void twl6040_hs_jack_report(struct snd_soc_codec *codec,
|
|
@@ -964,6 +977,43 @@ static const struct snd_kcontrol_new hfr_mux_controls =
|
|
|
static const struct snd_kcontrol_new ep_driver_switch_controls =
|
|
|
SOC_DAPM_SINGLE("Switch", TWL6040_REG_EARCTL, 0, 1, 0);
|
|
|
|
|
|
+/* Headset power mode */
|
|
|
+static const char *twl6040_headset_power_texts[] = {
|
|
|
+ "Low-Power", "High-Perfomance",
|
|
|
+};
|
|
|
+
|
|
|
+static const struct soc_enum twl6040_headset_power_enum =
|
|
|
+ SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(twl6040_headset_power_texts),
|
|
|
+ twl6040_headset_power_texts);
|
|
|
+
|
|
|
+static int twl6040_headset_power_get_enum(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
|
|
+
|
|
|
+ ucontrol->value.enumerated.item[0] = priv->hs_power_mode;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int twl6040_headset_power_put_enum(struct snd_kcontrol *kcontrol,
|
|
|
+ struct snd_ctl_elem_value *ucontrol)
|
|
|
+{
|
|
|
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
|
|
|
+ struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
|
|
|
+ int high_perf = ucontrol->value.enumerated.item[0];
|
|
|
+ int ret = 0;
|
|
|
+
|
|
|
+ if (!priv->hs_power_mode_locked)
|
|
|
+ ret = headset_power_mode(codec, high_perf);
|
|
|
+
|
|
|
+ if (!ret)
|
|
|
+ priv->hs_power_mode = high_perf;
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
static const struct snd_kcontrol_new twl6040_snd_controls[] = {
|
|
|
/* Capture gains */
|
|
|
SOC_DOUBLE_TLV("Capture Preamplifier Volume",
|
|
@@ -982,6 +1032,10 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
|
|
|
TWL6040_REG_HFLGAIN, TWL6040_REG_HFRGAIN, 0, 0x1D, 1, hf_tlv),
|
|
|
SOC_SINGLE_TLV("Earphone Playback Volume",
|
|
|
TWL6040_REG_EARCTL, 1, 0xF, 1, ep_tlv),
|
|
|
+
|
|
|
+ SOC_ENUM_EXT("Headset Power Mode", twl6040_headset_power_enum,
|
|
|
+ twl6040_headset_power_get_enum,
|
|
|
+ twl6040_headset_power_put_enum),
|
|
|
};
|
|
|
|
|
|
static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
|
|
@@ -1339,7 +1393,6 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- headset_power_mode(codec, 0);
|
|
|
priv->sysclk_constraints = &lp_constraints;
|
|
|
break;
|
|
|
case TWL6040_SYSCLK_SEL_HPPLL:
|
|
@@ -1348,7 +1401,6 @@ static int twl6040_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
|
|
if (ret)
|
|
|
return ret;
|
|
|
|
|
|
- headset_power_mode(codec, 1);
|
|
|
priv->sysclk_constraints = &hp_constraints;
|
|
|
break;
|
|
|
default:
|