[PORT FROM R2]audio: sn95031: added GPIO support for jack detection
authorOmair Mohammed Abdullah <omair.m.abdullah@intel.com>
Tue, 27 Dec 2011 06:25:23 +0000 (11:55 +0530)
committerbuildbot <buildbot@intel.com>
Mon, 13 Feb 2012 08:33:02 +0000 (00:33 -0800)
BZ: 14181 20155

PR3.2 devices have a GPIO pin which can be used to detect high impedance
headsets.

Added GPIO support in the codec driver so that whenever an insert interrupt is
received and the micbias value is out of range, it checks the GPIO pin and
reports the jack as a HEADSET if the GPIO pin says that there is a headset.

PR2 devices don't have this GPIO pin and cannot support high impedance headsets.
PR3.1 and PR3.0 devices need to add a pull up/down resistor (enabled in IFWI) so
that the GPIO_AON_77 value doesn't fluctuate.

Change-Id: Iea972f515584354903e045ca3c2e631713439c3f
Old-Change-Id: Id524cc3bda8f7fb9688f7f2df4ff9bfabe99acb0
Signed-off-by: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
Reviewed-on: http://android.intel.com:8080/34870
Reviewed-by: M, Arulselvan <arulselvan.m@intel.com>
Tested-by: M, Arulselvan <arulselvan.m@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
sound/soc/codecs/sn95031.c
sound/soc/mid-x86/mfld_machine.c

index 338da4e..8cf6e37 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/gpio.h>
 #include <asm/intel_scu_ipc.h>
 #include <asm/intel_mid_gpadc.h>
+#include <asm/mrst.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #define SN95031_RATES (SNDRV_PCM_RATE_8000_96000)
 #define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
 #define SN95031_SW_DBNC 250
-
-struct sn95031_work {
-       struct delayed_work work;
-       struct snd_soc_codec *codec;
-};
+#define HEADSET_DET_PIN 77
 
 struct sn95031_jack_work {
        unsigned int intr_id;
@@ -1216,14 +1214,23 @@ static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
 
 static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
 {
-       int micbias, jack_type;
+       int micbias, jack_type, hs_gpio = 1;
 
        sn95031_enable_mic_bias(mfld_jack->codec);
        micbias = sn95031_get_mic_bias(mfld_jack->codec);
 
        jack_type = snd_soc_jack_get_type(mfld_jack, micbias);
-
        pr_debug("jack type detected = %d, micbias = %d\n", jack_type, micbias);
+
+       if (mfld_board_id() == MFLD_BID_PR3) {
+               if ((jack_type != SND_JACK_HEADSET) &&
+                   (jack_type != SND_JACK_HEADPHONE))
+                       hs_gpio = gpio_get_value(HEADSET_DET_PIN);
+               if (!hs_gpio) {
+                       jack_type = SND_JACK_HEADPHONE;
+                       pr_debug("GPIO says there is a headphone, reporting it\n");
+               }
+       }
        if (jack_type == SND_JACK_HEADSET)
                sn95031_enable_jack_btn(mfld_jack->codec);
        else
@@ -1235,19 +1242,16 @@ static void sn95031_jack_report(struct snd_soc_jack *jack, unsigned int status)
 {
        unsigned int mask = SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_HEADSET;
 
-       pr_debug("jack reported of type: %d\n", status);
-       if ((status == SND_JACK_HEADSET) | (status == SND_JACK_HEADPHONE)) {
-               pr_debug("detected headset or headphone, disabling JACKDET\n");
-               snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK,
-                                                               BIT(2), BIT(2));
-
+       pr_debug("jack reported of type: 0x%x\n", status);
+       if ((status == SND_JACK_HEADSET) || (status == SND_JACK_HEADPHONE)) {
                /* if we detected valid headset then disable headset ground.
                 * Otherwise enable it in else condition
                 * this is required for jack detect to work well */
                snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(1), 0);
-       } else
+       } else if (status == 0) {
                snd_soc_update_bits(jack->codec,
                                        SN95031_BTNCTRL2, BIT(1), BIT(1));
+       }
        snd_soc_jack_report(jack, status, mask);
 #ifdef CONFIG_SWITCH_MID
        if (status) {
@@ -1273,41 +1277,61 @@ void sn95031_jack_wq(struct work_struct *work)
        struct snd_soc_jack *jack = jack_wq->jack;
        unsigned int voltage, status = 0;
 
-       pr_debug("jack status in wq:%d\n", jack_wq->intr_id);
+       pr_debug("jack status in wq: 0x%x\n", jack_wq->intr_id);
        if (jack_wq->intr_id & SN95031_JACK_INSERTED) {
                status = sn95031_get_headset_state(jack);
                jack_wq->intr_id &= ~SN95031_JACK_INSERTED;
+               /* unmask button press interrupts */
+               if (status == SND_JACK_HEADSET)
+                       snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK,
+                                                       BIT(1)|BIT(0), 0);
+               cancel_delayed_work(&sn95031_ctx->jack_work.work);
        } else if (jack_wq->intr_id & SN95031_JACK_REMOVED) {
+               if (mfld_board_id() == MFLD_BID_PR3) {
+                       if (!gpio_get_value(HEADSET_DET_PIN)) {
+                               pr_debug("remove interrupt, but GPIO says inserted\n");
+                               return;
+                       }
+               }
                pr_debug("reporting jack as removed\n");
                sn95031_disable_jack_btn(jack->codec);
                snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK, BIT(2), 0);
                sn95031_disable_mic_bias(jack->codec);
-               jack_wq->intr_id &= ~SN95031_JACK_REMOVED;
+               jack_wq->intr_id = 0;
+               cancel_delayed_work(&sn95031_ctx->jack_work.work);
        } else if (jack_wq->intr_id & SN95031_JACK_BTN0) {
+               jack_wq->intr_id &= ~SN95031_JACK_BTN0;
                if (sn95031_lp_flag) {
                        snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
                        sn95031_lp_flag = 0;
+                       /* clear up BTN1 intr_id if it was not cleared */
+                       jack_wq->intr_id &= ~SN95031_JACK_BTN1;
+                       pr_debug("short press on releasing long press, "
+                                  "report button release\n");
                        return;
                } else {
                        status = SND_JACK_HEADSET | SND_JACK_BTN_0;
+                       pr_debug("short press detected\n");
                }
-               jack_wq->intr_id &= ~SN95031_JACK_BTN0;
        } else if (jack_wq->intr_id & SN95031_JACK_BTN1) {
-               /* we get spurious intterupts if jack key is held down
-               * so we ignore them untill key is released by
-               * checking the voltage level */
+               /* we get spurious interrupts if jack key is held down
+               * so we ignore them until key is released by checking the
+               * voltage level */
                if (sn95031_lp_flag) {
                        voltage = sn95031_read_voltage();
                        if (voltage > 400) {
                                snd_soc_jack_report(jack,
-                                                       SND_JACK_HEADSET, mask);
-                               sn95031_lp_flag = 0; /* button released */
+                                               SND_JACK_HEADSET, mask);
+                               sn95031_lp_flag = 0;
+                               jack_wq->intr_id &= ~SN95031_JACK_BTN1;
+                               pr_debug("button released after long press\n");
                        }
                        return;
                }
                status = SND_JACK_HEADSET | SND_JACK_BTN_1;
                sn95031_lp_flag = 1;
                jack_wq->intr_id &= ~SN95031_JACK_BTN1;
