diff options
Diffstat (limited to 'sound/soc/codecs/wm8994.c')
-rw-r--r-- | sound/soc/codecs/wm8994.c | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index b30b2dd3f1f4..948677b581b1 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -21,6 +21,7 @@ #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> +#include <sound/jack.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> @@ -95,6 +96,11 @@ struct wm8994_priv { struct wm8994_micdet micdet[2]; + wm8958_micdet_cb jack_cb; + void *jack_cb_data; + bool jack_is_mic; + bool jack_is_video; + int revision; struct wm8994_pdata *pdata; }; @@ -140,6 +146,7 @@ static int wm8994_volatile(unsigned int reg) case WM8994_LDO_1: case WM8994_LDO_2: case WM8958_DSP2_EXECCONTROL: + case WM8958_MIC_DETECT_3: return 1; default: return 0; @@ -2633,6 +2640,133 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data) return IRQ_HANDLED; } +/* Default microphone detection handler for WM8958 - the user can + * override this if they wish. + */ +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; + + /* If nothing present then clear our statuses */ + if (!(status & WM8958_MICD_STS)) { + wm8994->jack_is_video = false; + wm8994->jack_is_mic = false; + goto done; + } + + /* Assume anything over 475 ohms is a microphone and remember + * that we've seen one (since buttons override it) */ + if (status & 0x600) + wm8994->jack_is_mic = true; + if (wm8994->jack_is_mic) + report |= SND_JACK_MICROPHONE; + + /* Video has an impedence of approximately 75 ohms; assume + * this isn't used as a button and remember it since buttons + * override it. */ + if (status & 0x40) + wm8994->jack_is_video = true; + if (wm8994->jack_is_video) + report |= SND_JACK_VIDEOOUT; + + /* Everything else is buttons; just assign slots */ + 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 & 0x80) + report |= SND_JACK_BTN_4; + if (status & 0x100) + report |= SND_JACK_BTN_5; + +done: + snd_soc_jack_report(wm8994->micdet[0].jack, + 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_JACK_MICROPHONE | SND_JACK_VIDEOOUT, + report); +} + +/** + * wm8958_mic_detect - Enable microphone detection via the WM8958 IRQ + * + * @codec: WM8958 codec + * @jack: jack to report detection events on + * + * Enable microphone detection functionality for the WM8958. By + * default simple detection which supports the detection of up to 6 + * buttons plus video and microphone functionality is supported. + * + * The WM8958 has an advanced jack detection facility which is able to + * support complex accessory detection, especially when used in + * conjunction with external circuitry. In order to provide maximum + * flexiblity a callback is provided which allows a completely custom + * detection algorithm. + */ +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; + + if (control->type != WM8958) + return -EINVAL; + + if (jack) { + if (!cb) { + dev_dbg(codec->dev, "Using default micdet callback\n"); + cb = wm8958_default_micdet; + cb_data = codec; + } + + 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); + } else { + snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, + WM8958_MICD_ENA, 0); + } + + return 0; +} +EXPORT_SYMBOL_GPL(wm8958_mic_detect); + +static irqreturn_t wm8958_mic_irq(int irq, void *data) +{ + struct wm8994_priv *wm8994 = data; + struct snd_soc_codec *codec = wm8994->codec; + int reg; + + reg = snd_soc_read(codec, WM8958_MIC_DETECT_3); + if (reg < 0) { + dev_err(codec->dev, "Failed to read mic detect status: %d\n", + reg); + return IRQ_NONE; + } + + if (!(reg & WM8958_MICD_VALID)) { + dev_dbg(codec->dev, "Mic detect data not valid\n"); + goto out; + } + + if (wm8994->jack_cb) + wm8994->jack_cb(reg, wm8994->jack_cb_data); + else + dev_warn(codec->dev, "Accessory detection with no callback\n"); + +out: + return IRQ_HANDLED; +} + static int wm8994_codec_probe(struct snd_soc_codec *codec) { struct wm8994 *control; @@ -2732,6 +2866,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) "Failed to request Mic2 short IRQ: %d\n", ret); break; + + case WM8958: + ret = wm8994_request_irq(codec->control_data, + WM8994_IRQ_MIC1_DET, + wm8958_mic_irq, "Mic detect", + wm8994); + if (ret != 0) + dev_warn(codec->dev, + "Failed to request Mic detect IRQ: %d\n", + ret); + break; } /* Remember if AIFnLRCLK is configured as a GPIO. This should be @@ -2866,6 +3011,11 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec) wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, wm8994); break; + + case WM8958: + wm8994_free_irq(codec->control_data, WM8994_IRQ_MIC1_DET, + wm8994); + break; } kfree(wm8994->retune_mobile_texts); kfree(wm8994->drc_texts); |