asoc: max98088: add headset/headphone detection code
Nikesh Oswal [Fri, 30 Sep 2011 11:57:08 +0000 (16:57 +0530)]
Bug: 862023

Change-Id: Ieb9247cef5feecf3e241d8980f9c4376cadf2965
Signed-off-by: Nikesh Oswal <noswal@nvidia.com>
Reviewed-on: http://git-master/r/55465
Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com>
Reviewed-by: Scott Peterson <speterson@nvidia.com>

Rebase-Id: Rf725fad369dbbe4d5f6eaa7b667170a127fa0a08

include/sound/max98088.h
sound/soc/codecs/max98088.c
sound/soc/codecs/max98088.h

index c3ba823..9b4fceb 100644 (file)
@@ -31,6 +31,10 @@ struct max98088_pdata {
        struct max98088_eq_cfg *eq_cfg;
        unsigned int eq_cfgcnt;
 
+       /* has to be one of 25,50,100 or 200 ms
+       as per the data sheet */
+       unsigned int debounce_time_ms;
+
        /* Receiver output can be configured as power amplifier or LINE out */
        /* Set receiver_mode to:
         * 0 = amplifier output, or
index ac65a2d..e7e5bfe 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/slab.h>
 #include <asm/div64.h>
 #include <sound/max98088.h>
+#include <sound/jack.h>
 #include "max98088.h"
 
 enum max98088_type {
@@ -54,6 +55,8 @@ struct max98088_priv {
        unsigned int mic1pre;
        unsigned int mic2pre;
        unsigned int extmic_mode;
+       int irq;
+       struct snd_soc_jack *headset_jack;
 };
 
 static const u8 max98088_reg[M98088_REG_CNT] = {
@@ -1908,6 +1911,7 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        struct max98088_pdata *pdata = max98088->pdata;
        u8 regval = 0;
+       unsigned int debounce_time;
 
        if (!pdata) {
                dev_dbg(codec->dev, "No platform data\n");
@@ -1933,6 +1937,26 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
        /* Configure equalizers */
        if (pdata->eq_cfgcnt)
                max98088_handle_eq_pdata(codec);
+
+       /* Configure the debounce time */
+       if (max98088->irq) {
+               switch (pdata->debounce_time_ms) {
+               case 25:
+                       debounce_time = M98088_JDEB_25;
+                       break;
+               case 50:
+                       debounce_time = M98088_JDEB_50;
+                       break;
+               case 100:
+                       debounce_time = M98088_JDEB_100;
+                       break;
+               case 200:
+               default:
+                       debounce_time = M98088_JDEB_200;
+               }
+               snd_soc_update_bits(codec, M98088_REG_4B_CFG_JACKDET,
+                       M98088_JDEB, debounce_time);
+       }
 }
 
 #ifdef CONFIG_PM
@@ -1954,6 +1978,68 @@ static int max98088_resume(struct snd_soc_codec *codec)
 #define max98088_resume NULL
 #endif
 
+int max98088_report_jack(struct snd_soc_codec *codec)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       unsigned int value;
+       int jack_report = 0;
+
+       /* Read the Jack Status Register*/
+       value = snd_soc_read(codec, M98088_REG_02_JACK_STAUS);
+
+       if ((value & M98088_JKSNS_7) == 0)
+               jack_report |= SND_JACK_HEADPHONE;
+       if (value & M98088_JKSNS_6)
+               jack_report |= SND_JACK_MICROPHONE;
+
+       snd_soc_jack_report(max98088->headset_jack,
+               jack_report, SND_JACK_HEADSET);
+
+       return 0;
+}
+
+static irqreturn_t max98088_jack_handler(int irq, void *data)
+{
+       struct snd_soc_codec *codec = data;
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+
+       /*clear the interrupt by reading the status register */
+       snd_soc_read(codec, M98088_REG_00_IRQ_STATUS);
+       max98088_report_jack(codec);
+
+       return IRQ_HANDLED;
+}
+
+int max98088_headset_detect(struct snd_soc_codec *codec,
+       struct snd_soc_jack *jack, enum snd_jack_types type)
+{
+       struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
+       max98088->headset_jack = jack;
+
+       max98088_report_jack(codec);
+
+       if (max98088->irq) {
+               if (type & SND_JACK_HEADSET) {
+                       /* headphone + microphone detection */
+                       snd_soc_update_bits(codec, M98088_REG_4E_BIAS_CNTL,
+                               M98088_JDWK, 0);
+               } else {
+                       /* headphone detection only*/
+                       snd_soc_update_bits(codec, M98088_REG_4E_BIAS_CNTL,
+                               M98088_JDWK, 1);
+               }
+               /* Enable the Jack Detection Circuitry */
+               snd_soc_update_bits(codec, M98088_REG_4B_CFG_JACKDET,
+                       M98088_JDETEN, M98088_JDETEN);
+               /*Enable the jack detection interrupt*/
+               snd_soc_update_bits(codec, M98088_REG_0F_IRQ_ENABLE,
+                       M98088_IJDET, M98088_IJDET);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(max98088_headset_detect);
+
 static int max98088_probe(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
@@ -1990,6 +2076,18 @@ static int max98088_probe(struct snd_soc_codec *codec)
        max98088->mic1pre = 0;
        max98088->mic2pre = 0;
 
+       if (max98088->irq) {
+               /* register an audio interrupt */
+               ret = request_threaded_irq(max98088->irq, NULL,
+                       max98088_jack_handler,
+                       IRQF_TRIGGER_FALLING,
+                       "max98088", codec);
+               if (ret) {
+                       dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+                       goto err_access;
+               }
+       }
+
        ret = snd_soc_read(codec, M98088_REG_FF_REV_ID);
        if (ret < 0) {
                dev_err(codec->dev, "Failed to read device revision: %d\n",
@@ -2068,6 +2166,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c,
        i2c_set_clientdata(i2c, max98088);
        max98088->control_data = i2c;
        max98088->pdata = i2c->dev.platform_data;
+       max98088->irq = i2c->irq;
 
        ret = snd_soc_register_codec(&i2c->dev,
                        &soc_codec_dev_max98088, &max98088_dai[0], 2);
index be89a4f..cf4b04d 100644 (file)
        #define M98088_PWRSV8K                  (1<<1)
        #define M98088_PWRSV                    (1<<0)
 
+/* M98088_REG_4E_BIAS_CNTL */
+       #define M98088_JDWK                     (1<<1)
+
+/* M98088_REG_4B_CFG_JACKDET */
+       #define M98088_JDETEN                   (1<<7)
+       #define M98088_JDEB                     (3<<0)
+       #define M98088_JDEB_25                  (0<<0)
+       #define M98088_JDEB_50                  (1<<0)
+       #define M98088_JDEB_100                 (2<<0)
+       #define M98088_JDEB_200                 (3<<0)
+
+
+/* M98088_REG_0F_IRQ_ENABLE */
+       #define M98088_IJDET                    (1<<1)
+
+/* M98088_REG_02_JACK_STAUS */
+       #define M98088_JKSNS_7                  (1<<7)
+       #define M98088_JKSNS_6                  (1<<6)
+
 /* Line inputs */
 #define LINE_INA  0
 #define LINE_INB  1
 #define M98088_BYTE1(w) ((w >> 8) & 0xff)
 #define M98088_BYTE0(w) (w & 0xff)
 
+int max98088_headset_detect(struct snd_soc_codec *codec,
+        struct snd_soc_jack *jack, enum snd_jack_types type);
+
 #endif