Loading include/linux/mfd/wm8994/pdata.h +5 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading include/linux/mfd/wm8994/registers.h +8 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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 */ Loading sound/soc/codecs/wm8994.c +145 −43 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/gcd.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> Loading Loading @@ -1498,6 +1499,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); Loading Loading @@ -1744,6 +1763,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), }; Loading Loading @@ -1875,9 +1897,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" }, Loading Loading @@ -1928,6 +1950,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" }, Loading Loading @@ -2010,15 +2038,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); Loading Loading @@ -2067,6 +2096,8 @@ static int wm8994_get_fll_config(struct fll_div *fll, Nmod = freq_out % freq_in; pr_debug("Nmod=%d\n", Nmod); switch (control->type) { case WM8994: /* Calculate fractional part - scale up so we can round. */ Kpart = FIXED_FLL_SIZE * (long long)Nmod; Loading @@ -2079,8 +2110,18 @@ static int wm8994_get_fll_config(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; 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; } Loading Loading @@ -2144,9 +2185,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; Loading Loading @@ -2191,6 +2232,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 | Loading Loading @@ -2555,17 +2607,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; Loading @@ -2584,6 +2643,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; Loading Loading @@ -2622,12 +2682,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; Loading Loading @@ -2658,6 +2720,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; } Loading Loading @@ -3096,24 +3162,7 @@ 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; 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) Loading Loading @@ -3495,6 +3544,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; Loading @@ -3504,16 +3578,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; } Loading Loading @@ -3598,6 +3665,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); Loading Loading @@ -3780,11 +3849,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 Loading @@ -3794,6 +3885,9 @@ 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); /* We may occasionally read a detection without an impedence Loading Loading @@ -3846,8 +3940,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) goto out; } 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); Loading Loading @@ -3899,6 +3997,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: Loading @@ -3911,6 +4011,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]); Loading sound/soc/codecs/wm8994.h +3 −0 Original line number Diff line number Diff line Loading @@ -134,6 +134,9 @@ struct wm8994_priv { struct mutex accdet_lock; 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; Loading Loading
include/linux/mfd/wm8994/pdata.h +5 −0 Original line number Diff line number Diff line Loading @@ -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. */ Loading
include/linux/mfd/wm8994/registers.h +8 −0 Original line number Diff line number Diff line Loading @@ -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 */ Loading @@ -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 */ Loading
sound/soc/codecs/wm8994.c +145 −43 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/gcd.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> Loading Loading @@ -1498,6 +1499,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); Loading Loading @@ -1744,6 +1763,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), }; Loading Loading @@ -1875,9 +1897,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" }, Loading Loading @@ -1928,6 +1950,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" }, Loading Loading @@ -2010,15 +2038,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); Loading Loading @@ -2067,6 +2096,8 @@ static int wm8994_get_fll_config(struct fll_div *fll, Nmod = freq_out % freq_in; pr_debug("Nmod=%d\n", Nmod); switch (control->type) { case WM8994: /* Calculate fractional part - scale up so we can round. */ Kpart = FIXED_FLL_SIZE * (long long)Nmod; Loading @@ -2079,8 +2110,18 @@ static int wm8994_get_fll_config(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; 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; } Loading Loading @@ -2144,9 +2185,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; Loading Loading @@ -2191,6 +2232,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 | Loading Loading @@ -2555,17 +2607,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; Loading @@ -2584,6 +2643,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; Loading Loading @@ -2622,12 +2682,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; Loading Loading @@ -2658,6 +2720,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; } Loading Loading @@ -3096,24 +3162,7 @@ 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; 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) Loading Loading @@ -3495,6 +3544,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; Loading @@ -3504,16 +3578,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; } Loading Loading @@ -3598,6 +3665,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); Loading Loading @@ -3780,11 +3849,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 Loading @@ -3794,6 +3885,9 @@ 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); /* We may occasionally read a detection without an impedence Loading Loading @@ -3846,8 +3940,12 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data) goto out; } 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); Loading Loading @@ -3899,6 +3997,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: Loading @@ -3911,6 +4011,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]); Loading
sound/soc/codecs/wm8994.h +3 −0 Original line number Diff line number Diff line Loading @@ -134,6 +134,9 @@ struct wm8994_priv { struct mutex accdet_lock; 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; Loading