diff options
Diffstat (limited to 'sound/soc/codecs/wm8994.c')
-rw-r--r-- | sound/soc/codecs/wm8994.c | 659 |
1 files changed, 457 insertions, 202 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index d0c545b..93d27b6 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -38,6 +38,11 @@ #include "wm8994.h" #include "wm_hubs.h" +#define WM1811_JACKDET_MODE_NONE 0x0000 +#define WM1811_JACKDET_MODE_JACK 0x0100 +#define WM1811_JACKDET_MODE_MIC 0x0080 +#define WM1811_JACKDET_MODE_AUDIO 0x0180 + #define WM8994_NUM_DRC 3 #define WM8994_NUM_EQ 3 @@ -53,103 +58,69 @@ static int wm8994_retune_mobile_base[] = { WM8994_AIF2_EQ_GAINS_1, }; -static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) -{ - struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = codec->control_data; - - switch (reg) { - case WM8994_GPIO_1: - case WM8994_GPIO_2: - case WM8994_GPIO_3: - case WM8994_GPIO_4: - case WM8994_GPIO_5: - case WM8994_GPIO_6: - case WM8994_GPIO_7: - case WM8994_GPIO_8: - case WM8994_GPIO_9: - case WM8994_GPIO_10: - case WM8994_GPIO_11: - case WM8994_INTERRUPT_STATUS_1: - case WM8994_INTERRUPT_STATUS_2: - case WM8994_INTERRUPT_RAW_STATUS_2: - return 1; - - case WM8958_DSP2_PROGRAM: - case WM8958_DSP2_CONFIG: - case WM8958_DSP2_EXECCONTROL: - if (control->type == WM8958) - return 1; - else - return 0; +static void wm8958_default_micdet(u16 status, void *data); - default: - break; - } +static const struct wm8958_micd_rate micdet_rates[] = { + { 32768, true, 1, 4 }, + { 32768, false, 1, 1 }, + { 44100 * 256, true, 7, 10 }, + { 44100 * 256, false, 7, 10 }, +}; - if (reg >= WM8994_CACHE_SIZE) - return 0; - return wm8994_access_masks[reg].readable != 0; -} +static const struct wm8958_micd_rate jackdet_rates[] = { + { 32768, true, 0, 1 }, + { 32768, false, 0, 1 }, + { 44100 * 256, true, 7, 10 }, + { 44100 * 256, false, 7, 10 }, +}; -static int wm8994_volatile(struct snd_soc_codec *codec, unsigned int reg) +static void wm8958_micd_set_rate(struct snd_soc_codec *codec) { - if (reg >= WM8994_CACHE_SIZE) - return 1; - - switch (reg) { - case WM8994_SOFTWARE_RESET: - case WM8994_CHIP_REVISION: - case WM8994_DC_SERVO_1: - case WM8994_DC_SERVO_READBACK: - case WM8994_RATE_STATUS: - case WM8994_LDO_1: - case WM8994_LDO_2: - case WM8958_DSP2_EXECCONTROL: - case WM8958_MIC_DETECT_3: - case WM8994_DC_SERVO_4E: - return 1; - default: - return 0; - } -} + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + int best, i, sysclk, val; + bool idle; + const struct wm8958_micd_rate *rates; + int num_rates; -static int wm8994_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - int ret; + if (wm8994->jack_cb != wm8958_default_micdet) + return; - BUG_ON(reg > WM8994_MAX_REGISTER); + idle = !wm8994->jack_mic; - if (!wm8994_volatile(codec, reg)) { - ret = snd_soc_cache_write(codec, reg, value); - if (ret != 0) - dev_err(codec->dev, "Cache write to %x failed: %d\n", - reg, ret); + sysclk = snd_soc_read(codec, WM8994_CLOCKING_1); + if (sysclk & WM8994_SYSCLK_SRC) + sysclk = wm8994->aifclk[1]; + else + sysclk = wm8994->aifclk[0]; + + if (wm8994->pdata && wm8994->pdata->micd_rates) { + rates = wm8994->pdata->micd_rates; + num_rates = wm8994->pdata->num_micd_rates; + } else if (wm8994->jackdet) { + rates = jackdet_rates; + num_rates = ARRAY_SIZE(jackdet_rates); + } else { + rates = micdet_rates; + num_rates = ARRAY_SIZE(micdet_rates); } - return wm8994_reg_write(codec->control_data, reg, value); -} - -static unsigned int wm8994_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - unsigned int val; - int ret; - - BUG_ON(reg > WM8994_MAX_REGISTER); - - if (!wm8994_volatile(codec, reg) && wm8994_readable(codec, reg) && - reg < codec->driver->reg_cache_size) { - ret = snd_soc_cache_read(codec, reg, &val); - if (ret >= 0) - return val; - else - dev_err(codec->dev, "Cache read from %x failed: %d\n", - reg, ret); + best = 0; + for (i = 0; i < num_rates; i++) { + if (rates[i].idle != idle) + continue; + if (abs(rates[i].sysclk - sysclk) < + abs(rates[best].sysclk - sysclk)) + best = i; + else if (rates[best].idle != idle) + best = i; } - return wm8994_reg_read(codec->control_data, reg); + val = rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT + | rates[best].rate << WM8958_MICD_RATE_SHIFT; + + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_BIAS_STARTTIME_MASK | + WM8958_MICD_RATE_MASK, val); } static int configure_aif_clock(struct snd_soc_codec *codec, int aif) @@ -221,8 +192,10 @@ static int configure_clock(struct snd_soc_codec *codec) */ /* If they're equal it doesn't matter which is used */ - if (wm8994->aifclk[0] == wm8994->aifclk[1]) + if (wm8994->aifclk[0] == wm8994->aifclk[1]) { + wm8958_micd_set_rate(codec); return 0; + } if (wm8994->aifclk[0] < wm8994->aifclk[1]) new = WM8994_SYSCLK_SRC; @@ -231,10 +204,10 @@ static int configure_clock(struct snd_soc_codec *codec) change = snd_soc_update_bits(codec, WM8994_CLOCKING_1, WM8994_SYSCLK_SRC, new); - if (!change) - return 0; + if (change) + snd_soc_dapm_sync(&codec->dapm); - snd_soc_dapm_sync(&codec->dapm); + wm8958_micd_set_rate(codec); return 0; } @@ -708,6 +681,74 @@ SOC_SINGLE_TLV("MIXINL IN1RP Boost Volume", WM8994_INPUT_MIXER_1, 8, 1, 0, mixin_boost_tlv), }; +/* We run all mode setting through a function to enforce audio mode */ +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->active_refcount) + 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) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + mutex_lock(&wm8994->accdet_lock); + + wm8994->active_refcount++; + + 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); + } + } + + mutex_unlock(&wm8994->accdet_lock); +} + +static void active_dereference(struct snd_soc_codec *codec) +{ + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + u16 mode; + + mutex_lock(&wm8994->accdet_lock); + + wm8994->active_refcount--; + + dev_dbg(codec->dev, "Active refcount decremented, now %d\n", + wm8994->active_refcount); + + 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; + + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + mode); + } + } + + mutex_unlock(&wm8994->accdet_lock); +} + static int clk_sys_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1768,7 +1809,7 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, unsigned int freq_in, unsigned int freq_out) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = codec->control_data; + struct wm8994 *control = wm8994->wm8994; int reg_offset, ret; struct fll_div fll; u16 reg, aif1, aif2; @@ -1865,6 +1906,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, if (freq_out) { /* Enable VMID if we need it */ if (!was_enabled) { + active_reference(codec); + switch (control->type) { case WM8994: vmid_reference(codec); @@ -1908,6 +1951,8 @@ static int _wm8994_set_fll(struct snd_soc_codec *codec, int id, int src, default: break; } + + active_dereference(codec); } } @@ -2017,20 +2062,33 @@ static int wm8994_set_dai_sysclk(struct snd_soc_dai *dai, static int wm8994_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct wm8994 *control = codec->control_data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->wm8994; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: + /* MICBIAS into regulating mode */ + switch (control->type) { + case WM8958: + case WM1811: + snd_soc_update_bits(codec, WM8958_MICBIAS1, + WM8958_MICB1_MODE, 0); + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_MODE, 0); + break; + default: + break; + } + + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) + active_reference(codec); break; case SND_SOC_BIAS_STANDBY: if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { - pm_runtime_get_sync(codec->dev); - switch (control->type) { case WM8994: if (wm8994->revision < 4) { @@ -2077,25 +2135,40 @@ static int wm8994_set_bias_level(struct snd_soc_codec *codec, WM8994_LINEOUT2_DISCH); } + if (codec->dapm.bias_level == SND_SOC_BIAS_PREPARE) + active_dereference(codec); + /* MICBIAS into bypass mode on newer devices */ + switch (control->type) { + case WM8958: + case WM1811: + snd_soc_update_bits(codec, WM8958_MICBIAS1, + WM8958_MICB1_MODE, + WM8958_MICB1_MODE); + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_MODE, + WM8958_MICB2_MODE); + break; + default: + break; + } break; case SND_SOC_BIAS_OFF: - if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) { + if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) wm8994->cur_fw = NULL; - - pm_runtime_put(codec->dev); - } break; } codec->dapm.bias_level = level; + return 0; } static int wm8994_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct snd_soc_codec *codec = dai->codec; - struct wm8994 *control = codec->control_data; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->wm8994; int ms_reg; int aif1_reg; int ms = 0; @@ -2395,7 +2468,8 @@ static int wm8994_aif3_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; - struct wm8994 *control = codec->control_data; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + struct wm8994 *control = wm8994->wm8994; int aif1_reg; int aif1 = 0; @@ -2536,7 +2610,7 @@ static int wm8994_aif2_probe(struct snd_soc_dai *dai) #define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops wm8994_aif1_dai_ops = { +static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = { .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, @@ -2546,7 +2620,7 @@ static struct snd_soc_dai_ops wm8994_aif1_dai_ops = { .set_tristate = wm8994_set_tristate, }; -static struct snd_soc_dai_ops wm8994_aif2_dai_ops = { +static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = { .set_sysclk = wm8994_set_dai_sysclk, .set_fmt = wm8994_set_dai_fmt, .hw_params = wm8994_hw_params, @@ -2556,7 +2630,7 @@ static struct snd_soc_dai_ops wm8994_aif2_dai_ops = { .set_tristate = wm8994_set_tristate, }; -static struct snd_soc_dai_ops wm8994_aif3_dai_ops = { +static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = { .hw_params = wm8994_aif3_hw_params, .set_tristate = wm8994_set_tristate, }; @@ -2623,10 +2697,10 @@ static struct snd_soc_dai_driver wm8994_dai[] = { }; #ifdef CONFIG_PM -static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state) +static int wm8994_suspend(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = codec->control_data; + struct wm8994 *control = wm8994->wm8994; int i, ret; switch (control->type) { @@ -2634,6 +2708,9 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state) snd_soc_update_bits(codec, WM8994_MICBIAS, WM8994_MICD_ENA, 0); break; case WM1811: + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, 0); + /* Fall through */ case WM8958: snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); @@ -2657,14 +2734,14 @@ static int wm8994_suspend(struct snd_soc_codec *codec, pm_message_t state) static int wm8994_resume(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = codec->control_data; + struct wm8994 *control = wm8994->wm8994; int i, ret; unsigned int val, mask; if (wm8994->revision < 4) { /* force a HW read */ - val = wm8994_reg_read(codec->control_data, - WM8994_POWER_MANAGEMENT_5); + ret = regmap_read(control->regmap, + WM8994_POWER_MANAGEMENT_5, &val); /* modify the cache only */ codec->cache_only = 1; @@ -2703,6 +2780,13 @@ static int wm8994_resume(struct snd_soc_codec *codec) WM8994_MICD_ENA, WM8994_MICD_ENA); break; case WM1811: + if (wm8994->jackdet && wm8994->jack_cb) { + /* Restart from idle */ + snd_soc_update_bits(codec, WM8994_ANTIPOP_2, + WM1811_JACKDET_MODE_MASK, + WM1811_JACKDET_MODE_JACK); + break; + } case WM8958: if (wm8994->jack_cb) snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, @@ -2815,8 +2899,8 @@ static void wm8994_handle_pdata(struct wm8994_priv *wm8994) }; /* We need an array of texts for the enum API */ - wm8994->drc_texts = kmalloc(sizeof(char *) - * pdata->num_drc_cfgs, GFP_KERNEL); + wm8994->drc_texts = devm_kzalloc(wm8994->codec->dev, + sizeof(char *) * pdata->num_drc_cfgs, GFP_KERNEL); if (!wm8994->drc_texts) { dev_err(wm8994->codec->dev, "Failed to allocate %d DRC config texts\n", @@ -2879,7 +2963,7 @@ int wm8994_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); struct wm8994_micdet *micdet; - struct wm8994 *control = codec->control_data; + struct wm8994 *control = wm8994->wm8994; int reg; if (control->type != WM8994) @@ -2962,21 +3046,136 @@ static void wm8958_default_micdet(u16 status, void *data) { struct snd_soc_codec *codec = data; struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - int report = 0; + int report; + + dev_dbg(codec->dev, "MICDET %x\n", status); + + /* Either nothing present or just starting detection */ + if (!(status & WM8958_MICD_STS)) { + if (!wm8994->jackdet) { + /* If nothing present then clear our statuses */ + dev_dbg(codec->dev, "Detected open circuit\n"); + wm8994->jack_mic = false; + wm8994->mic_detecting = true; + + wm8958_micd_set_rate(codec); + + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + wm8994->btn_mask | + SND_JACK_HEADSET); + } + return; + } + + /* If the measurement is showing a high impedence we've got a + * microphone. + */ + if (wm8994->mic_detecting && (status & 0x600)) { + dev_dbg(codec->dev, "Detected microphone\n"); + + wm8994->mic_detecting = false; + wm8994->jack_mic = true; + + wm8958_micd_set_rate(codec); + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, + SND_JACK_HEADSET); + } + + + if (wm8994->mic_detecting && status & 0x4) { + dev_dbg(codec->dev, "Detected headphone\n"); + wm8994->mic_detecting = false; + + wm8958_micd_set_rate(codec); + + snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, + SND_JACK_HEADSET); + + /* If we have jackdet that will detect removal */ + if (wm8994->jackdet) { + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, 0); + + wm1811_jackdet_set_mode(codec, + WM1811_JACKDET_MODE_JACK); + } + } + + /* Report short circuit as a button */ + if (wm8994->jack_mic) { + report = 0; + if (status & 0x4) + report |= SND_JACK_BTN_0; + + if (status & 0x8) + report |= SND_JACK_BTN_1; + + if (status & 0x10) + report |= SND_JACK_BTN_2; + + if (status & 0x20) + report |= SND_JACK_BTN_3; + + if (status & 0x40) + report |= SND_JACK_BTN_4; + + if (status & 0x80) + report |= SND_JACK_BTN_5; + + snd_soc_jack_report(wm8994->micdet[0].jack, report, + wm8994->btn_mask); + } +} + +static irqreturn_t wm1811_jackdet_irq(int irq, void *data) +{ + struct wm8994_priv *wm8994 = data; + struct snd_soc_codec *codec = wm8994->codec; + int reg; + + mutex_lock(&wm8994->accdet_lock); + + reg = snd_soc_read(codec, WM1811_JACKDET_CTRL); + if (reg < 0) { + dev_err(codec->dev, "Failed to read jack status: %d\n", reg); + mutex_unlock(&wm8994->accdet_lock); + return IRQ_NONE; + } + + dev_dbg(codec->dev, "JACKDET %x\n", reg); - /* If nothing present then clear our statuses */ - if (!(status & WM8958_MICD_STS)) - goto done; + if (reg & WM1811_JACKDET_LVL) { + dev_dbg(codec->dev, "Jack detected\n"); - report = SND_JACK_MICROPHONE; + snd_soc_jack_report(wm8994->micdet[0].jack, + SND_JACK_MECHANICAL, SND_JACK_MECHANICAL); + + /* + * Start off measument of microphone impedence to find + * out what's actually there. + */ + 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"); - /* Everything else is buttons; just assign slots */ - if (status & 0x1c) - report |= SND_JACK_BTN_0; + snd_soc_jack_report(wm8994->micdet[0].jack, 0, + SND_JACK_MECHANICAL | SND_JACK_HEADSET | + wm8994->btn_mask); -done: - snd_soc_jack_report(wm8994->micdet[0].jack, report, - SND_JACK_BTN_0 | SND_JACK_MICROPHONE); + wm8994->mic_detecting = false; + wm8994->jack_mic = false; + 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); + + return IRQ_HANDLED; } /** @@ -2999,7 +3198,8 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, wm8958_micdet_cb cb, void *cb_data) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = codec->control_data; + struct wm8994 *control = wm8994->wm8994; + u16 micd_lvl_sel; switch (control->type) { case WM1811: @@ -3016,15 +3216,50 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, cb_data = codec; } + snd_soc_dapm_force_enable_pin(&codec->dapm, "CLK_SYS"); + wm8994->micdet[0].jack = jack; wm8994->jack_cb = cb; wm8994->jack_cb_data = cb_data; - snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, - WM8958_MICD_ENA, WM8958_MICD_ENA); + wm8994->mic_detecting = true; + wm8994->jack_mic = false; + + wm8958_micd_set_rate(codec); + + /* Detect microphones and short circuits by default */ + if (wm8994->pdata->micd_lvl_sel) + micd_lvl_sel = wm8994->pdata->micd_lvl_sel; + else + micd_lvl_sel = 0x41; + + wm8994->btn_mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4 | SND_JACK_BTN_5; + + snd_soc_update_bits(codec, WM8958_MIC_DETECT_2, + WM8958_MICD_LVL_SEL_MASK, micd_lvl_sel); + + WARN_ON(codec->dapm.bias_level > SND_SOC_BIAS_STANDBY); + + /* + * If we can use jack detection start off with that, + * otherwise jump straight to microphone detection. + */ + if (wm8994->jackdet) { + snd_soc_update_bits(codec, WM8994_LDO_1, + WM8994_LDO1_DISCH, 0); + wm1811_jackdet_set_mode(codec, + WM1811_JACKDET_MODE_JACK); + } else { + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, WM8958_MICD_ENA); + } + } else { snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0); + snd_soc_dapm_disable_pin(&codec->dapm, "CLK_SYS"); } return 0; @@ -3037,6 +3272,18 @@ 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); + return IRQ_HANDLED; + } + /* We may occasionally read a detection without an impedence * range being provided - if that happens loop again. */ @@ -3044,6 +3291,7 @@ 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); @@ -3074,6 +3322,8 @@ 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; } @@ -3106,22 +3356,28 @@ static irqreturn_t wm8994_temp_shut(int irq, void *data) static int wm8994_codec_probe(struct snd_soc_codec *codec) { - struct wm8994 *control; + struct wm8994 *control = dev_get_drvdata(codec->dev->parent); struct wm8994_priv *wm8994; struct snd_soc_dapm_context *dapm = &codec->dapm; + unsigned int reg; int ret, i; - codec->control_data = dev_get_drvdata(codec->dev->parent); - control = codec->control_data; + codec->control_data = control->regmap; - wm8994 = kzalloc(sizeof(struct wm8994_priv), GFP_KERNEL); + 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); + for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) init_completion(&wm8994->fll_locked[i]); @@ -3134,25 +3390,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) pm_runtime_enable(codec->dev); pm_runtime_resume(codec->dev); - /* Read our current status back from the chip - we don't want to - * reset as this may interfere with the GPIO or LDO operation. */ - for (i = 0; i < WM8994_CACHE_SIZE; i++) { - if (!wm8994_readable(codec, i) || wm8994_volatile(codec, i)) - continue; - - ret = wm8994_reg_read(codec->control_data, i); - if (ret <= 0) - continue; - - ret = snd_soc_cache_write(codec, i, ret); - if (ret != 0) { - dev_err(codec->dev, - "Failed to initialise cache for 0x%x: %d\n", - i, ret); - goto err; - } - } - /* Set revision-specific configuration */ wm8994->revision = snd_soc_read(codec, WM8994_CHIP_REVISION); switch (control->type) { @@ -3200,14 +3437,14 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; } - wm8994_request_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, wm8994_fifo_error, "FIFO error", codec); - wm8994_request_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, wm8994_temp_warn, "Thermal warning", codec); - wm8994_request_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, + wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, wm8994_temp_shut, "Thermal shutdown", codec); - ret = wm8994_request_irq(codec->control_data, WM8994_IRQ_DCS_DONE, + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, wm_hubs_dcs_done, "DC servo done", &wm8994->hubs); if (ret == 0) @@ -3227,7 +3464,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) ret); } - ret = wm8994_request_irq(codec->control_data, + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994_mic_irq, "Mic 1 short", wm8994); @@ -3236,7 +3473,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) "Failed to request Mic1 short IRQ: %d\n", ret); - ret = wm8994_request_irq(codec->control_data, + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, wm8994_mic_irq, "Mic 2 detect", wm8994); @@ -3245,7 +3482,7 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) "Failed to request Mic2 detect IRQ: %d\n", ret); - ret = wm8994_request_irq(codec->control_data, + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_MIC2_SHRT, wm8994_mic_irq, "Mic 2 short", wm8994); @@ -3270,9 +3507,24 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) } } + switch (control->type) { + case WM1811: + if (wm8994->revision > 1) { + ret = wm8994_request_irq(wm8994->wm8994, + WM8994_IRQ_GPIO(6), + wm1811_jackdet_irq, "JACKDET", + wm8994); + if (ret == 0) + wm8994->jackdet = true; + } + break; + default: + break; + } + wm8994->fll_locked_irq = true; for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) { - ret = wm8994_request_irq(codec->control_data, + ret = wm8994_request_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, wm8994_fll_locked_irq, "FLL lock", &wm8994->fll_locked[i]); @@ -3284,24 +3536,24 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) * configured on init - if a system wants to do this dynamically * at runtime we can deal with that then. */ - ret = wm8994_reg_read(codec->control_data, WM8994_GPIO_1); + ret = regmap_read(control->regmap, WM8994_GPIO_1, ®); if (ret < 0) { dev_err(codec->dev, "Failed to read GPIO1 state: %d\n", ret); goto err_irq; } - if ((ret & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { + if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { wm8994->lrclk_shared[0] = 1; wm8994_dai[0].symmetric_rates = 1; } else { wm8994->lrclk_shared[0] = 0; } - ret = wm8994_reg_read(codec->control_data, WM8994_GPIO_6); + ret = regmap_read(control->regmap, WM8994_GPIO_6, ®); if (ret < 0) { dev_err(codec->dev, "Failed to read GPIO6 state: %d\n", ret); goto err_irq; } - if ((ret & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { + if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) { wm8994->lrclk_shared[1] = 1; wm8994_dai[1].symmetric_rates = 1; } else { @@ -3368,6 +3620,19 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) break; } + /* Put MICBIAS into bypass mode by default on newer devices */ + switch (control->type) { + case WM8958: + case WM1811: + snd_soc_update_bits(codec, WM8958_MICBIAS1, + WM8958_MICB1_MODE, WM8958_MICB1_MODE); + snd_soc_update_bits(codec, WM8958_MICBIAS2, + WM8958_MICB2_MODE, WM8958_MICB2_MODE); + break; + default: + break; + } + wm8994_update_class_w(codec); wm8994_handle_pdata(wm8994); @@ -3479,28 +3744,29 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) return 0; err_irq: - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, wm8994); + if (wm8994->jackdet) + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_SHRT, wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994); if (wm8994->micdet_irq) free_irq(wm8994->micdet_irq, wm8994); for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) - wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, &wm8994->fll_locked[i]); - wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, &wm8994->hubs); - wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec); - wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec); - wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec); -err: - kfree(wm8994); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, codec); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, codec); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, codec); + return ret; } static int wm8994_codec_remove(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); - struct wm8994 *control = codec->control_data; + struct wm8994 *control = wm8994->wm8994; int i; wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF); @@ -3508,24 +3774,27 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) pm_runtime_disable(codec->dev); for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++) - wm8994_free_irq(codec->control_data, WM8994_IRQ_FLL1_LOCK + i, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i, &wm8994->fll_locked[i]); - wm8994_free_irq(codec->control_data, WM8994_IRQ_DCS_DONE, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_DCS_DONE, &wm8994->hubs); - wm8994_free_irq(codec->control_data, WM8994_IRQ_FIFOS_ERR, codec); - wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_SHUT, codec); - wm8994_free_irq(codec->control_data, WM8994_IRQ_TEMP_WARN, codec); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, codec); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, codec); + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, codec); + + if (wm8994->jackdet) + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_GPIO(6), wm8994); switch (control->type) { case WM8994: if (wm8994->micdet_irq) free_irq(wm8994->micdet_irq, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC2_DET, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC2_DET, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_SHRT, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_SHRT, wm8994); - wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, + wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_MIC1_DET, wm8994); break; @@ -3542,27 +3811,24 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) if (wm8994->enh_eq) release_firmware(wm8994->enh_eq); kfree(wm8994->retune_mobile_texts); - kfree(wm8994->drc_texts); - kfree(wm8994); 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, - .read = wm8994_read, - .write = wm8994_write, - .readable_register = wm8994_readable, - .volatile_register = wm8994_volatile, .set_bias_level = wm8994_set_bias_level, - - .reg_cache_size = WM8994_CACHE_SIZE, - .reg_cache_default = wm8994_reg_defaults, - .reg_word_size = 2, - .compress_type = SND_SOC_RBTREE_COMPRESSION, + .reg_cache_size = WM8994_MAX_REGISTER, + .volatile_register = wm8994_soc_volatile, }; static int __devinit wm8994_probe(struct platform_device *pdev) @@ -3586,18 +3852,7 @@ static struct platform_driver wm8994_codec_driver = { .remove = __devexit_p(wm8994_remove), }; -static __init int wm8994_init(void) -{ - return platform_driver_register(&wm8994_codec_driver); -} -module_init(wm8994_init); - -static __exit void wm8994_exit(void) -{ - platform_driver_unregister(&wm8994_codec_driver); -} -module_exit(wm8994_exit); - +module_platform_driver(wm8994_codec_driver); MODULE_DESCRIPTION("ASoC WM8994 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); |