Commit 22f8d055 authored by Mark Brown's avatar Mark Brown

ASoC: wm8994: Provide VMID mode control and fix default sequence

The optimal management of VMID depends on a number of factors which vary
dynamically at runtime, for example the connection to a system docking
station. In some circumstances it is desirable to keep VMID enabled all
the time, in others it is desirable to aggressively power it up and down.

Provide a callback allowing machine driver to configure either the normal
power up/down mode (WM8994_VMID_MODE_NORMAL) or to maintain VMID even
when idle (WM8994_VMID_MODE_FORCE). This callback, wm8994_vmid_mode(),
should be called with the CODEC lock.
Signed-off-by: default avatarMark Brown <broonie@opensource.wolfsonmicro.com>
parent 6f8270cc
...@@ -777,36 +777,68 @@ static void vmid_reference(struct snd_soc_codec *codec) ...@@ -777,36 +777,68 @@ static void vmid_reference(struct snd_soc_codec *codec)
if (wm8994->vmid_refcount == 1) { if (wm8994->vmid_refcount == 1) {
snd_soc_update_bits(codec, WM8994_ANTIPOP_1, snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
WM8994_LINEOUT_VMID_BUF_ENA |
WM8994_LINEOUT1_DISCH | WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH, WM8994_LINEOUT2_DISCH, 0);
WM8994_LINEOUT_VMID_BUF_ENA);
wm_hubs_vmid_ena(codec); wm_hubs_vmid_ena(codec);
/* Startup bias, VMID ramp & buffer */ switch (wm8994->vmid_mode) {
snd_soc_update_bits(codec, WM8994_ANTIPOP_2, default:
WM8994_BIAS_SRC | WARN_ON(0 == "Invalid VMID mode");
WM8994_VMID_DISCH | case WM8994_VMID_NORMAL:
WM8994_STARTUP_BIAS_ENA | /* Startup bias, VMID ramp & buffer */
WM8994_VMID_BUF_ENA | snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
WM8994_VMID_RAMP_MASK, WM8994_BIAS_SRC |
WM8994_BIAS_SRC | WM8994_VMID_DISCH |
WM8994_STARTUP_BIAS_ENA | WM8994_STARTUP_BIAS_ENA |
WM8994_VMID_BUF_ENA | WM8994_VMID_BUF_ENA |
(0x2 << WM8994_VMID_RAMP_SHIFT)); WM8994_VMID_RAMP_MASK,
WM8994_BIAS_SRC |
WM8994_STARTUP_BIAS_ENA |
WM8994_VMID_BUF_ENA |
(0x3 << WM8994_VMID_RAMP_SHIFT));
/* Main bias enable, VMID=2x40k */
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
WM8994_BIAS_ENA |
WM8994_VMID_SEL_MASK,
WM8994_BIAS_ENA | 0x2);
msleep(50);
/* Main bias enable, VMID=2x40k */ snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, WM8994_VMID_RAMP_MASK |
WM8994_BIAS_ENA | WM8994_BIAS_SRC,
WM8994_VMID_SEL_MASK, 0);
WM8994_BIAS_ENA | 0x2); break;
msleep(50); case WM8994_VMID_FORCE:
/* Startup bias, slow VMID ramp & buffer */
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
WM8994_BIAS_SRC |
WM8994_VMID_DISCH |
WM8994_STARTUP_BIAS_ENA |
WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK,
WM8994_BIAS_SRC |
WM8994_STARTUP_BIAS_ENA |
WM8994_VMID_BUF_ENA |
(0x2 << WM8994_VMID_RAMP_SHIFT));
/* Main bias enable, VMID=2x40k */
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
WM8994_BIAS_ENA |
WM8994_VMID_SEL_MASK,
WM8994_BIAS_ENA | 0x2);
msleep(400);
snd_soc_update_bits(codec, WM8994_ANTIPOP_2, snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
WM8994_VMID_RAMP_MASK | WM8994_BIAS_SRC, WM8994_VMID_RAMP_MASK |
0); WM8994_BIAS_SRC,
0);
break;
}
} }
} }
...@@ -820,34 +852,55 @@ static void vmid_dereference(struct snd_soc_codec *codec) ...@@ -820,34 +852,55 @@ static void vmid_dereference(struct snd_soc_codec *codec)
wm8994->vmid_refcount); wm8994->vmid_refcount);
if (wm8994->vmid_refcount == 0) { if (wm8994->vmid_refcount == 0) {
/* Switch over to startup biases */ if (wm8994->hubs.lineout1_se)
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
WM8994_LINEOUT1N_ENA |
WM8994_LINEOUT1P_ENA,
WM8994_LINEOUT1N_ENA |
WM8994_LINEOUT1P_ENA);
if (wm8994->hubs.lineout2_se)
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
WM8994_LINEOUT2N_ENA |
WM8994_LINEOUT2P_ENA,
WM8994_LINEOUT2N_ENA |
WM8994_LINEOUT2P_ENA);
/* Start discharging VMID */
snd_soc_update_bits(codec, WM8994_ANTIPOP_2, snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
WM8994_BIAS_SRC | WM8994_BIAS_SRC |
WM8994_STARTUP_BIAS_ENA | WM8994_VMID_DISCH,
WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK,
WM8994_BIAS_SRC | WM8994_BIAS_SRC |
WM8994_STARTUP_BIAS_ENA | WM8994_VMID_DISCH);
WM8994_VMID_BUF_ENA |
(1 << WM8994_VMID_RAMP_SHIFT));
/* Disable main biases */ switch (wm8994->vmid_mode) {
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, case WM8994_VMID_FORCE:
WM8994_BIAS_ENA | msleep(350);
WM8994_VMID_SEL_MASK, 0); break;
default:
break;
}
/* Discharge VMID */ snd_soc_update_bits(codec, WM8994_ADDITIONAL_CONTROL,
snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM8994_VROI, WM8994_VROI);
WM8994_VMID_DISCH, WM8994_VMID_DISCH);
/* Discharge line */ /* Active discharge */
snd_soc_update_bits(codec, WM8994_ANTIPOP_1, snd_soc_update_bits(codec, WM8994_ANTIPOP_1,
WM8994_LINEOUT1_DISCH | WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH, WM8994_LINEOUT2_DISCH,
WM8994_LINEOUT1_DISCH | WM8994_LINEOUT1_DISCH |
WM8994_LINEOUT2_DISCH); WM8994_LINEOUT2_DISCH);
msleep(5); msleep(150);
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_3,
WM8994_LINEOUT1N_ENA |
WM8994_LINEOUT1P_ENA |
WM8994_LINEOUT2N_ENA |
WM8994_LINEOUT2P_ENA, 0);
snd_soc_update_bits(codec, WM8994_ADDITIONAL_CONTROL,
WM8994_VROI, 0);
/* Switch off startup biases */ /* Switch off startup biases */
snd_soc_update_bits(codec, WM8994_ANTIPOP_2, snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
...@@ -855,6 +908,12 @@ static void vmid_dereference(struct snd_soc_codec *codec) ...@@ -855,6 +908,12 @@ static void vmid_dereference(struct snd_soc_codec *codec)
WM8994_STARTUP_BIAS_ENA | WM8994_STARTUP_BIAS_ENA |
WM8994_VMID_BUF_ENA | WM8994_VMID_BUF_ENA |
WM8994_VMID_RAMP_MASK, 0); WM8994_VMID_RAMP_MASK, 0);
snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1,
WM8994_BIAS_ENA | WM8994_VMID_SEL_MASK, 0);
snd_soc_update_bits(codec, WM8994_ANTIPOP_2,
WM8994_VMID_RAMP_MASK, 0);
} }
pm_runtime_put(codec->dev); pm_runtime_put(codec->dev);
...@@ -2197,6 +2256,55 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, ...@@ -2197,6 +2256,55 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec,
return 0; return 0;
} }
int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
switch (mode) {
case WM8994_VMID_NORMAL:
if (wm8994->hubs.lineout1_se) {
snd_soc_dapm_disable_pin(&codec->dapm,
"LINEOUT1N Driver");
snd_soc_dapm_disable_pin(&codec->dapm,
"LINEOUT1P Driver");
}
if (wm8994->hubs.lineout2_se) {
snd_soc_dapm_disable_pin(&codec->dapm,
"LINEOUT2N Driver");
snd_soc_dapm_disable_pin(&codec->dapm,
"LINEOUT2P Driver");
}
/* Do the sync with the old mode to allow it to clean up */
snd_soc_dapm_sync(&codec->dapm);
wm8994->vmid_mode = mode;
break;
case WM8994_VMID_FORCE:
if (wm8994->hubs.lineout1_se) {
snd_soc_dapm_force_enable_pin(&codec->dapm,
"LINEOUT1N Driver");
snd_soc_dapm_force_enable_pin(&codec->dapm,
"LINEOUT1P Driver");
}
if (wm8994->hubs.lineout2_se) {
snd_soc_dapm_force_enable_pin(&codec->dapm,
"LINEOUT2N Driver");
snd_soc_dapm_force_enable_pin(&codec->dapm,
"LINEOUT2P Driver");
}
wm8994->vmid_mode = mode;
snd_soc_dapm_sync(&codec->dapm);
break;
default:
return -EINVAL;
}
return 0;
}
static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{ {
struct snd_soc_codec *codec = dai->codec; struct snd_soc_codec *codec = dai->codec;
......
...@@ -32,6 +32,11 @@ ...@@ -32,6 +32,11 @@
#define WM8994_FLL_SRC_LRCLK 3 #define WM8994_FLL_SRC_LRCLK 3
#define WM8994_FLL_SRC_BCLK 4 #define WM8994_FLL_SRC_BCLK 4
enum wm8994_vmid_mode {
WM8994_VMID_NORMAL,
WM8994_VMID_FORCE,
};
typedef void (*wm8958_micdet_cb)(u16 status, void *data); typedef void (*wm8958_micdet_cb)(u16 status, void *data);
int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
...@@ -39,6 +44,8 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, ...@@ -39,6 +44,8 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
wm8958_micdet_cb cb, void *cb_data); wm8958_micdet_cb cb, void *cb_data);
int wm8994_vmid_mode(struct snd_soc_codec *codec, enum wm8994_vmid_mode mode);
int wm8958_aif_ev(struct snd_soc_dapm_widget *w, int wm8958_aif_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event); struct snd_kcontrol *kcontrol, int event);
...@@ -75,6 +82,7 @@ struct wm8994_priv { ...@@ -75,6 +82,7 @@ struct wm8994_priv {
int vmid_refcount; int vmid_refcount;
int active_refcount; int active_refcount;
enum wm8994_vmid_mode vmid_mode;
int dac_rates[2]; int dac_rates[2];
int lrclk_shared[2]; int lrclk_shared[2];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment