diff options
Diffstat (limited to 'sound/soc/codecs/wm8994.c')
-rw-r--r-- | sound/soc/codecs/wm8994.c | 533 |
1 files changed, 400 insertions, 133 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 93d27b660257..9685dff44dd8 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -686,14 +686,23 @@ static void wm1811_jackdet_set_mode(struct snd_soc_codec *codec, u16 mode) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + if (!wm8994->jackdet || !wm8994->jack_cb) + return; + if (wm8994->active_refcount) mode = WM1811_JACKDET_MODE_AUDIO; + if (mode == wm8994->jackdet_mode) + return; + + wm8994->jackdet_mode = mode; + + /* Always use audio mode to detect while the system is active */ + if (mode != WM1811_JACKDET_MODE_NONE) + mode = WM1811_JACKDET_MODE_AUDIO; + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, WM1811_JACKDET_MODE_MASK, mode); - - if (mode == WM1811_JACKDET_MODE_MIC) - msleep(2); } static void active_reference(struct snd_soc_codec *codec) @@ -707,15 +716,8 @@ static void active_reference(struct snd_soc_codec *codec) dev_dbg(codec->dev, "Active refcount incremented, now %d\n", wm8994->active_refcount); - if (wm8994->active_refcount == 1) { - /* If we're using jack detection go into audio mode */ - if (wm8994->jackdet && wm8994->jack_cb) { - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM1811_JACKDET_MODE_MASK, - WM1811_JACKDET_MODE_AUDIO); - msleep(2); - } - } + /* If we're using jack detection go into audio mode */ + wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_AUDIO); mutex_unlock(&wm8994->accdet_lock); } @@ -734,16 +736,12 @@ static void active_dereference(struct snd_soc_codec *codec) if (wm8994->active_refcount == 0) { /* Go into appropriate detection only mode */ - if (wm8994->jackdet && wm8994->jack_cb) { - if (wm8994->jack_mic || wm8994->mic_detecting) - mode = WM1811_JACKDET_MODE_MIC; - else - mode = WM1811_JACKDET_MODE_JACK; + if (wm8994->jack_mic || wm8994->mic_detecting) + mode = WM1811_JACKDET_MODE_MIC; + else + mode = WM1811_JACKDET_MODE_JACK; - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM1811_JACKDET_MODE_MASK, - mode); - } + wm1811_jackdet_set_mode(codec, mode); } mutex_unlock(&wm8994->accdet_lock); @@ -770,28 +768,77 @@ static void vmid_reference(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + pm_runtime_get_sync(codec->dev); + wm8994->vmid_refcount++; dev_dbg(codec->dev, "Referencing VMID, refcount is now %d\n", wm8994->vmid_refcount); if (wm8994->vmid_refcount == 1) { - /* Startup bias, VMID ramp & buffer */ - snd_soc_update_bits(codec, WM8994_ANTIPOP_2, - WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - WM8994_VMID_RAMP_MASK, - WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - (0x11 << WM8994_VMID_RAMP_SHIFT)); + snd_soc_update_bits(codec, WM8994_ANTIPOP_1, + WM8994_LINEOUT1_DISCH | + WM8994_LINEOUT2_DISCH, 0); - /* 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); + wm_hubs_vmid_ena(codec); + + switch (wm8994->vmid_mode) { + default: + WARN_ON(0 == "Invalid VMID mode"); + case WM8994_VMID_NORMAL: + /* Startup bias, 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 | + (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); + + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM8994_VMID_RAMP_MASK | + WM8994_BIAS_SRC, + 0); + break; + + 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); - msleep(20); + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM8994_VMID_RAMP_MASK | + WM8994_BIAS_SRC, + 0); + break; + } } } @@ -805,30 +852,55 @@ static void vmid_dereference(struct snd_soc_codec *codec) wm8994->vmid_refcount); 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, WM8994_BIAS_SRC | - WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - WM8994_VMID_RAMP_MASK, + WM8994_VMID_DISCH, WM8994_BIAS_SRC | - WM8994_STARTUP_BIAS_ENA | - WM8994_VMID_BUF_ENA | - (1 << WM8994_VMID_RAMP_SHIFT)); + WM8994_VMID_DISCH); - /* Disable main biases */ - snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_1, - WM8994_BIAS_ENA | - WM8994_VMID_SEL_MASK, 0); + switch (wm8994->vmid_mode) { + case WM8994_VMID_FORCE: + msleep(350); + break; + default: + break; + } + + snd_soc_update_bits(codec, WM8994_ADDITIONAL_CONTROL, + WM8994_VROI, WM8994_VROI); - /* Discharge line */ + /* Active discharge */ snd_soc_update_bits(codec, WM8994_ANTIPOP_1, WM8994_LINEOUT1_DISCH | WM8994_LINEOUT2_DISCH, WM8994_LINEOUT1_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 */ snd_soc_update_bits(codec, WM8994_ANTIPOP_2, @@ -836,7 +908,15 @@ static void vmid_dereference(struct snd_soc_codec *codec) WM8994_STARTUP_BIAS_ENA | WM8994_VMID_BUF_ENA | 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); } static int vmid_event(struct snd_soc_dapm_widget *w, @@ -1450,17 +1530,17 @@ SND_SOC_DAPM_AIF_IN_E("AIF2DACR", NULL, 0, WM8994_POWER_MANAGEMENT_5, 12, 0, wm8958_aif_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), -SND_SOC_DAPM_AIF_IN("AIF1DACDAT", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_AIF_IN("AIF2DACDAT", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIF1DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIF2DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF1ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF2ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("AIF1DAC Mux", SND_SOC_NOPM, 0, 0, &aif1dac_mux), SND_SOC_DAPM_MUX("AIF2DAC Mux", SND_SOC_NOPM, 0, 0, &aif2dac_mux), SND_SOC_DAPM_MUX("AIF2ADC Mux", SND_SOC_NOPM, 0, 0, &aif2adc_mux), -SND_SOC_DAPM_AIF_IN("AIF3DACDAT", "AIF3 Playback", 0, SND_SOC_NOPM, 0, 0), -SND_SOC_DAPM_AIF_OUT("AIF3ADCDAT", "AIF3 Capture", 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_IN("AIF3DACDAT", NULL, 0, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_AIF_OUT("AIF3ADCDAT", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_SUPPLY("TOCLK", WM8994_CLOCKING_1, 4, 0, NULL, 0), @@ -1575,6 +1655,14 @@ static const struct snd_soc_dapm_route intercon[] = { { "TOCLK", NULL, "CLK_SYS" }, + { "AIF1DACDAT", NULL, "AIF1 Playback" }, + { "AIF2DACDAT", NULL, "AIF2 Playback" }, + { "AIF3DACDAT", NULL, "AIF3 Playback" }, + + { "AIF1 Capture", NULL, "AIF1ADCDAT" }, + { "AIF2 Capture", NULL, "AIF2ADCDAT" }, + { "AIF3 Capture", NULL, "AIF3ADCDAT" }, + /* AIF1 outputs */ { "AIF1ADC1L", NULL, "AIF1ADC1L Mixer" }, { "AIF1ADC1L Mixer", "ADC/DMIC Switch", "ADCL Mux" }, @@ -1887,7 +1975,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, WM8994_FLL1_OUTDIV_MASK | WM8994_FLL1_FRATIO_MASK, reg); - snd_soc_write(codec, WM8994_FLL1_CONTROL_3 + reg_offset, fll.k); + snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_3 + reg_offset, + WM8994_FLL1_K_MASK, fll.k); snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_4 + reg_offset, WM8994_FLL1_N_MASK, @@ -2065,6 +2154,8 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; + wm_hubs_set_bias_level(codec, level); + switch (level) { case SND_SOC_BIAS_ON: break; @@ -2159,11 +2250,61 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, wm8994->cur_fw = NULL; break; } + codec->dapm.bias_level = level; 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) { struct snd_soc_codec *codec = dai->codec; @@ -2645,6 +2786,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, + .sig_bits = 24, }, .capture = { .stream_name = "AIF1 Capture", @@ -2652,6 +2794,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, + .sig_bits = 24, }, .ops = &wm8994_aif1_dai_ops, }, @@ -2664,6 +2807,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, + .sig_bits = 24, }, .capture = { .stream_name = "AIF2 Capture", @@ -2671,6 +2815,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, + .sig_bits = 24, }, .probe = wm8994_aif2_probe, .ops = &wm8994_aif2_dai_ops, @@ -2684,6 +2829,7 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, + .sig_bits = 24, }, .capture = { .stream_name = "AIF3 Capture", @@ -2691,13 +2837,14 @@ static struct snd_soc_dai_driver wm8994_dai[] = { .channels_max = 2, .rates = WM8994_RATES, .formats = WM8994_FORMATS, - }, + .sig_bits = 24, + }, .ops = &wm8994_aif3_dai_ops, } }; #ifdef CONFIG_PM -static int wm8994_suspend(struct snd_soc_codec *codec) +static int wm8994_codec_suspend(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; @@ -2731,7 +2878,7 @@ static int wm8994_suspend(struct snd_soc_codec *codec) return 0; } -static int wm8994_resume(struct snd_soc_codec *codec) +static int wm8994_codec_resume(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994 *control = wm8994->wm8994; @@ -2753,13 +2900,6 @@ static int wm8994_resume(struct snd_soc_codec *codec) codec->cache_only = 0; } - /* Restore the registers */ - ret = snd_soc_cache_sync(codec); - if (ret != 0) - dev_err(codec->dev, "Failed to sync cache: %d\n", ret); - - wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { if (!wm8994->fll_suspend[i].out) continue; @@ -2787,6 +2927,7 @@ static int wm8994_resume(struct snd_soc_codec *codec) WM1811_JACKDET_MODE_JACK); break; } + break; case WM8958: if (wm8994->jack_cb) snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, @@ -2797,8 +2938,8 @@ static int wm8994_resume(struct snd_soc_codec *codec) return 0; } #else -#define wm8994_suspend NULL -#define wm8994_resume NULL +#define wm8994_codec_suspend NULL +#define wm8994_codec_resume NULL #endif static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994) @@ -2861,7 +3002,7 @@ static void wm8994_handle_retune_mobile_pdata(struct wm8994_priv *wm8994) wm8994->retune_mobile_enum.max = wm8994->num_retune_mobile_texts; wm8994->retune_mobile_enum.texts = wm8994->retune_mobile_texts; - ret = snd_soc_add_controls(wm8994->codec, controls, + ret = snd_soc_add_codec_controls(wm8994->codec, controls, ARRAY_SIZE(controls)); if (ret != 0) dev_err(wm8994->codec->dev, @@ -2914,7 +3055,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) wm8994->drc_enum.max = pdata->num_drc_cfgs; wm8994->drc_enum.texts = wm8994->drc_texts; - ret = snd_soc_add_controls(wm8994->codec, controls, + ret = snd_soc_add_codec_controls(wm8994->codec, controls, ARRAY_SIZE(controls)); if (ret != 0) dev_err(wm8994->codec->dev, @@ -2930,7 +3071,7 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) if (pdata->num_retune_mobile_cfgs) wm8994_handle_retune_mobile_pdata(wm8994); else - snd_soc_add_controls(wm8994->codec, wm8994_eq_controls, + snd_soc_add_codec_controls(wm8994->codec, wm8994_eq_controls, ARRAY_SIZE(wm8994_eq_controls)); for (i = 0; i < ARRAY_SIZE(pdata->micbias); i++) { @@ -2947,8 +3088,6 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) * @codec: WM8994 codec * @jack: jack to report detection events on * @micbias: microphone bias to detect on - * @det: value to report for presence detection - * @shrt: value to report for short detection * * Enable microphone detection via IRQ on the WM8994. If GPIOs are * being used to bring out signals to the processor then only platform @@ -2959,43 +3098,63 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) * and micbias2_lvl platform data members. */ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, - int micbias, int det, int shrt) + int micbias) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_micdet *micdet; struct wm8994 *control = wm8994->wm8994; - int reg; + int reg, ret; - if (control->type != WM8994) + if (control->type != WM8994) { + dev_warn(codec->dev, "Not a WM8994\n"); return -EINVAL; + } switch (micbias) { case 1: micdet = &wm8994->micdet[0]; + if (jack) + ret = snd_soc_dapm_force_enable_pin(&codec->dapm, + "MICBIAS1"); + else + ret = snd_soc_dapm_disable_pin(&codec->dapm, + "MICBIAS1"); break; case 2: micdet = &wm8994->micdet[1]; + if (jack) + ret = snd_soc_dapm_force_enable_pin(&codec->dapm, + "MICBIAS1"); + else + ret = snd_soc_dapm_disable_pin(&codec->dapm, + "MICBIAS1"); break; default: + dev_warn(codec->dev, "Invalid MICBIAS %d\n", micbias); return -EINVAL; - } + } + + if (ret != 0) + dev_warn(codec->dev, "Failed to configure MICBIAS%d: %d\n", + micbias, ret); - dev_dbg(codec->dev, "Configuring microphone detection on %d: %x %x\n", - micbias, det, shrt); + dev_dbg(codec->dev, "Configuring microphone detection on %d %p\n", + micbias, jack); /* Store the configuration */ micdet->jack = jack; - micdet->det = det; - micdet->shrt = shrt; + micdet->detecting = true; /* If either of the jacks is set up then enable detection */ if (wm8994->micdet[0].jack || wm8994->micdet[1].jack) reg = WM8994_MICD_ENA; - else + else reg = 0; snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, reg); + snd_soc_dapm_sync(&codec->dapm); + return 0; } EXPORT_SYMBOL_GPL(wm8994_mic_detect); @@ -3021,20 +3180,42 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) dev_dbg(codec->dev, "Microphone status: %x\n", reg); report = 0; - if (reg & WM8994_MIC1_DET_STS) - report |= priv->micdet[0].det; - if (reg & WM8994_MIC1_SHRT_STS) - report |= priv->micdet[0].shrt; + if (reg & WM8994_MIC1_DET_STS) { + if (priv->micdet[0].detecting) + report = SND_JACK_HEADSET; + } + if (reg & WM8994_MIC1_SHRT_STS) { + if (priv->micdet[0].detecting) + report = SND_JACK_HEADPHONE; + else + report |= SND_JACK_BTN_0; + } + if (report) + priv->micdet[0].detecting = false; + else + priv->micdet[0].detecting = true; + snd_soc_jack_report(priv->micdet[0].jack, report, - priv->micdet[0].det | priv->micdet[0].shrt); + SND_JACK_HEADSET | SND_JACK_BTN_0); report = 0; - if (reg & WM8994_MIC2_DET_STS) - report |= priv->micdet[1].det; - if (reg & WM8994_MIC2_SHRT_STS) - report |= priv->micdet[1].shrt; + if (reg & WM8994_MIC2_DET_STS) { + if (priv->micdet[1].detecting) + report = SND_JACK_HEADSET; + } + if (reg & WM8994_MIC2_SHRT_STS) { + if (priv->micdet[1].detecting) + report = SND_JACK_HEADPHONE; + else + report |= SND_JACK_BTN_0; + } + if (report) + priv->micdet[1].detecting = false; + else + priv->micdet[1].detecting = true; + snd_soc_jack_report(priv->micdet[1].jack, report, - priv->micdet[1].det | priv->micdet[1].shrt); + SND_JACK_HEADSET | SND_JACK_BTN_0); return IRQ_HANDLED; } @@ -3083,7 +3264,7 @@ static void wm8958_default_micdet(u16 status, void *data) } - if (wm8994->mic_detecting && status & 0x4) { + if (wm8994->mic_detecting && status & 0xfc) { dev_dbg(codec->dev, "Detected headphone\n"); wm8994->mic_detecting = false; @@ -3094,11 +3275,23 @@ static void wm8958_default_micdet(u16 status, void *data) /* If we have jackdet that will detect removal */ if (wm8994->jackdet) { + mutex_lock(&wm8994->accdet_lock); + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK); + + mutex_unlock(&wm8994->accdet_lock); + + if (wm8994->pdata->jd_ext_cap) { + mutex_lock(&codec->mutex); + snd_soc_dapm_disable_pin(&codec->dapm, + "MICBIAS2"); + snd_soc_dapm_sync(&codec->dapm); + mutex_unlock(&codec->mutex); + } } } @@ -3133,6 +3326,7 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) struct wm8994_priv *wm8994 = data; struct snd_soc_codec *codec = wm8994->codec; int reg; + bool present; mutex_lock(&wm8994->accdet_lock); @@ -3145,11 +3339,17 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) dev_dbg(codec->dev, "JACKDET %x\n", reg); - if (reg & WM1811_JACKDET_LVL) { + present = reg & WM1811_JACKDET_LVL; + + if (present) { dev_dbg(codec->dev, "Jack detected\n"); - snd_soc_jack_report(wm8994->micdet[0].jack, - SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, 0); + + /* Disable debounce while inserted */ + snd_soc_update_bits(codec, WM1811_JACKDET_CTRL, + WM1811_JACKDET_DB, 0); /* * Start off measument of microphone impedence to find @@ -3157,14 +3357,18 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) */ wm8994->mic_detecting = true; wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_MIC); + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, WM8958_MICD_ENA); } else { dev_dbg(codec->dev, "Jack not detected\n"); - snd_soc_jack_report(wm8994->micdet[0].jack, 0, - SND_JACK_MECHANICAL | SND_JACK_HEADSET | - wm8994->btn_mask); + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, WM8958_MICB2_DISCH); + + /* Enable debounce while removed */ + snd_soc_update_bits(codec, WM1811_JACKDET_CTRL, + WM1811_JACKDET_DB, WM1811_JACKDET_DB); wm8994->mic_detecting = false; wm8994->jack_mic = false; @@ -3175,6 +3379,28 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) mutex_unlock(&wm8994->accdet_lock); + /* If required for an external cap force MICBIAS on */ + if (wm8994->pdata->jd_ext_cap) { + mutex_lock(&codec->mutex); + + if (present) + snd_soc_dapm_force_enable_pin(&codec->dapm, + "MICBIAS2"); + else + snd_soc_dapm_disable_pin(&codec->dapm, "MICBIAS2"); + + snd_soc_dapm_sync(&codec->dapm); + mutex_unlock(&codec->mutex); + } + + if (present) + snd_soc_jack_report(wm8994->micdet[0].jack, + SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + else + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + SND_JACK_MECHANICAL | SND_JACK_HEADSET | + wm8994->btn_mask); + return IRQ_HANDLED; } @@ -3217,6 +3443,7 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS"); + snd_soc_dapm_sync(&codec->dapm); wm8994->micdet[0].jack = jack; wm8994->jack_cb = cb; @@ -3247,6 +3474,9 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, * otherwise jump straight to microphone detection. */ if (wm8994->jackdet) { + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_DISCH, + WM8958_MICB2_DISCH); snd_soc_update_bits(codec, WM8994_LDO_1, WM8994_LDO1_DISCH, 0); wm1811_jackdet_set_mode(codec, @@ -3259,7 +3489,9 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } else { snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); + wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_NONE); snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS"); + snd_soc_dapm_sync(&codec->dapm); } return 0; @@ -3272,17 +3504,13 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) struct snd_soc_codec *codec = wm8994->codec; int reg, count; - mutex_lock(&wm8994->accdet_lock); - /* * Jack detection may have detected a removal simulataneously * with an update of the MICDET status; if so it will have * stopped detection and we can ignore this interrupt. */ - if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) { - mutex_unlock(&wm8994->accdet_lock); + if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) return IRQ_HANDLED; - } /* We may occasionally read a detection without an impedence * range being provided - if that happens loop again. @@ -3291,7 +3519,6 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) do { reg = snd_soc_read(codec, WM8958_MIC_DETECT_3); if (reg < 0) { - mutex_unlock(&wm8994->accdet_lock); dev_err(codec->dev, "Failed to read mic detect status: %d\n", reg); @@ -3322,8 +3549,6 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) dev_warn(codec->dev, "Accessory detection with no callback\n"); out: - mutex_unlock(&wm8994->accdet_lock); - return IRQ_HANDLED; } @@ -3357,23 +3582,16 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data) static int wm8994_codec_probe(struct snd_soc_codec *codec) { struct wm8994 *control = dev_get_drvdata(codec->dev->parent); - struct wm8994_priv *wm8994; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct snd_soc_dapm_context *dapm = &codec->dapm; unsigned int reg; int ret, i; + wm8994->codec = codec; codec->control_data = control->regmap; - wm8994 = devm_kzalloc(codec->dev, sizeof(struct wm8994_priv), - GFP_KERNEL); - if (wm8994 == NULL) - return -ENOMEM; - snd_soc_codec_set_drvdata(codec, wm8994); - snd_soc_codec_set_cache_io(codec, 16, 16, SND_SOC_REGMAP); - wm8994->wm8994 = dev_get_drvdata(codec->dev->parent); - wm8994->pdata = dev_get_platdata(codec->dev->parent); wm8994->codec = codec; mutex_init(&wm8994->accdet_lock); @@ -3388,12 +3606,20 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) WM8994_IRQ_MIC1_DET; pm_runtime_enable(codec->dev); - pm_runtime_resume(codec->dev); + pm_runtime_idle(codec->dev); + + /* By default use idle_bias_off, will override for WM8994 */ + codec->dapm.idle_bias_off = 1; /* Set revision-specific configuration */ wm8994->revision = snd_soc_read(codec, WM8994_CHIP_REVISION); switch (control->type) { case WM8994: + /* Single ended line outputs should have VMID on. */ + if (!wm8994->pdata->lineout1_diff || + !wm8994->pdata->lineout2_diff) + codec->dapm.idle_bias_off = 0; + switch (wm8994->revision) { case 2: case 3: @@ -3411,11 +3637,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM8958: wm8994->hubs.dcs_readback_mode = 1; + wm8994->hubs.hp_startup_mode = 1; break; case WM1811: wm8994->hubs.dcs_readback_mode = 2; wm8994->hubs.no_series_update = 1; + wm8994->hubs.hp_startup_mode = 1; + wm8994->hubs.no_cache_class_w = true; switch (wm8994->revision) { case 0: @@ -3532,6 +3761,9 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->fll_locked_irq = false; } + /* Make sure we can read from the GPIOs if they're inputs */ + pm_runtime_get_sync(codec->dev); + /* Remember if AIFnLRCLK is configured as a GPIO. This should be * configured on init - if a system wants to do this dynamically * at runtime we can deal with that then. @@ -3560,7 +3792,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994->lrclk_shared[1] = 0; } - wm8994_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + pm_runtime_put(codec->dev); /* Latch volume updates (right only; we always do left then right). */ snd_soc_update_bits(codec, WM8994_AIF1_DAC1_LEFT_VOLUME, @@ -3638,7 +3870,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) wm8994_handle_pdata(wm8994); wm_hubs_add_analogue_controls(codec); - snd_soc_add_controls(codec, wm8994_snd_controls, + snd_soc_add_codec_controls(codec, wm8994_snd_controls, ARRAY_SIZE(wm8994_snd_controls)); snd_soc_dapm_new_controls(dapm, wm8994_dapm_widgets, ARRAY_SIZE(wm8994_dapm_widgets)); @@ -3664,7 +3896,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } break; case WM8958: - snd_soc_add_controls(codec, wm8958_snd_controls, + snd_soc_add_codec_controls(codec, wm8958_snd_controls, ARRAY_SIZE(wm8958_snd_controls)); snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, ARRAY_SIZE(wm8958_dapm_widgets)); @@ -3686,7 +3918,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; case WM1811: - snd_soc_add_controls(codec, wm8958_snd_controls, + snd_soc_add_codec_controls(codec, wm8958_snd_controls, ARRAY_SIZE(wm8958_snd_controls)); snd_soc_dapm_new_controls(dapm, wm8958_dapm_widgets, ARRAY_SIZE(wm8958_dapm_widgets)); @@ -3815,24 +4047,27 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) return 0; } -static int wm8994_soc_volatile(struct snd_soc_codec *codec, - unsigned int reg) -{ - return true; -} - static struct snd_soc_codec_driver soc_codec_dev_wm8994 = { .probe = wm8994_codec_probe, .remove = wm8994_codec_remove, - .suspend = wm8994_suspend, - .resume = wm8994_resume, + .suspend = wm8994_codec_suspend, + .resume = wm8994_codec_resume, .set_bias_level = wm8994_set_bias_level, - .reg_cache_size = WM8994_MAX_REGISTER, - .volatile_register = wm8994_soc_volatile, }; static int __devinit wm8994_probe(struct platform_device *pdev) { + struct wm8994_priv *wm8994; + + wm8994 = devm_kzalloc(&pdev->dev, sizeof(struct wm8994_priv), + GFP_KERNEL); + if (wm8994 == NULL) + return -ENOMEM; + platform_set_drvdata(pdev, wm8994); + + wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent); + wm8994->pdata = dev_get_platdata(pdev->dev.parent); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994, wm8994_dai, ARRAY_SIZE(wm8994_dai)); } @@ -3843,11 +4078,43 @@ static int __devexit wm8994_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int wm8994_suspend(struct device *dev) +{ + struct wm8994_priv *wm8994 = dev_get_drvdata(dev); + + /* Drop down to power saving mode when system is suspended */ + if (wm8994->jackdet && !wm8994->active_refcount) + regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + wm8994->jackdet_mode); + + return 0; +} + +static int wm8994_resume(struct device *dev) +{ + struct wm8994_priv *wm8994 = dev_get_drvdata(dev); + + if (wm8994->jackdet && wm8994->jack_cb) + regmap_update_bits(wm8994->wm8994->regmap, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + WM1811_JACKDET_MODE_AUDIO); + + return 0; +} +#endif + +static const struct dev_pm_ops wm8994_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wm8994_suspend, wm8994_resume) +}; + static struct platform_driver wm8994_codec_driver = { .driver = { - .name = "wm8994-codec", - .owner = THIS_MODULE, - }, + .name = "wm8994-codec", + .owner = THIS_MODULE, + .pm = &wm8994_pm_ops, + }, .probe = wm8994_probe, .remove = __devexit_p(wm8994_remove), }; |