+               pr_debug("long press detected\n");
        }
        sn95031_jack_report(jack, status);
 }
@@ -1342,20 +1366,23 @@ void sn95031_jack_detection(struct mfld_jack_data *jack_data)
                } else {
                        sn95031->jack_work.intr_id |= jack_data->intr_id;
                }
+               /* mask button press interrupts until jack is reported*/
+               snd_soc_update_bits(jack_data->mfld_jack->codec,
+                    SN95031_ACCDETMASK, BIT(1)|BIT(0), BIT(1)|BIT(0));
                return;
        }
 
        if (jack_data->intr_id & SN95031_JACK_BTN0 ||
                                jack_data->intr_id & SN95031_JACK_BTN1) {
-               if (jack_data->mfld_jack->status == SND_JACK_HEADSET) {
+               if ((jack_data->mfld_jack->status & SND_JACK_HEADSET) != 0) {
                        retval = sn95031_schedule_jack_wq(jack_data);
                        if (!retval) {
-                               pr_debug("spurious button press detected\n");
+                               pr_debug("spurious btn press, lp_flag:%d\n",
+                                                       sn95031_lp_flag);
                                sn95031->jack_work.intr_id = jack_data->intr_id;
                                return;
-                       } else {
-                               sn95031->jack_work.intr_id |= jack_data->intr_id;
                        }
+                       sn95031->jack_work.intr_id |= jack_data->intr_id;
                        pr_debug("BTN_Press detected\n");
                } else {
                        pr_debug("BTN_press received, but jack is removed\n");
@@ -1428,7 +1455,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
        /* voice related stuff */
        snd_soc_write(codec, SN95031_VOICETXVOL, 0x89);
        /* debounce time and long press duration */
-       snd_soc_write(codec, SN95031_BTNCTRL1, 0xA7);
+       snd_soc_write(codec, SN95031_BTNCTRL1, 0x57);
 
        /* soft mute ramp time */
        snd_soc_write(codec, SN95031_SOFTMUTE, 0x3);
index 7e433e1..2c01058 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/io.h>
 #include <linux/async.h>
 #include <linux/wakelock.h>
+#include <linux/gpio.h>
+#include <asm/mrst.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
@@ -40,6 +42,7 @@
 #define MID_STEREO 2
 #define MID_MAX_CAP 5
 #define MFLD_JACK_INSERT 0x04
+#define HEADSET_DET_PIN 77
 
 enum soc_mic_bias_zones {
        MFLD_MV_START = 0,
@@ -530,6 +533,22 @@ static int __devinit snd_mfld_mc_probe(struct platform_device *pdev)
                ret_val = -ENODEV;
                goto unalloc;
        }
+
+       if (mfld_board_id() == MFLD_BID_PR3) {
+               ret_val = gpio_request(HEADSET_DET_PIN, "headset_detect_pin");
+               if (ret_val) {
+                       pr_err("HEADSET GPIO allocation failed: %d\n", ret_val);
+                       kfree(mc_drv_ctx);
+                       return ret_val;
+               }
+               ret_val = gpio_direction_input(HEADSET_DET_PIN);
+               if (ret_val) {
+                       pr_err("HEADSET GPIO direction wrong: %d\n", ret_val);
+                       kfree(mc_drv_ctx);
+                       return ret_val;
+               }
+       }
+
        mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
                                        resource_size(irq_mem));
        if (!mc_drv_ctx->int_base) {
@@ -578,6 +597,8 @@ static int __devexit snd_mfld_mc_remove(struct platform_device *pdev)
        wake_lock_destroy(&mc_drv_ctx->wake_lock);
 #endif
        kfree(mc_drv_ctx);
+       if (mfld_board_id() == MFLD_BID_PR3)
+               gpio_free(HEADSET_DET_PIN);
        snd_soc_card_set_drvdata(soc_card, NULL);
        snd_soc_unregister_card(soc_card);
        platform_set_drvdata(pdev, NULL);