From d1a0a2995855e8d583c5cf97dbf0f6b376668c45 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 10 May 2013 21:40:10 +0100 Subject: ASoC: wm8994: Support EFS mode for FLL Later WM8994 devices support an enhanced accuracy FLL divisor mode called EFS which allows more precise selection of fractional source to output ratios. Support this on relevant devices. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 49 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 1eb152cb1097..9f32dd8660d5 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -2005,15 +2006,16 @@ struct fll_div { u16 outdiv; u16 n; u16 k; + u16 lambda; u16 clk_ref_div; u16 fll_fratio; }; -static int wm8994_get_fll_config(struct fll_div *fll, +static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, int freq_in, int freq_out) { u64 Kpart; - unsigned int K, Ndiv, Nmod; + unsigned int K, Ndiv, Nmod, gcd_fll; pr_debug("FLL input=%dHz, output=%dHz\n", freq_in, freq_out); @@ -2062,20 +2064,30 @@ static int wm8994_get_fll_config(struct fll_div *fll, Nmod = freq_out % freq_in; pr_debug("Nmod=%d\n", Nmod); - /* Calculate fractional part - scale up so we can round. */ - Kpart = FIXED_FLL_SIZE * (long long)Nmod; + switch (control->type) { + case WM8994: + /* Calculate fractional part - scale up so we can round. */ + Kpart = FIXED_FLL_SIZE * (long long)Nmod; - do_div(Kpart, freq_in); + do_div(Kpart, freq_in); - K = Kpart & 0xFFFFFFFF; + K = Kpart & 0xFFFFFFFF; - if ((K % 10) >= 5) - K += 5; + if ((K % 10) >= 5) + K += 5; - /* Move down to proper range now rounding is done */ - fll->k = K / 10; + /* Move down to proper range now rounding is done */ + fll->k = K / 10; - pr_debug("N=%x K=%x\n", fll->n, fll->k); + pr_debug("N=%x K=%x\n", fll->n, fll->k); + + default: + gcd_fll = gcd(freq_out, freq_in); + + fll->k = (freq_out - (freq_in * fll->n)) / gcd_fll; + fll->lambda = freq_in / gcd_fll; + + } return 0; } @@ -2139,9 +2151,9 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, * analysis bugs spewing warnings. */ if (freq_out) - ret = wm8994_get_fll_config(&fll, freq_in, freq_out); + ret = wm8994_get_fll_config(control, &fll, freq_in, freq_out); else - ret = wm8994_get_fll_config(&fll, wm8994->fll[id].in, + ret = wm8994_get_fll_config(control, &fll, wm8994->fll[id].in, wm8994->fll[id].out); if (ret < 0) return ret; @@ -2186,6 +2198,17 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, WM8994_FLL1_N_MASK, fll.n << WM8994_FLL1_N_SHIFT); + if (fll.lambda) { + snd_soc_update_bits(codec, WM8958_FLL1_EFS_1 + reg_offset, + WM8958_FLL1_LAMBDA_MASK, + fll.lambda); + snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, WM8958_FLL1_EFS_ENA); + } else { + snd_soc_update_bits(codec, WM8958_FLL1_EFS_2 + reg_offset, + WM8958_FLL1_EFS_ENA, 0); + } + snd_soc_update_bits(codec, WM8994_FLL1_CONTROL_5 + reg_offset, WM8994_FLL1_FRC_NCO | WM8958_FLL1_BYP | WM8994_FLL1_REFCLK_DIV_MASK | -- cgit v1.2.3-59-g8ed1b From 571ab6c6f38e44eca40098f58b28e5016c8dbc50 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 15 May 2013 10:09:43 +0300 Subject: ASoC: wm8994: missing break in wm8994_get_fll_config() Smatch complains that: sound/soc/codecs/wm8994.c:2087 wm8994_get_fll_config() warn: missing break? reassigning 'fll->k' Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 9f32dd8660d5..303d755b9342 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2080,6 +2080,7 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, fll->k = K / 10; pr_debug("N=%x K=%x\n", fll->n, fll->k); + break; default: gcd_fll = gcd(freq_out, freq_in); -- cgit v1.2.3-59-g8ed1b From 435705e89265dec3c641fe75deb748f05e232e59 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 20 May 2013 11:16:10 -0500 Subject: ASoC: wm8994: Handle LRCLK inversion for WM8958 and WM1811A On WM8958 and WM1811A separate control of the LRCLK inversion bit is available for the DAC and ADC LRCLKs which for compatibility reasons is done in a new register bit. Since writes to each scheme have no effect on parts using the other just always write to both for simplicity. Signed-off-by: Mark Brown Acked-by: Vinod Koul Tested-by: Samreen Nilofer --- include/linux/mfd/wm8994/registers.h | 8 ++++++++ sound/soc/codecs/wm8994.c | 14 ++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'sound/soc') diff --git a/include/linux/mfd/wm8994/registers.h b/include/linux/mfd/wm8994/registers.h index 053548961c15..db8cef3d5321 100644 --- a/include/linux/mfd/wm8994/registers.h +++ b/include/linux/mfd/wm8994/registers.h @@ -2668,6 +2668,10 @@ /* * R772 (0x304) - AIF1ADC LRCLK */ +#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ #define WM8994_AIF1ADC_LRCLK_DIR 0x0800 /* AIF1ADC_LRCLK_DIR */ #define WM8994_AIF1ADC_LRCLK_DIR_MASK 0x0800 /* AIF1ADC_LRCLK_DIR */ #define WM8994_AIF1ADC_LRCLK_DIR_SHIFT 11 /* AIF1ADC_LRCLK_DIR */ @@ -2679,6 +2683,10 @@ /* * R773 (0x305) - AIF1DAC LRCLK */ +#define WM8958_AIF1_LRCLK_INV 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_MASK 0x1000 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_SHIFT 12 /* AIF1_LRCLK_INV */ +#define WM8958_AIF1_LRCLK_INV_WIDTH 1 /* AIF1_LRCLK_INV */ #define WM8994_AIF1DAC_LRCLK_DIR 0x0800 /* AIF1DAC_LRCLK_DIR */ #define WM8994_AIF1DAC_LRCLK_DIR_MASK 0x0800 /* AIF1DAC_LRCLK_DIR */ #define WM8994_AIF1DAC_LRCLK_DIR_SHIFT 11 /* AIF1DAC_LRCLK_DIR */ diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 303d755b9342..f1c54af45dcf 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2574,17 +2574,24 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) struct wm8994 *control = wm8994->wm8994; int ms_reg; int aif1_reg; + int dac_reg; + int adc_reg; int ms = 0; int aif1 = 0; + int lrclk = 0; switch (dai->id) { case 1: ms_reg = WM8994_AIF1_MASTER_SLAVE; aif1_reg = WM8994_AIF1_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; break; case 2: ms_reg = WM8994_AIF2_MASTER_SLAVE; aif1_reg = WM8994_AIF2_CONTROL_1; + dac_reg = WM8994_AIF1DAC_LRCLK; + adc_reg = WM8994_AIF1ADC_LRCLK; break; default: return -EINVAL; @@ -2603,6 +2610,7 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_B: aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; case SND_SOC_DAIFMT_DSP_A: aif1 |= 0x18; break; @@ -2641,12 +2649,14 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) break; case SND_SOC_DAIFMT_IB_IF: aif1 |= WM8994_AIF1_BCLK_INV | WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; break; case SND_SOC_DAIFMT_IB_NF: aif1 |= WM8994_AIF1_BCLK_INV; break; case SND_SOC_DAIFMT_NB_IF: aif1 |= WM8994_AIF1_LRCLK_INV; + lrclk |= WM8958_AIF1_LRCLK_INV; break; default: return -EINVAL; @@ -2677,6 +2687,10 @@ static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) aif1); snd_soc_update_bits(codec, ms_reg, WM8994_AIF1_MSTR, ms); + snd_soc_update_bits(codec, dac_reg, + WM8958_AIF1_LRCLK_INV, lrclk); + snd_soc_update_bits(codec, adc_reg, + WM8958_AIF1_LRCLK_INV, lrclk); return 0; } -- cgit v1.2.3-59-g8ed1b From 287d03e9cd41a5f60bf96f43f8efea454f1cf74e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 28 May 2013 12:52:07 +0100 Subject: ASoC: wm8994: Remove restore of DAC enable state It's not been needed since the regmap conversion. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index f1c54af45dcf..a265fd42b700 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3131,22 +3131,6 @@ 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; int i, ret; - unsigned int val, mask; - - if (control->revision < 4) { - /* force a HW read */ - ret = regmap_read(control->regmap, - WM8994_POWER_MANAGEMENT_5, &val); - - /* modify the cache only */ - codec->cache_only = 1; - mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | - WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; - val &= mask; - snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, - mask, val); - codec->cache_only = 0; - } for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { if (!wm8994->fll_suspend[i].out) -- cgit v1.2.3-59-g8ed1b From f7dbd399efff631203be9f09c07f128df18a3ee4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 28 May 2013 12:52:09 +0100 Subject: ASoC: wm8994: Ensure lambda is zeroed for WM8994 Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index a265fd42b700..0805d6ff9ff7 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -2078,6 +2078,7 @@ static int wm8994_get_fll_config(struct wm8994 *control, struct fll_div *fll, /* Move down to proper range now rounding is done */ fll->k = K / 10; + fll->lambda = 0; pr_debug("N=%x K=%x\n", fll->n, fll->k); break; -- cgit v1.2.3-59-g8ed1b From 70bd3b298bbbd5a36c55af957bb3b5f727218918 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 29 May 2013 20:28:16 +0100 Subject: ASoC: wm8994: Defer declaration of open circuit microphones Provide a bit of debounce to handle pathological cases with slow input better by allowing the microphone detection to run for a bit longer. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 40 +++++++++++++++++++++++++++++++--------- sound/soc/codecs/wm8994.h | 1 + 2 files changed, 32 insertions(+), 9 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 0805d6ff9ff7..2c2a183da2b6 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3513,6 +3513,31 @@ static void wm8958_button_det(struct snd_soc_codec *codec, u16 status) wm8994->btn_mask); } +static void wm8958_open_circuit_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + open_circuit_work.work); + struct device *dev = wm8994->wm8994->dev; + + wm1811_micd_stop(wm8994->hubs.codec); + + mutex_lock(&wm8994->accdet_lock); + + dev_dbg(dev, "Reporting open circuit\n"); + + wm8994->jack_mic = false; + wm8994->mic_detecting = true; + + wm8958_micd_set_rate(wm8994->hubs.codec); + + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); + + mutex_unlock(&wm8994->accdet_lock); +} + static void wm8958_mic_id(void *data, u16 status) { struct snd_soc_codec *codec = data; @@ -3522,16 +3547,9 @@ static void wm8958_mic_id(void *data, u16 status) if (!(status & WM8958_MICD_STS)) { /* If nothing present then clear our statuses */ dev_dbg(codec->dev, "Detected open circuit\n"); - wm8994->jack_mic = false; - wm8994->mic_detecting = true; - - wm1811_micd_stop(codec); - wm8958_micd_set_rate(codec); - - snd_soc_jack_report(wm8994->micdet[0].jack, 0, - wm8994->btn_mask | - SND_JACK_HEADSET); + schedule_delayed_work(&wm8994->open_circuit_work, + msecs_to_jiffies(2500)); return; } @@ -3812,6 +3830,8 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) return IRQ_HANDLED; + cancel_delayed_work_sync(&wm8994->open_circuit_work); + pm_runtime_get_sync(codec->dev); /* We may occasionally read a detection without an impedence @@ -3911,6 +3931,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) mutex_init(&wm8994->accdet_lock); INIT_DELAYED_WORK(&wm8994->jackdet_bootstrap, wm1811_jackdet_bootstrap); + INIT_DELAYED_WORK(&wm8994->open_circuit_work, + wm8958_open_circuit_work); switch (control->type) { case WM8994: diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 55ddf4d57d9b..9d19a9185d35 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -134,6 +134,7 @@ struct wm8994_priv { struct mutex accdet_lock; struct wm8994_micdet micdet[2]; struct delayed_work mic_work; + struct delayed_work open_circuit_work; bool mic_detecting; bool jack_mic; int btn_mask; -- cgit v1.2.3-59-g8ed1b From 50941968fc9e359a89da2136b11328fe700dbd7d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 30 May 2013 13:42:26 +0100 Subject: ASoC: wm8994: Add digital loopback paths There is loopback control within the audio interfaces, provide control of this as there are some obscure scenarios where this could be used in production. Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 2c2a183da2b6..55a5cc639b90 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -1494,6 +1494,24 @@ static const char *aif1dac_text[] = { "AIF1DACDAT", "AIF3DACDAT", }; +static const char *loopback_text[] = { + "None", "ADCDAT", +}; + +static const struct soc_enum aif1_loopback_enum = + SOC_ENUM_SINGLE(WM8994_AIF1_CONTROL_2, WM8994_AIF1_LOOPBACK_SHIFT, 2, + loopback_text); + +static const struct snd_kcontrol_new aif1_loopback = + SOC_DAPM_ENUM("AIF1 Loopback", aif1_loopback_enum); + +static const struct soc_enum aif2_loopback_enum = + SOC_ENUM_SINGLE(WM8994_AIF2_CONTROL_2, WM8994_AIF2_LOOPBACK_SHIFT, 2, + loopback_text); + +static const struct snd_kcontrol_new aif2_loopback = + SOC_DAPM_ENUM("AIF2 Loopback", aif2_loopback_enum); + static const struct soc_enum aif1dac_enum = SOC_ENUM_SINGLE(WM8994_POWER_MANAGEMENT_6, 0, 2, aif1dac_text); @@ -1740,6 +1758,9 @@ SND_SOC_DAPM_ADC("DMIC1R", NULL, WM8994_POWER_MANAGEMENT_4, 2, 0), SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 1, 0), SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), +SND_SOC_DAPM_MUX("AIF1 Loopback", SND_SOC_NOPM, 0, 0, &aif1_loopback), +SND_SOC_DAPM_MUX("AIF2 Loopback", SND_SOC_NOPM, 0, 0, &aif2_loopback), + SND_SOC_DAPM_POST("Debug log", post_ev), }; @@ -1871,9 +1892,9 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF1DAC2L", NULL, "AIF1DAC Mux" }, { "AIF1DAC2R", NULL, "AIF1DAC Mux" }, - { "AIF1DAC Mux", "AIF1DACDAT", "AIF1DACDAT" }, + { "AIF1DAC Mux", "AIF1DACDAT", "AIF1 Loopback" }, { "AIF1DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, - { "AIF2DAC Mux", "AIF2DACDAT", "AIF2DACDAT" }, + { "AIF2DAC Mux", "AIF2DACDAT", "AIF2 Loopback" }, { "AIF2DAC Mux", "AIF3DACDAT", "AIF3DACDAT" }, { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCL" }, { "AIF2ADC Mux", "AIF2ADCDAT", "AIF2ADCR" }, @@ -1924,6 +1945,12 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACL" }, { "AIF3ADCDAT", "AIF2DACDAT", "AIF2DACR" }, + /* Loopback */ + { "AIF1 Loopback", "ADCDAT", "AIF1ADCDAT" }, + { "AIF1 Loopback", "None", "AIF1DACDAT" }, + { "AIF2 Loopback", "ADCDAT", "AIF2ADCDAT" }, + { "AIF2 Loopback", "None", "AIF2DACDAT" }, + /* Sidetone */ { "Left Sidetone", "ADC/DMIC1", "ADCL Mux" }, { "Left Sidetone", "DMIC2", "DMIC2L" }, -- cgit v1.2.3-59-g8ed1b From 2d01e39b9073361317eb72b390dc2a4a3d76e192 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 30 May 2013 13:42:30 +0100 Subject: ASoC: wm8994: Remove unused variable Signed-off-by: Mark Brown --- sound/soc/codecs/wm8994.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/soc') diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 55a5cc639b90..5d046b1c5a7e 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3157,7 +3157,6 @@ static int wm8994_codec_suspend(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; int i, ret; for (i = 0; i < ARRAY_SIZE(wm8994->fll); i++) { -- cgit v1.2.3-59-g8ed1b From 2da1c4bf765cb32024e5db6fa75dab92916fa3b1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 30 May 2013 13:42:29 +0100 Subject: ASoC: wm8994: Allow debounce before MICDET identification For systems which do not have a jack detection feature allow some debounce to be specified before we perform accessory identification, improving robustness without impacting button detection responsiveness. Signed-off-by: Mark Brown --- include/linux/mfd/wm8994/pdata.h | 5 +++++ sound/soc/codecs/wm8994.c | 35 +++++++++++++++++++++++++++++++++-- sound/soc/codecs/wm8994.h | 2 ++ 3 files changed, 40 insertions(+), 2 deletions(-) (limited to 'sound/soc') diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 68e776594889..b5046f6313a9 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -182,6 +182,11 @@ struct wm8994_pdata { */ int micdet_delay; + /* Delay between microphone detect completing and reporting on + * insert (specified in ms) + */ + int mic_id_delay; + /* IRQ for microphone detection if brought out directly as a * signal. */ diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 5d046b1c5a7e..0f58f003dc1a 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3660,6 +3660,8 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) pm_runtime_get_sync(codec->dev); + cancel_delayed_work_sync(&wm8994->mic_complete_work); + mutex_lock(&wm8994->accdet_lock); reg = snd_soc_read(codec, WM1811_JACKDET_CTRL); @@ -3842,11 +3844,33 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, } EXPORT_SYMBOL_GPL(wm8958_mic_detect); +static void wm8958_mic_work(struct work_struct *work) +{ + struct wm8994_priv *wm8994 = container_of(work, + struct wm8994_priv, + mic_complete_work.work); + struct snd_soc_codec *codec = wm8994->hubs.codec; + + dev_crit(codec->dev, "MIC WORK %x\n", wm8994->mic_status); + + pm_runtime_get_sync(codec->dev); + + mutex_lock(&wm8994->accdet_lock); + + wm8994->mic_id_cb(wm8994->mic_id_cb_data, wm8994->mic_status); + + mutex_unlock(&wm8994->accdet_lock); + + pm_runtime_put(codec->dev); + + dev_crit(codec->dev, "MIC WORK %x DONE\n", wm8994->mic_status); +} + static irqreturn_t wm8958_mic_irq(int irq, void *data) { struct wm8994_priv *wm8994 = data; struct snd_soc_codec *codec = wm8994->hubs.codec; - int reg, count, ret; + int reg, count, ret, id_delay; /* * Jack detection may have detected a removal simulataneously @@ -3856,6 +3880,7 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA)) return IRQ_HANDLED; + cancel_delayed_work_sync(&wm8994->mic_complete_work); cancel_delayed_work_sync(&wm8994->open_circuit_work); pm_runtime_get_sync(codec->dev); @@ -3904,8 +3929,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) } } + wm8994->mic_status = reg; + id_delay = wm8994->wm8994->pdata.mic_id_delay; + if (wm8994->mic_detecting) - wm8994->mic_id_cb(wm8994->mic_id_cb_data, reg); + schedule_delayed_work(&wm8994->mic_complete_work, + msecs_to_jiffies(id_delay)); else wm8958_button_det(codec, reg); @@ -3971,6 +4000,8 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; } + INIT_DELAYED_WORK(&wm8994->mic_complete_work, wm8958_mic_work); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) init_completion(&wm8994->fll_locked[i]); diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index 9d19a9185d35..6536f8d45ac6 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h @@ -135,6 +135,8 @@ struct wm8994_priv { struct wm8994_micdet micdet[2]; struct delayed_work mic_work; struct delayed_work open_circuit_work; + struct delayed_work mic_complete_work; + u16 mic_status; bool mic_detecting; bool jack_mic; int btn_mask; -- cgit v1.2.3-59-g8ed1b