audio: sn95031: cleanup jack detection code
authorOmair Mohammed Abdullah <omair.m.abdullah@linux.intel.com>
Mon, 2 Apr 2012 08:52:55 +0000 (14:22 +0530)
committerbuildbot <buildbot@intel.com>
Fri, 11 May 2012 10:37:29 +0000 (03:37 -0700)
BZ: 22081

Machine specific stuff like GPIO was present in the codec driver in Medfield.
Moved the code to the machine driver. Jack detection design is changed a little
but the logic remains the same.

The bottom half of the interrupt handler now directly calls
mfld_jack_detection(). During boot, jack detection is done by calling
mfld_schedule_wq() instead of mfld_jack_check(), which is removed.

Additionally, a new file mfld_common.[ch] is added which contains common
routines across all medfield boards.

Change-Id: I552c7bfdc40062f17f5360b88b0ddec4c0bd5d82
Signed-off-by: Omair Mohammed Abdullah <omair.m.abdullah@linux.intel.com>
Reviewed-on: http://android.intel.com:8080/42078
Reviewed-by: Babu, Ramesh <ramesh.babu@intel.com>
Reviewed-by: Gupta, ArvindX K <arvindx.k.gupta@intel.com>
Reviewed-by: Koul, Vinod <vinod.koul@intel.com>
Reviewed-by: Hibare, PramodX <pramodx.hibare@intel.com>
Tested-by: Hibare, PramodX <pramodx.hibare@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
sound/soc/codecs/sn95031.c
sound/soc/codecs/sn95031.h
sound/soc/mid-x86/Makefile
sound/soc/mid-x86/mfld_common.c [new file with mode: 0644]
sound/soc/mid-x86/mfld_common.h [new file with mode: 0644]
sound/soc/mid-x86/mfld_machine.c
sound/soc/mid-x86/mfld_machine_gi.c
sound/soc/mid-x86/sst_platform.c

index 24a3fb1..877c021 100644 (file)
@@ -28,9 +28,9 @@
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/module.h>
 #include <linux/gpio.h>
 #include <asm/intel_scu_ipc.h>
-#include <asm/intel_mid_gpadc.h>
 #include <asm/intel-mid.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 
 #define SN95031_RATES (SNDRV_PCM_RATE_8000_96000)
 #define SN95031_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
-#define LP_THRESHOLD 400
-#define HEADSET_DET_PIN 77
-/*
- * On LEX platform, for JACK mechanism changed, the mic bias voltage
- * maybe unstale after 250ms wq delay, Use longer time delay 700ms as
- * workaround, it covers most normal plug in operation.
- */
-#ifdef CONFIG_SND_MFLD_MACHINE_GI
-#define SN95031_SW_DBNC 700
-#else
-#define SN95031_SW_DBNC 250
-#endif
-
-struct sn95031_jack_work {
-       unsigned int intr_id;
-       struct delayed_work work;
-       struct snd_soc_jack *jack;
-};
 
 /* codec private data */
 struct sn95031_priv {
        uint8_t clk_src;
-       enum sn95031_pll_status  pll_state;
-       struct sn95031_jack_work jack_work;
+       enum sn95031_pll_status pll_state;
 };
 
-void *audio_adc_handle;
-unsigned int sn95031_lp_flag;
-
-/* This Function reads the voltage level from the ADC Driver*/
-static unsigned int sn95031_read_voltage(void)
-{
-       unsigned int mic_bias;
-
-       /* Reads the mic bias value */
-       if (!sn95031_lp_flag)
-               /* GPADC MIC BIAS takes around a 50ms to settle down and
-               * get sampled porperly, reading earlier than this causes to
-               * read incorrect values */
-               msleep(50);
-       intel_mid_gpadc_sample(audio_adc_handle, SN95031_ADC_SAMPLE_COUNT,
-                                                               &mic_bias);
-       mic_bias = (mic_bias * SN95031_ADC_ONE_LSB_MULTIPLIER) / 1000;
-       pr_debug("mic bias = %dmV\n", mic_bias);
-       return mic_bias;
-}
-
-/* enables mic bias voltage */
-static void sn95031_enable_mic_bias(struct snd_soc_codec *codec)
-{
-       pr_debug("enable mic bias\n");
-       mutex_lock(&codec->mutex);
-       /* GI board has amic bias swapped, we need to enable
-               Mic2 bias for jack */
-       /* TODO Remove this once issue fixed in HW */
-#ifdef CONFIG_SND_MFLD_MACHINE_GI
-               snd_soc_dapm_force_enable_pin(&codec->dapm, "AMIC2Bias");
-#else
-               snd_soc_dapm_force_enable_pin(&codec->dapm, "AMIC1Bias");
-#endif
-       snd_soc_dapm_sync(&codec->dapm);
-       mutex_unlock(&codec->mutex);
-}
-
-/* disables mic bias voltage */
-static void sn95031_disable_mic_bias(struct snd_soc_codec *codec)
-{
-       pr_debug("disable mic bias\n");
-       mutex_lock(&codec->mutex);
-       /* TODO Remove this once issue fixed in HW */
-#ifdef CONFIG_SND_MFLD_MACHINE_GI
-               snd_soc_dapm_disable_pin(&codec->dapm, "AMIC2Bias");
-#else
-               snd_soc_dapm_disable_pin(&codec->dapm, "AMIC1Bias");
-#endif
-       snd_soc_dapm_sync(&codec->dapm);
-       mutex_unlock(&codec->mutex);
-}
-/* end - adc helper functions */
-
 static inline unsigned int sn95031_read(struct snd_soc_codec *codec,
                        unsigned int reg)
 {
@@ -153,7 +80,7 @@ void sn95031_configure_pll(struct snd_soc_codec *codec, int operation)
        sn95031_ctx = snd_soc_codec_get_drvdata(codec);
 
        if (sn95031_ctx->pll_state == PLL_ENABLE_PENDING
-                       && operation == ENABLE_PLL) {
+                       && operation == SN95031_ENABLE_PLL) {
                pr_debug("setting PLL to 0x%x\n", sn95031_ctx->clk_src);
                snd_soc_write(codec, SN95031_AUDPLLCTRL, 0x20);
                udelay(1000);
@@ -167,7 +94,7 @@ void sn95031_configure_pll(struct snd_soc_codec *codec, int operation)
                snd_soc_update_bits(codec, SN95031_AUDPLLCTRL, BIT(5), BIT(5));
                udelay(1000);
                sn95031_ctx->pll_state = PLL_ENABLED;
-       } else if (operation == DISABLE_PLL) {
+       } else if (operation == SN95031_DISABLE_PLL) {
                pr_debug("disabling PLL\n");
                sn95031_ctx->clk_src = SN95031_INVALID;
                sn95031_ctx->pll_state = PLL_DISABLED;
@@ -207,7 +134,7 @@ static int sn95031_set_vaud_bias(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_OFF:
                pr_debug("vaud_bias OFF, doing rail shutdown\n");
-               sn95031_configure_pll(codec, DISABLE_PLL);
+               sn95031_configure_pll(codec, SN95031_DISABLE_PLL);
                /*
                 * off mode is 100, and we need AOAC as off as well,
                 * so 100100b ie 24
@@ -228,7 +155,7 @@ static int sn95031_vhs_event(struct snd_soc_dapm_widget *w,
                /* power up the rail- 1.8v, powersave mode */
                snd_soc_write(w->codec, SN95031_VHSP, 0xED);
                snd_soc_write(w->codec, SN95031_VHSN, 0x2D);
-               msleep(1);
+               usleep_range(1000, 1100);
        } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
                pr_debug("VHS SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
                /* First disable VHSN and then followed by VHSP rail.
@@ -248,7 +175,7 @@ static int sn95031_vihf_event(struct snd_soc_dapm_widget *w,
                pr_debug("VIHF SND_SOC_DAPM_EVENT_ON doing rail startup now\n");
                /* power up the rail */
                snd_soc_write(w->codec, SN95031_VIHF, 0x2D);
-               msleep(1);
+               usleep_range(1000, 1100);
        } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
                pr_debug("VIHF SND_SOC_DAPM_EVENT_OFF doing rail shutdown\n");
                snd_soc_write(w->codec, SN95031_VIHF, 0x24);
@@ -754,7 +681,8 @@ static const struct snd_soc_dapm_route sn95031_audio_map[] = {
 
        /* AMIC2 */
        { "Mic_InputR Capture Route", "AMIC", "MIC2 Enable"},
-#ifdef CONFIG_SND_MFLD_MACHINE_GI
+#if (defined(CONFIG_SND_MFLD_MACHINE_GI) \
+               || defined(CONFIG_SND_MFLD_MACHINE_GI_MODULE))
        { "MIC1 Enable", NULL, "AMIC2Bias"},
        { "MIC2 Enable", NULL, "AMIC1Bias"},
        { "AMIC1Bias", NULL, "AMIC2"},
@@ -871,7 +799,7 @@ static int sn95031_codec_set_pll(struct snd_soc_codec *codec, int pll_id,
        if (!freq_in || !freq_out) {
                /* disable PLL  */
                pr_debug("request to disable pll\n");
-               sn95031_configure_pll(codec, DISABLE_PLL);
+               sn95031_configure_pll(codec, SN95031_DISABLE_PLL);
                retval = 0;
                goto out;
        }
@@ -1074,19 +1002,19 @@ static int sn95031_voice_hw_free(struct snd_pcm_substream *substream,
 }
 
 /* Codec DAI section */
-static struct snd_soc_dai_ops sn95031_headset_dai_ops = {
+static const struct snd_soc_dai_ops sn95031_headset_dai_ops = {
        .digital_mute   = sn95031_pcm_hs_mute,
        .hw_params      = sn95031_pcm_hw_params,
        .set_tristate   = sn95031_set_pcm2_tristate,
 };
 
-static struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
+static const struct snd_soc_dai_ops sn95031_speaker_dai_ops = {
        .digital_mute   = sn95031_pcm_spkr_mute,
        .hw_params      = sn95031_pcm_hw_params,
        .set_tristate   = sn95031_set_pcm2_tristate,
 };
 
-static struct snd_soc_dai_ops sn95031_voice_dai_ops = {
+static const struct snd_soc_dai_ops sn95031_voice_dai_ops = {
        .digital_mute   = sn95031_pcm_hs_mute,
        .hw_params      = sn95031_voice_hw_params,
        .set_fmt        = sn95031_set_voice_dai_fmt,
@@ -1163,193 +1091,6 @@ static struct snd_soc_dai_driver sn95031_dais[] = {
 },
 };
 
-static inline void sn95031_disable_jack_btn(struct snd_soc_codec *codec)
-{
-       snd_soc_update_bits(codec, SN95031_BTNCTRL2, BIT(0), 0);
-}
-
-static inline void sn95031_enable_jack_btn(struct snd_soc_codec *codec)
-{
-       snd_soc_update_bits(codec, SN95031_BTNCTRL2, BIT(0), BIT(0));
-}
-
-static int sn95031_get_headset_state(struct snd_soc_jack *mfld_jack)
-{
-       int micbias, jack_type, hs_gpio = 1;
-
-       sn95031_enable_mic_bias(mfld_jack->codec);
-       micbias = sn95031_read_voltage();
-
-       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
-               sn95031_disable_mic_bias(mfld_jack->codec);
-
-       return jack_type;
-}
-static void sn95031_jack_report(struct snd_soc_jack *jack, unsigned int status)
-{
-       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_HEADSET;
-
-       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 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
-       /* report to the switch driver as well */
-       if (status) {
-               if (status == SND_JACK_HEADPHONE)
-                       mid_headset_report((1<<1));
-               else if (status == SND_JACK_HEADSET)
-                       mid_headset_report(1);
-       } else {
-               mid_headset_report(0);
-       }
-#endif
-}
-
-void sn95031_jack_wq(struct work_struct *work)
-{
-       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_HEADSET;
-       struct sn95031_priv *sn95031_ctx =
-               container_of(work, struct sn95031_priv, jack_work.work.work);
-       struct sn95031_jack_work *jack_wq = &sn95031_ctx->jack_work;
-       struct snd_soc_jack *jack = jack_wq->jack;
-       unsigned int voltage, status = 0, intr_id = jack_wq->intr_id;
-
-       pr_debug("jack status in wq: 0x%x\n", intr_id);
-       if (intr_id & SN95031_JACK_INSERTED) {
-               status = sn95031_get_headset_state(jack);
-               /* unmask button press interrupts */
-               if (status == SND_JACK_HEADSET)
-                       snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK,
-                                                       BIT(1)|BIT(0), 0);
-       } else if (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 = 0;
-               cancel_delayed_work(&sn95031_ctx->jack_work.work);
-       } else if (intr_id & SN95031_JACK_BTN0) {
-               if (sn95031_lp_flag) {
-                       snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
-                       sn95031_lp_flag = 0;
-                       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");
-                       snd_soc_jack_report(jack, status, mask);
-                       /* send explicit button release */
-                       if (status & SND_JACK_BTN_0)
-                               snd_soc_jack_report(jack,
-                                               SND_JACK_HEADSET, mask);
-                       return;
-               }
-       } else if (intr_id & SN95031_JACK_BTN1) {
-               /* 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 > LP_THRESHOLD) {
-                               snd_soc_jack_report(jack,
-                                               SND_JACK_HEADSET, mask);
-                               sn95031_lp_flag = 0;
-                               pr_debug("button released after long press\n");
-                       }
-                       return;
-               }
-               /* Codec sends separate long press event after button pressed
-                * for a specified time. Need to send separate button pressed
-                * and released events for Android */
-               status = SND_JACK_HEADSET | SND_JACK_BTN_0;
-               sn95031_lp_flag = 1;
-               pr_debug("long press detected\n");
-       } else {
-               pr_err("Invalid intr_id:0x%x\n", intr_id);
-               return;
-       }
-       sn95031_jack_report(jack, status);
-}
-
-static int sn95031_schedule_jack_wq(struct mfld_jack_data *jack_data)
-{
-       int retval = 0;
-       struct sn95031_priv *sn95031 = snd_soc_codec_get_drvdata(
-                       jack_data->mfld_jack->codec);
-
-       sn95031->jack_work.jack = jack_data->mfld_jack;
-       retval = schedule_delayed_work(&sn95031->jack_work.work,
-                       msecs_to_jiffies(SN95031_SW_DBNC));
-       return retval;
-}
-
-void sn95031_jack_detection(struct mfld_jack_data *jack_data)
-{
-       int retval = 0;
-       struct sn95031_priv *sn95031 = snd_soc_codec_get_drvdata(
-                       jack_data->mfld_jack->codec);
-
-       pr_debug("interrupt id read in sram = 0x%x\n", jack_data->intr_id);
-
-       if (jack_data->intr_id & SN95031_JACK_INSERTED ||
-                               jack_data->intr_id & SN95031_JACK_REMOVED) {
-               retval = sn95031_schedule_jack_wq(jack_data);
-               if (!retval)
-                       pr_debug("jack inserted/removed,intr already queued\n");
-               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) != 0) {
-                       sn95031->jack_work.intr_id = jack_data->intr_id;
-                       retval = sn95031_schedule_jack_wq(jack_data);
-                       if (!retval) {
-                               pr_debug("spurious btn press, lp_flag:%d\n",
-                                                       sn95031_lp_flag);
-                               return;
-                       }
-                       pr_debug("BTN_Press detected\n");
-               } else {
-                       pr_debug("BTN_press received, but jack is removed\n");
-               }
-       }
-}
-EXPORT_SYMBOL_GPL(sn95031_jack_detection);
-
 /* codec registration */
 static int sn95031_codec_probe(struct snd_soc_codec *codec)
 {
@@ -1357,7 +1098,6 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
 
        pr_debug("codec_probe called\n");
 
-       codec->dapm.bias_level = SND_SOC_BIAS_OFF;
        codec->dapm.idle_bias_off = 1;
 
        sn95031_ctx = kzalloc(sizeof(struct sn95031_priv), GFP_ATOMIC);
@@ -1368,7 +1108,6 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
        sn95031_ctx->clk_src = SN95031_INVALID;
        sn95031_ctx->pll_state = PLL_DISABLED;
 
-       INIT_DELAYED_WORK(&sn95031_ctx->jack_work.work, sn95031_jack_wq);
 
        /* PCM1 slot configurations*/
        snd_soc_write(codec, SN95031_NOISEMUX, 0x0);
@@ -1448,17 +1187,7 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
        snd_soc_add_controls(codec, sn95031_snd_controls,
                             ARRAY_SIZE(sn95031_snd_controls));
 
-       /*GPADC handle for audio_detection*/
-       audio_adc_handle = intel_mid_gpadc_alloc(SN95031_AUDIO_SENSOR,
-                               SN95031_AUDIO_DETECT_CODE);
-       if (!audio_adc_handle) {
-               pr_err("invalid ADC handle\n");
-               kfree(sn95031_ctx);
-               return -ENOMEM;
-       }
-
        snd_soc_codec_set_drvdata(codec, sn95031_ctx);
-
        return 0;
 }
 
@@ -1470,13 +1199,10 @@ static int sn95031_codec_remove(struct snd_soc_codec *codec)
        pr_debug("codec_remove called\n");
        sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
 
-       /*Free the adc handle*/
-       intel_mid_gpadc_free(audio_adc_handle);
-       cancel_delayed_work(&sn95031_ctx->jack_work.work);
        kfree(sn95031_ctx);
-
        return 0;
 }
+
 struct snd_soc_codec_driver sn95031_codec = {
        .probe          = sn95031_codec_probe,
        .remove         = sn95031_codec_remove,
index 8d3481e..c6d3c83 100644 (file)
 #define SN95031_ADC_LOOP_MAX (SN95031_ADC_CHANLS_MAX - 1)
 #define SN95031_ADC_NO_LOOP 0x07
 #define SN95031_AUDIO_GPIO_CTRL 0x070
-/* ADC channel code values */
-#define SN95031_AUDIO_DETECT_CODE 0x06
-/*Count of AUD_DETECT ADC Registers*/
-#define SN95031_AUDIO_SENSOR 1
-#define SN95031_ADC_SAMPLE_COUNT 1
-/* multipier to convert to mV */
-#define SN95031_ADC_ONE_LSB_MULTIPLIER 2346
 
 /*BCU related values*/
 #define SN95031_BCU_VOLUME_RECOVERY_3DB 0x3
 #define SN95031_PCM1SYNC 0x04
 #define SN95031_INVALID 0x8
 
-#define DISABLE_PLL 0
-#define ENABLE_PLL 1
+#define SN95031_DISABLE_PLL 0
+#define SN95031_ENABLE_PLL 1
 
 enum sn95031_pll_status {
        PLL_DISABLED,
@@ -153,17 +146,8 @@ enum sn95031_pll_status {
        PLL_ENABLED
 };
 
-struct mfld_jack_data {
-       int intr_id;
-       int micbias_vol;
-       struct snd_soc_jack *mfld_jack;
-};
-
-extern void sn95031_jack_detection(struct mfld_jack_data *jack_data);
-extern void sn95031_oc_handler(struct snd_soc_codec *codec,
-                                       int oc_interrupt_value);
-
 void sn95031_configure_pll(struct snd_soc_codec *codec, int operation);
+
 #ifdef CONFIG_SWITCH_MID
 extern void mid_headset_report(int state);
 #endif
index 313b148..a6425e0 100644 (file)
@@ -2,11 +2,11 @@ snd-soc-sst-platform-objs := sst_platform.o
 obj-$(CONFIG_SND_SST_PLATFORM) += snd-soc-sst-platform.o
 
 # Medfield board
-snd-soc-mfld-machine-objs := mfld_machine.o
+snd-soc-mfld-machine-objs := mfld_common.o mfld_machine.o
 obj-$(CONFIG_SND_MFLD_MACHINE) += snd-soc-mfld-machine.o
 
 # Gilligan Island/Lexington board
-snd-soc-mfld-machine-gi-objs := mfld_machine_gi.o
+snd-soc-mfld-machine-gi-objs := mfld_common.o mfld_machine_gi.o
 obj-$(CONFIG_SND_MFLD_MACHINE_GI) += snd-soc-mfld-machine-gi.o
 
 # Cloverview/Clovertrail+ board
diff --git a/sound/soc/mid-x86/mfld_common.c b/sound/soc/mid-x86/mfld_common.c
new file mode 100644 (file)
index 0000000..fae765f
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ *  mfld_common.c - Common routines for the Medfield platform
+ *  based on Intel Medfield MID platform
+ *
+ *  Copyright (C) 2012 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/delay.h>
+#include <asm/intel_mid_gpadc.h>
+#include <asm/intel_scu_ipcutil.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include "../codecs/sn95031.h"
+#include "mfld_common.h"
+
+/* reads the voltage level from the ADC Driver*/
+unsigned int mfld_jack_read_voltage(struct snd_soc_jack *jack)
+{
+       unsigned int mic_bias;
+       struct mfld_mc_private *ctx =
+                       snd_soc_card_get_drvdata(jack->codec->card);
+
+       /* Reads the mic bias value */
+       if (!ctx->mfld_jack_lp_flag)
+               /* GPADC MIC BIAS takes around a 50ms to settle down and
+               * get sampled porperly, reading earlier than this causes to
+               * read incorrect values */
+               msleep(50);
+       intel_mid_gpadc_sample(ctx->audio_adc_handle,
+                       MFLD_ADC_SAMPLE_COUNT, &mic_bias);
+       mic_bias = (mic_bias * MFLD_ADC_ONE_LSB_MULTIPLIER) / 1000;
+       pr_debug("mic bias = %dmV\n", mic_bias);
+       return mic_bias;
+}
+
+int mfld_vibra_enable_clk(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *k, int event)
+{
+       int clk_id = 0;
+
+       if (!strcmp(w->name, "Vibra1Clock"))
+               clk_id = CLK0_VIBRA1;
+       else if (!strcmp(w->name, "Vibra2Clock"))
+               clk_id = CLK0_VIBRA2;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               intel_scu_ipc_set_osc_clk0(true, clk_id);
+       else if (SND_SOC_DAPM_EVENT_OFF(event))
+               intel_scu_ipc_set_osc_clk0(false, clk_id);
+       return 0;
+}
+
+/* Callback to set volume for *VOLCTRL regs. Needs to be implemented separately
+ * since clock and VAUDA need to be on before value can be written to the regs
+ */
+int mfld_set_vol_2r(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       int err;
+       unsigned int val, val2, val_mask;
+       int sst_pll_mode_saved;
+
+       val_mask = mask << shift;
+       val = (ucontrol->value.integer.value[0] & mask);
+       val2 = (ucontrol->value.integer.value[1] & mask);
+
+       if (invert) {
+               val = max - val;
+               val2 = max - val2;
+       }
+
+       val = val << shift;
+       val2 = val2 << shift;
+
+       pr_debug("enabling PLL and VAUDA to change volume\n");
+       mutex_lock(&codec->mutex);
+       sst_pll_mode_saved = intel_scu_ipc_set_osc_clk0(true, CLK0_QUERY);
+       intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
+       snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
+       snd_soc_dapm_sync(&codec->dapm);
+
+       err = snd_soc_update_bits(codec, reg, val_mask, val);
+       if (err < 0)
+               goto restore_state;
+
+       err = snd_soc_update_bits(codec, reg2, val_mask, val2);
+restore_state:
+       snd_soc_dapm_disable_pin(&codec->dapm, "VirtBias");
+       if (!(sst_pll_mode_saved & CLK0_MSIC))
+               intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
+       mutex_unlock(&codec->mutex);
+       return err;
+}
+
+int mfld_get_pcm1_mode(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+
+       pr_debug("PCM1 master mode: %d\n", ctx->sn95031_pcm1_mode);
+       ucontrol->value.integer.value[0] = ctx->sn95031_pcm1_mode;
+       return 0;
+}
+
+int mfld_set_pcm1_mode(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+
+       ctx->sn95031_pcm1_mode = ucontrol->value.integer.value[0];
+       return 0;
+}
+
+int mfld_headset_get_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+       ucontrol->value.integer.value[0] = ctx->hs_switch;
+       return 0;
+}
+
+int mfld_headset_set_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+
+       if (ucontrol->value.integer.value[0] == ctx->hs_switch)
+               return 0;
+
+       if (ucontrol->value.integer.value[0]) {
+               pr_debug("hs_set HS path\n");
+               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+       } else {
+               pr_debug("hs_set EP path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+       }
+       mutex_lock(&codec->mutex);
+       snd_soc_dapm_sync(&codec->dapm);
+       mutex_unlock(&codec->mutex);
+       ctx->hs_switch = ucontrol->value.integer.value[0];
+
+       return 0;
+}
+
+static void mfld_lo_enable_out_pins(struct snd_soc_codec *codec)
+{
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+
+       snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
+       snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
+       snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
+       snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
+       snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
+       snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
+       if (ctx->hs_switch) {
+               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+       } else {
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
+       }
+       mutex_lock(&codec->mutex);
+       snd_soc_dapm_sync(&codec->dapm);
+       mutex_unlock(&codec->mutex);
+}
+
+int mfld_lo_get_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+       ucontrol->value.integer.value[0] = ctx->sn95031_lo_dac;
+       return 0;
+}
+
+int mfld_lo_set_switch(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(codec->card);
+
+       if (ucontrol->value.integer.value[0] == ctx->sn95031_lo_dac)
+               return 0;
+
+       /* we dont want to work with last state of lineout so just enable all
+        * pins and then disable pins not required
+        */
+       mfld_lo_enable_out_pins(codec);
+       switch (ucontrol->value.integer.value[0]) {
+       case 0:
+               pr_debug("set vibra path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
+               snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
+               break;
+
+       case 1:
+               pr_debug("set hs  path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
+               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
+               break;
+
+       case 2:
+               pr_debug("set spkr path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
+               snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
+               break;
+
+       case 3:
+               pr_debug("set null path\n");
+               snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
+               snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
+               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
+               break;
+       }
+       mutex_lock(&codec->mutex);
+       snd_soc_dapm_sync(&codec->dapm);
+       mutex_unlock(&codec->mutex);
+       ctx->sn95031_lo_dac = ucontrol->value.integer.value[0];
+       return 0;
+}
+
diff --git a/sound/soc/mid-x86/mfld_common.h b/sound/soc/mid-x86/mfld_common.h
new file mode 100644 (file)
index 0000000..f106cde
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *  mfld_common.h - Common routines for the Medfield platform
+ *  based on Intel Medfield MID platform
+ *
+ *  Copyright (C) 2012 Intel Corp
+ *  Author: Vinod Koul <vinod.koul@intel.com>
+ *  Author: Harsha Priya <priya.harsha@intel.com>
+ *  Author: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#ifndef _MFLD_COMMON_H
+#define _MFLD_COMMON_H
+
+/* ADC channel code values */
+#define MFLD_AUDIO_DETECT_CODE 0x06
+/*Count of AUD_DETECT ADC Registers*/
+#define MFLD_AUDIO_SENSOR 1
+#define MFLD_ADC_SAMPLE_COUNT 1
+/* multipier to convert to mV */
+#define MFLD_ADC_ONE_LSB_MULTIPLIER 2346
+
+#define MFLD_JACK_INSERT_ID      0x04
+#define MFLD_LP_THRESHOLD_VOLTAGE 400 /* mV */
+
+enum soc_mic_bias_zones {
+       MFLD_MV_START = 0,
+       /* mic bias volutage range for Headphones*/
+       MFLD_MV_HP = 400,
+       /* mic bias volutage range for American Headset*/
+       MFLD_MV_AM_HS = 650,
+       /* mic bias volutage range for Headset*/
+       MFLD_MV_HS = 2000,
+       MFLD_MV_UNDEFINED,
+};
+
+struct mfld_jack_work {
+       unsigned int intr_id;
+       struct delayed_work work;
+       struct snd_soc_jack *jack;
+};
+
+struct mfld_mc_private {
+       struct ipc_device *socdev;
+       void __iomem *int_base;
+       struct snd_soc_jack mfld_jack;
+       u8 jack_interrupt_status;
+       u8 oc_interrupt_status;
+       spinlock_t lock; /* lock for interrupt status and jack debounce */
+       struct mfld_jack_work jack_work;
+       void *audio_adc_handle;
+       int sn95031_pcm1_mode;
+       unsigned int mfld_jack_lp_flag;
+       unsigned int hs_switch;
+       unsigned int sn95031_lo_dac;
+#ifdef CONFIG_HAS_WAKELOCK
+       struct wake_lock *jack_wake_lock;
+#endif
+};
+
+
+/* sound card controls */
+static const char * const headset_switch_text[] = {"Earpiece", "Headset"};
+
+static const char * const lo_text[] = {"Vibra", "Headset", "IHF", "None"};
+
+static const struct soc_enum mfld_headset_enum =
+       SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
+
+static const struct soc_enum sn95031_lo_enum =
+       SOC_ENUM_SINGLE_EXT(4, lo_text);
+
+static const char * const sn95031_pcm1_mode_text[] = {"Slave", "Master"};
+
+static const struct soc_enum sn95031_pcm1_mode_config_enum =
+       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sn95031_pcm1_mode_text),
+                                       sn95031_pcm1_mode_text);
+
+
+unsigned int mfld_jack_read_voltage(struct snd_soc_jack *jack);
+int mfld_vibra_enable_clk(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *k, int event);
+int mfld_set_vol_2r(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol);
+int mfld_set_pcm1_mode(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol);
+int mfld_get_pcm1_mode(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol);
+int mfld_headset_get_switch(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol);
+int mfld_headset_set_switch(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol);
+int mfld_lo_get_switch(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol);
+int mfld_lo_set_switch(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol);
+
+#endif
index e974e86..79eb59d 100644 (file)
 #include <linux/ipc_device.h>
 #include <asm/intel-mid.h>
 #include <asm/intel_scu_ipcutil.h>
+#include <asm/intel_mid_gpadc.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <sound/tlv.h>
 #include "../codecs/sn95031.h"
+#include "mfld_common.h"
 
-#define MFLD_JACK_INSERT 0x04
-#define HEADSET_DET_PIN 77
-
-enum soc_mic_bias_zones {
-       MFLD_MV_START = 0,
-       /* mic bias volutage range for Headphones*/
-       MFLD_MV_HP = 400,
-       /* mic bias volutage range for American Headset*/
-       MFLD_MV_AM_HS = 650,
-       /* mic bias volutage range for Headset*/
-       MFLD_MV_HS = 2000,
-       MFLD_MV_UNDEFINED,
-};
-
-struct mfld_mc_private {
-       void __iomem *int_base;
-       u8 jack_interrupt_status;
-       u8 oc_interrupt_status;
-       spinlock_t lock; /* lock for interrupt status and jack debounce */
-       int pcm1_master_mode;
-       unsigned int hs_switch;
-       unsigned int lo_dac;
-#ifdef CONFIG_HAS_WAKELOCK
-       struct wake_lock wake_lock;
-#endif
-};
-
-static struct snd_soc_jack mfld_jack;
+#define MFLD_JACK_DEBOUNCE_TIME          250 /* mS */
+#define MFLD_GPIO_HEADSET_DET_PIN 77
 
 /* jack detection voltage zones */
 static struct snd_soc_jack_zone mfld_zones[] = {
@@ -77,247 +53,219 @@ static struct snd_soc_jack_zone mfld_zones[] = {
        {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
 };
 
-/* sound card controls */
-static const char *headset_switch_text[] = {"Earpiece", "Headset"};
-
-static const char *lo_text[] = {"Vibra", "Headset", "IHF", "None"};
-
-static const struct soc_enum headset_enum =
-       SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
-
-static const struct soc_enum lo_enum =
-       SOC_ENUM_SINGLE_EXT(4, lo_text);
-
-static const char *sn95031_pcm1_mode_text[] = {"Slave", "Master"};
-
-static const struct soc_enum SN95031_pcm1_mode_config_enum =
-       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sn95031_pcm1_mode_text),
-                                                       sn95031_pcm1_mode_text);
-
-static int headset_get_switch(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-       ucontrol->value.integer.value[0] = mc_drv_ctx->hs_switch;
-       return 0;
-}
-
-static int headset_set_switch(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static void mfld_jack_enable_mic_bias(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-
-       if (ucontrol->value.integer.value[0] == mc_drv_ctx->hs_switch)
-               return 0;
-
-       if (ucontrol->value.integer.value[0]) {
-               pr_debug("hs_set HS path\n");
-               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
-       } else {
-               pr_debug("hs_set EP path\n");
-               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
-       }
+       pr_debug("enable mic bias\n");
        mutex_lock(&codec->mutex);
+       snd_soc_dapm_force_enable_pin(&codec->dapm, "AMIC1Bias");
        snd_soc_dapm_sync(&codec->dapm);
        mutex_unlock(&codec->mutex);
-       mc_drv_ctx->hs_switch = ucontrol->value.integer.value[0];
-
-       return 0;
 }
 
-static void lo_enable_out_pins(struct snd_soc_codec *codec)
+static void mfld_jack_disable_mic_bias(struct snd_soc_codec *codec)
 {
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-       snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTL");
-       snd_soc_dapm_enable_pin(&codec->dapm, "IHFOUTR");
-       snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTL");
-       snd_soc_dapm_enable_pin(&codec->dapm, "LINEOUTR");
-       snd_soc_dapm_enable_pin(&codec->dapm, "VIB1OUT");
-       snd_soc_dapm_enable_pin(&codec->dapm, "VIB2OUT");
-       if (mc_drv_ctx->hs_switch) {
-               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
-       } else {
-               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
-       }
+       pr_debug("disable mic bias\n");
+       mutex_lock(&codec->mutex);
+       snd_soc_dapm_disable_pin(&codec->dapm, "AMIC1Bias");
+       snd_soc_dapm_sync(&codec->dapm);
+       mutex_unlock(&codec->mutex);
 }
 
-static int lo_get_switch(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static int mfld_get_headset_state(struct snd_soc_jack *jack)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-       ucontrol->value.integer.value[0] = mc_drv_ctx->lo_dac;
-       return 0;
-}
+       int micbias, jack_type, hs_gpio = 1;
 
-static int lo_set_switch(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
+       mfld_jack_enable_mic_bias(jack->codec);
+       micbias = mfld_jack_read_voltage(jack);
 
-       if (ucontrol->value.integer.value[0] == mc_drv_ctx->lo_dac)
-               return 0;
+       jack_type = snd_soc_jack_get_type(jack, micbias);
+       pr_debug("jack type detected = %d, micbias = %d\n", jack_type, micbias);
 
-       /* we dont want to work with last state of lineout so just enable all
-        * pins and then disable pins not required
-        */
-       lo_enable_out_pins(codec);
-       switch (ucontrol->value.integer.value[0]) {
-       case 0:
-               pr_debug("set vibra path\n");
-               snd_soc_dapm_disable_pin(&codec->dapm, "VIB1OUT");
-               snd_soc_dapm_disable_pin(&codec->dapm, "VIB2OUT");
-               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0);
-               break;
-
-       case 1:
-               pr_debug("set hs  path\n");
-               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
-               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x22);
-               break;
-
-       case 2:
-               pr_debug("set spkr path\n");
-               snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTL");
-               snd_soc_dapm_disable_pin(&codec->dapm, "IHFOUTR");
-               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x44);
-               break;
-
-       case 3:
-               pr_debug("set null path\n");
-               snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTL");
-               snd_soc_dapm_disable_pin(&codec->dapm, "LINEOUTR");
-               snd_soc_update_bits(codec, SN95031_LOCTL, 0x66, 0x66);
-               break;
+       if (mfld_board_id() == MFLD_BID_PR3) {
+               if ((jack_type != SND_JACK_HEADSET) &&
+                   (jack_type != SND_JACK_HEADPHONE))
+                       hs_gpio = gpio_get_value(MFLD_GPIO_HEADSET_DET_PIN);
+               if (!hs_gpio) {
+                       jack_type = SND_JACK_HEADPHONE;
+                       pr_debug("GPIO says there is a headphone, reporting it\n");
+               }
        }
-       mutex_lock(&codec->mutex);
-       snd_soc_dapm_sync(&codec->dapm);
-       mutex_unlock(&codec->mutex);
-       mc_drv_ctx->lo_dac = ucontrol->value.integer.value[0];
-       return 0;
-}
 
-static int sn95031_get_pcm1_mode(struct snd_kcontrol *kcontrol,
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
+       if (jack_type == SND_JACK_HEADSET)
+               /* enable btn press detection */
+               snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(0), 1);
+       else
+               mfld_jack_disable_mic_bias(jack->codec);
 
-       pr_debug("PCM1 master mode: %d\n", mc_drv_ctx->pcm1_master_mode);
-       ucontrol->value.integer.value[0] = mc_drv_ctx->pcm1_master_mode;
-       return 0;
+       return jack_type;
 }
 
-static int sn95031_set_pcm1_mode(struct snd_kcontrol *kcontrol,
-                               struct snd_ctl_elem_value *ucontrol)
+static void mfld_jack_report(struct snd_soc_jack *jack, unsigned int status)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-
-       mc_drv_ctx->pcm1_master_mode = ucontrol->value.integer.value[0];
-       return 0;
+       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_HEADSET;
+
+       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.
+                * this is required for jack detection to work well */
+               snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(1), 0);
+       } 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
+       /* report to the switch driver as well */
+       if (status) {
+               if (status == SND_JACK_HEADPHONE)
+                       mid_headset_report((1<<1));
+               else if (status == SND_JACK_HEADSET)
+                       mid_headset_report(1);
+       } else {
+               mid_headset_report(0);
+       }
+#endif
 }
 
-static int mfld_vibra_enable_clk(struct snd_soc_dapm_widget *w,
-                       struct snd_kcontrol *k, int event)
+void mfld_jack_wq(struct work_struct *work)
 {
-       int clk_id = 0;
-
-       if (!strcmp(w->name, "Vibra1Clock"))
-               clk_id = CLK0_VIBRA1;
-       else if (!strcmp(w->name, "Vibra2Clock"))
-               clk_id = CLK0_VIBRA2;
+       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_HEADSET;
+       struct mfld_mc_private *ctx =
+               container_of(work, struct mfld_mc_private, jack_work.work.work);
+       struct mfld_jack_work *jack_work = &ctx->jack_work;
+       struct snd_soc_jack *jack = jack_work->jack;
+       unsigned int voltage, status = 0, intr_id = jack_work->intr_id;
+
+       pr_debug("jack status in wq: 0x%x\n", intr_id);
+       if (intr_id & SN95031_JACK_INSERTED) {
+               status = mfld_get_headset_state(jack);
+               /* unmask button press interrupts */
+               if (status == SND_JACK_HEADSET)
+                       snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK,
+                                                       BIT(1)|BIT(0), 0);
+       } else if (intr_id & SN95031_JACK_REMOVED) {
+               if (mfld_board_id() == MFLD_BID_PR3) {
+                       if (!gpio_get_value(MFLD_GPIO_HEADSET_DET_PIN)) {
+                               pr_debug("remove interrupt, "
+                                       "but GPIO says inserted\n");
+                               return;
+                       }
+               }
+               pr_debug("reporting jack as removed\n");
+               snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(0), 0);
+               snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK, BIT(2), 0);
+               mfld_jack_disable_mic_bias(jack->codec);
+               jack_work->intr_id = 0;
+               cancel_delayed_work(&ctx->jack_work.work);
+       } else if (intr_id & SN95031_JACK_BTN0) {
+               if (ctx->mfld_jack_lp_flag) {
+                       snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
+                       ctx->mfld_jack_lp_flag = 0;
+                       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");
+                       snd_soc_jack_report(jack, status, mask);
+                       /* send explicit button release */
+                       if (status & SND_JACK_BTN_0)
+                               snd_soc_jack_report(jack,
+                                               SND_JACK_HEADSET, mask);
+                       return;
+               }
+       } else if (intr_id & SN95031_JACK_BTN1) {
+               /* we get spurious interrupts if jack key is held down
+               * so we ignore them until key is released by checking the
+               * voltage level */
+               if (ctx->mfld_jack_lp_flag) {
+                       voltage = mfld_jack_read_voltage(jack);
+                       if (voltage > MFLD_LP_THRESHOLD_VOLTAGE) {
+                               snd_soc_jack_report(jack,
+                                               SND_JACK_HEADSET, mask);
+                               ctx->mfld_jack_lp_flag = 0;
+                               pr_debug("button released after long press\n");
+                       }
+                       return;
+               }
+               /* Codec sends separate long press event after button pressed
+                * for a specified time. Need to send separate button pressed
+                * and released events for Android */
+               status = SND_JACK_HEADSET | SND_JACK_BTN_0;
+               ctx->mfld_jack_lp_flag = 1;
+               pr_debug("long press detected\n");
+       } else {
+               pr_err("Invalid intr_id:0x%x\n", intr_id);
+               return;
+       }
+       mfld_jack_report(jack, status);
+}
 
-       if (SND_SOC_DAPM_EVENT_ON(event))
-               intel_scu_ipc_set_osc_clk0(true, clk_id);
-       else if (SND_SOC_DAPM_EVENT_OFF(event))
-               intel_scu_ipc_set_osc_clk0(false, clk_id);
-       return 0;
+static int mfld_schedule_jack_wq(struct mfld_jack_work *jack_work)
+{
+       return schedule_delayed_work(&jack_work->work,
+                       msecs_to_jiffies(MFLD_JACK_DEBOUNCE_TIME));
 }
 
-/* Callback to set volume for *VOLCTRL regs. Needs to be implemented separately
- * since clock and VAUDA need to be on before value can be written to the regs
+/* The Medfield jack takes additional time for the interrupts to settle,
+ * hence we have an software debounce mechanism so and take the value of
+ * the final interrupt reported withing the debounce time to be true
  */
-static int sn95031_set_vol_2r(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
+static void mfld_jack_detection(unsigned int intr_id,
+                               struct mfld_jack_work *jack_work)
 {
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int err;
-       unsigned int val, val2, val_mask;
-       int sst_pll_mode_saved;
-
-       val_mask = mask << shift;
-       val = (ucontrol->value.integer.value[0] & mask);
-       val2 = (ucontrol->value.integer.value[1] & mask);
-
-       if (invert) {
-               val = max - val;
-               val2 = max - val2;
+       int retval;
+       struct mfld_mc_private *ctx =
+               container_of(jack_work, struct mfld_mc_private, jack_work);
+
+       pr_debug("interrupt id read in sram = 0x%x\n", intr_id);
+
+       if (intr_id & SN95031_JACK_INSERTED ||
+                               intr_id & SN95031_JACK_REMOVED) {
+               ctx->jack_work.intr_id = intr_id;
+               retval = mfld_schedule_jack_wq(jack_work);
+               if (!retval)
+                       pr_debug("jack inserted/removed,intr already queued\n");
+               /* mask button press interrupts until jack is reported*/
+               snd_soc_update_bits(ctx->mfld_jack.codec,
+                    SN95031_ACCDETMASK, BIT(1)|BIT(0), BIT(1)|BIT(0));
+               return;
        }
 
-       val = val << shift;
-       val2 = val2 << shift;
-
-       pr_debug("enabling PLL and VAUDA to change volume\n");
-       mutex_lock(&codec->mutex);
-       sst_pll_mode_saved = intel_scu_ipc_set_osc_clk0(true, CLK0_QUERY);
-       intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
-       snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
-       snd_soc_dapm_sync(&codec->dapm);
-
-       err = snd_soc_update_bits(codec, reg, val_mask, val);
-       if (err < 0)
-               goto restore_state;
-
-       err = snd_soc_update_bits(codec, reg2, val_mask, val2);
-restore_state:
-       snd_soc_dapm_disable_pin(&codec->dapm, "VirtBias");
-       if (!(sst_pll_mode_saved & CLK0_MSIC))
-               intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
-       mutex_unlock(&codec->mutex);
-       return err;
+       if (intr_id & SN95031_JACK_BTN0 ||
+                               intr_id & SN95031_JACK_BTN1) {
+               if ((ctx->mfld_jack.status & SND_JACK_HEADSET) != 0) {
+                       ctx->jack_work.intr_id = intr_id;
+                       retval = mfld_schedule_jack_wq(jack_work);
+                       if (!retval) {
+                               pr_debug("spurious btn press, lp_flag:%d\n",
+                                               ctx->mfld_jack_lp_flag);
+                               return;
+                       }
+                       pr_debug("BTN_Press detected\n");
+               } else {
+                       pr_debug("BTN_press received, but jack is removed\n");
+               }
+       }
 }
 
 static const DECLARE_TLV_DB_SCALE(out_tlv, -6200, 100, 0);
 
 static const struct snd_kcontrol_new mfld_snd_controls[] = {
-       SOC_ENUM_EXT("Playback Switch", headset_enum,
-                       headset_get_switch, headset_set_switch),
-       SOC_ENUM_EXT("Lineout Mux", lo_enum,
-                       lo_get_switch, lo_set_switch),
-       SOC_ENUM_EXT("PCM1 Mode", SN95031_pcm1_mode_config_enum,
-                       sn95031_get_pcm1_mode, sn95031_set_pcm1_mode),
-       /* Add digital volume and mute controls for Headphone/Headset*/
+       SOC_ENUM_EXT("Playback Switch", mfld_headset_enum,
+                       mfld_headset_get_switch, mfld_headset_set_switch),
+       SOC_ENUM_EXT("Lineout Mux", sn95031_lo_enum,
+                       mfld_lo_get_switch, mfld_lo_set_switch),
+       SOC_ENUM_EXT("PCM1 Mode", sn95031_pcm1_mode_config_enum,
+                       mfld_get_pcm1_mode, mfld_set_pcm1_mode),
+       /* Add digital volume and mute controls for Headphone/Headset */
        SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", SN95031_HSLVOLCTRL,
                                SN95031_HSRVOLCTRL, 0, 71, 1,
-                               snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+                               snd_soc_get_volsw_2r, mfld_set_vol_2r,
                                out_tlv),
        SOC_DOUBLE_R_EXT_TLV("Speaker Playback Volume", SN95031_IHFLVOLCTRL,
                                SN95031_IHFRVOLCTRL, 0, 71, 1,
-                               snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+                               snd_soc_get_volsw_2r, mfld_set_vol_2r,
                                out_tlv),
 };
 
@@ -344,31 +292,11 @@ static const struct snd_soc_dapm_route mfld_map[] = {
        {"VIB2SPI", NULL, "Vibra2Clock"},
 };
 
-static void mfld_jack_check(unsigned int intr_status)
-{
-       struct mfld_jack_data jack_data;
-
-       jack_data.mfld_jack = &mfld_jack;
-       jack_data.intr_id = intr_status;
-
-       sn95031_jack_detection(&jack_data);
-       /* TODO: add american headset detection post gpiolib support */
-}
-
-static unsigned int async_param;
-static LIST_HEAD(mfld_jack_async_list);
-static void mfld_jack_check_async(void *ptr, async_cookie_t cookie)
-{
-       mfld_jack_check(*(unsigned int *)ptr);
-       return;
-}
-
 static int mfld_init(struct snd_soc_pcm_runtime *runtime)
 {
        struct snd_soc_codec *codec = runtime->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
        int ret_val;
 
        /* Add jack sense widgets */
@@ -388,8 +316,8 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime)
        /* default is lineout NC, userspace sets it explcitly */
        snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
        snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
-       mc_drv_ctx->lo_dac = 3;
-       mc_drv_ctx->hs_switch = 0;
+       ctx->sn95031_lo_dac = 3;
+       ctx->hs_switch = 0;
        /* we dont use linein in this so set to NC */
        snd_soc_dapm_disable_pin(dapm, "LINEINL");
        snd_soc_dapm_disable_pin(dapm, "LINEINR");
@@ -415,26 +343,26 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime)
        mutex_unlock(&codec->mutex);
        /* Headset and button jack detection */
        ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
-                       SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                       SND_JACK_BTN_1, &mfld_jack);
+                       SND_JACK_HEADSET | SND_JACK_BTN_0,
+                       &ctx->mfld_jack);
        if (ret_val) {
                pr_err("jack creation failed\n");
                return ret_val;
        }
 
-       ret_val = snd_soc_jack_add_zones(&mfld_jack,
+       ret_val = snd_soc_jack_add_zones(&ctx->mfld_jack,
                        ARRAY_SIZE(mfld_zones), mfld_zones);
        if (ret_val) {
                pr_err("adding jack zones failed\n");
                return ret_val;
        }
 
+       ctx->jack_work.jack = &ctx->mfld_jack;
        /* we want to check if anything is inserted at boot,
         * so send a fake event to codec and it will read adc
         * to find if anything is there or not */
-       async_param = MFLD_JACK_INSERT;
-       async_schedule_domain(mfld_jack_check_async,
-                       &async_param, &mfld_jack_async_list);
+       ctx->jack_work.intr_id = MFLD_JACK_INSERT_ID;
+       mfld_schedule_jack_wq(&ctx->jack_work);
        return ret_val;
 }
 
@@ -473,7 +401,7 @@ static int mfld_media_hw_params(struct snd_pcm_substream *substream,
        snd_soc_dapm_sync(&codec->dapm);
        mutex_unlock(&codec->mutex);
        usleep_range(5000, 6000);
-       sn95031_configure_pll(codec, ENABLE_PLL);
+       sn95031_configure_pll(codec, SN95031_ENABLE_PLL);
 
        /* enable PCM2 */
        snd_soc_dai_set_tristate(rtd->codec_dai, 0);
@@ -486,10 +414,10 @@ static int mfld_voice_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *soc_card = rtd->card;
-       struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(soc_card);
        pr_debug("%s\n", __func__);
 
-       if (mc_drv_ctx->pcm1_master_mode) { /* VOIP call */
+       if (ctx->sn95031_pcm1_mode) { /* VOIP call */
                snd_soc_codec_set_pll(codec, 0, SN95031_PLLIN, 1, 1);
                snd_soc_dai_set_fmt(rtd->codec_dai, SND_SOC_DAIFMT_CBM_CFM
                                                | SND_SOC_DAIFMT_DSP_A);
@@ -509,7 +437,7 @@ static int mfld_voice_hw_params(struct snd_pcm_substream *substream,
        snd_soc_dapm_sync(&codec->dapm);
        mutex_unlock(&codec->mutex);
        usleep_range(5000, 6000);
-       sn95031_configure_pll(codec, ENABLE_PLL);
+       sn95031_configure_pll(codec, SN95031_ENABLE_PLL);
        return 0;
 }
 
@@ -670,7 +598,7 @@ static int mfld_card_stream_event(struct snd_soc_dapm_context *dapm, int event)
        pr_debug("machine stream event: %d\n", event);
        if (event == SND_SOC_DAPM_STREAM_STOP) {
                if (!codec->active) {
-                       sn95031_configure_pll(codec, DISABLE_PLL);
+                       sn95031_configure_pll(codec, SN95031_DISABLE_PLL);
                        return intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
                }
        }
@@ -686,18 +614,17 @@ static struct snd_soc_card snd_soc_card_mfld = {
 
 static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
 {
-       struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
+       struct mfld_mc_private *ctx = (struct mfld_mc_private *) dev;
        u16 intr_status = 0;
 
-       memcpy_fromio(&intr_status, ((void *)(mc_private->int_base)),
-                       sizeof(u16));
+       memcpy_fromio(&intr_status, ((void *)(ctx->int_base)), sizeof(u16));
        /* not overwrite status here */
-       spin_lock(&mc_private->lock);
+       spin_lock(&ctx->lock);
        /*To retrieve the jack_interrupt_status value (MSB)*/
-       mc_private->jack_interrupt_status |= 0x0F & (intr_status >> 8);
+       ctx->jack_interrupt_status |= 0x0F & (intr_status >> 8);
        /*To retrieve the oc_interrupt_status value (LSB)*/
-       mc_private->oc_interrupt_status |= 0x1F & intr_status;
-       spin_unlock(&mc_private->lock);
+       ctx->oc_interrupt_status |= 0x1F & intr_status;
+       spin_unlock(&ctx->lock);
 #ifdef CONFIG_HAS_WAKELOCK
        /*
         * We don't have any call back from the jack detection completed.
@@ -705,44 +632,43 @@ static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
         * to finish. Jack detection is happening rarely so this doesn't
         * have big impact to power consumption.
         */
-       wake_lock_timeout(&mc_private->wake_lock, 2*HZ);
+       wake_lock_timeout(ctx->jack_wake_lock, 2*HZ);
 #endif
        return IRQ_WAKE_THREAD;
 }
 
 static irqreturn_t snd_mfld_codec_intr_detection(int irq, void *data)
 {
-       struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
+       struct mfld_mc_private *ctx = (struct mfld_mc_private *) data;
        unsigned long flags;
        u8 jack_int_value = 0;
 
-       if (mfld_jack.codec == NULL) {
+       if (ctx->mfld_jack.codec == NULL) {
                pr_debug("codec NULL returning..");
-               spin_lock_irqsave(&mc_drv_ctx->lock, flags);
-               mc_drv_ctx->jack_interrupt_status = 0;
-               mc_drv_ctx->oc_interrupt_status = 0;
-               spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
+               spin_lock_irqsave(&ctx->lock, flags);
+               ctx->jack_interrupt_status = 0;
+               ctx->oc_interrupt_status = 0;
+               spin_unlock_irqrestore(&ctx->lock, flags);
                goto ret;
        }
-       spin_lock_irqsave(&mc_drv_ctx->lock, flags);
-       if (!((mc_drv_ctx->jack_interrupt_status) ||
-                       (mc_drv_ctx->oc_interrupt_status))) {
-               spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
+       spin_lock_irqsave(&ctx->lock, flags);
+       if (!(ctx->jack_interrupt_status || ctx->oc_interrupt_status)) {
+               spin_unlock_irqrestore(&ctx->lock, flags);
                pr_err("OC and Jack Intr with status 0, return....\n");
                goto ret;
        }
-       if (mc_drv_ctx->oc_interrupt_status) {
-               pr_info("OC int value: %d\n", mc_drv_ctx->oc_interrupt_status);
-               mc_drv_ctx->oc_interrupt_status = 0;
+       if (ctx->oc_interrupt_status) {
+               pr_info("OC int value: %d\n", ctx->oc_interrupt_status);
+               ctx->oc_interrupt_status = 0;
        }
-       if (mc_drv_ctx->jack_interrupt_status) {
-               jack_int_value = mc_drv_ctx->jack_interrupt_status;
-               mc_drv_ctx->jack_interrupt_status = 0;
+       if (ctx->jack_interrupt_status) {
+               jack_int_value = ctx->jack_interrupt_status;
+               ctx->jack_interrupt_status = 0;
        }
-       spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
+       spin_unlock_irqrestore(&ctx->lock, flags);
 
        if (jack_int_value)
-               mfld_jack_check(jack_int_value);
+               mfld_jack_detection(jack_int_value, &ctx->jack_work);
 
 ret:
        return IRQ_HANDLED;
@@ -751,7 +677,7 @@ ret:
 static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
 {
        int ret_val = 0, irq;
-       struct mfld_mc_private *mc_drv_ctx;
+       struct mfld_mc_private *ctx;
        struct resource *irq_mem;
 
        pr_debug("snd_mfld_mc_probe called\n");
@@ -761,43 +687,47 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
 
        /* audio interrupt base of SRAM location where
         * interrupts are stored by System FW */
-       mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
-       if (!mc_drv_ctx) {
+       ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx) {
                pr_err("allocation failed\n");
                return -ENOMEM;
        }
-       spin_lock_init(&mc_drv_ctx->lock);
+       spin_lock_init(&ctx->lock);
 #ifdef CONFIG_HAS_WAKELOCK
-       wake_lock_init(&mc_drv_ctx->wake_lock,
-                      WAKE_LOCK_SUSPEND, "jack_detect");
+       ctx->jack_wake_lock =
+               kzalloc(sizeof(*(ctx->jack_wake_lock)), GFP_ATOMIC);
+       wake_lock_init(ctx->jack_wake_lock, WAKE_LOCK_SUSPEND, "jack_detect");
 #endif
 
-       irq_mem = ipc_get_resource_byname(
-                               ipcdev, IORESOURCE_MEM, "IRQ_BASE");
+       irq_mem = ipc_get_resource_byname(ipcdev, IORESOURCE_MEM, "IRQ_BASE");
        if (!irq_mem) {
                pr_err("no mem resource given\n");
                ret_val = -ENODEV;
                goto unalloc;
        }
 
+       /*GPADC handle for audio_detection*/
+       ctx->audio_adc_handle =
+               intel_mid_gpadc_alloc(MFLD_AUDIO_SENSOR,
+                                     MFLD_AUDIO_DETECT_CODE);
+       if (!ctx->audio_adc_handle) {
+               pr_err("invalid ADC handle\n");
+               ret_val = -ENOMEM;
+               goto unalloc;
+       }
+       INIT_DELAYED_WORK(&ctx->jack_work.work, mfld_jack_wq);
+
        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);
+               ret_val = gpio_request_one(MFLD_GPIO_HEADSET_DET_PIN,
+                                          GPIOF_DIR_IN, "headset_detect_gpio");
                if (ret_val) {
-                       pr_err("HEADSET GPIO direction wrong: %d\n", ret_val);
-                       kfree(mc_drv_ctx);
-                       return ret_val;
+                       pr_err("Headset detect GPIO alloc fail:%d\n", ret_val);
+                       goto unalloc;
                }
        }
 
-       mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
-                                       resource_size(irq_mem));
-       if (!mc_drv_ctx->int_base) {
+       ctx->int_base = ioremap_nocache(irq_mem->start, resource_size(irq_mem));
+       if (!ctx->int_base) {
                pr_err("Mapping of cache failed\n");
                ret_val = -ENOMEM;
                goto unalloc;
@@ -806,7 +736,7 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
        ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
                        snd_mfld_codec_intr_detection,
                        IRQF_SHARED | IRQF_NO_SUSPEND,
-                       ipcdev->dev.driver->name, mc_drv_ctx);
+                       ipcdev->dev.driver->name, ctx);
        if (ret_val) {
                pr_err("cannot register IRQ\n");
                goto unalloc;
@@ -814,7 +744,7 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
        /* register the soc card */
        snd_soc_card_mfld.dev = &ipcdev->dev;
        snd_soc_card_mfld.dapm.stream_event = mfld_card_stream_event;
-       snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
+       snd_soc_card_set_drvdata(&snd_soc_card_mfld, ctx);
        ret_val = snd_soc_register_card(&snd_soc_card_mfld);
        if (ret_val) {
                pr_debug("snd_soc_register_card failed %d\n", ret_val);
@@ -825,26 +755,29 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
        return ret_val;
 
 freeirq:
-       free_irq(irq, mc_drv_ctx);
+       free_irq(irq, ctx);
 unalloc:
-       kfree(mc_drv_ctx);
+       kfree(ctx);
        return ret_val;
 }
 
 static int __devexit snd_mfld_mc_remove(struct ipc_device *ipcdev)
 {
        struct snd_soc_card *soc_card = ipc_get_drvdata(ipcdev);
-       struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(soc_card);
        pr_debug("snd_mfld_mc_remove called\n");
-       free_irq(ipc_get_irq(ipcdev, 0), mc_drv_ctx);
+       free_irq(ipc_get_irq(ipcdev, 0), ctx);
 #ifdef CONFIG_HAS_WAKELOCK
-       if (wake_lock_active(&mc_drv_ctx->wake_lock))
-               wake_unlock(&mc_drv_ctx->wake_lock);
-       wake_lock_destroy(&mc_drv_ctx->wake_lock);
+       if (wake_lock_active(ctx->jack_wake_lock))
+               wake_unlock(ctx->jack_wake_lock);
+       wake_lock_destroy(ctx->jack_wake_lock);
+       kfree(ctx->jack_wake_lock);
 #endif
-       kfree(mc_drv_ctx);
+       cancel_delayed_work(&ctx->jack_work.work);
+       intel_mid_gpadc_free(ctx->audio_adc_handle);
+       kfree(ctx);
        if (mfld_board_id() == MFLD_BID_PR3)
-               gpio_free(HEADSET_DET_PIN);
+               gpio_free(MFLD_GPIO_HEADSET_DET_PIN);
        snd_soc_card_set_drvdata(soc_card, NULL);
        snd_soc_unregister_card(soc_card);
        ipc_set_drvdata(ipcdev, NULL);
@@ -876,7 +809,6 @@ late_initcall(snd_mfld_driver_init);
 static void __exit snd_mfld_driver_exit(void)
 {
        pr_debug("snd_mfld_driver_exit called\n");
-       async_synchronize_full_domain(&mfld_jack_async_list);
        ipc_driver_unregister(&snd_mfld_mc_driver);
 }
 module_exit(snd_mfld_driver_exit);
index 8482c6a..7d50cbb 100644 (file)
 #include <linux/io.h>
 #include <linux/async.h>
 #include <linux/wakelock.h>
-#include <linux/gpio.h>
 #include <linux/ipc_device.h>
 #include <asm/intel-mid.h>
 #include <asm/intel_scu_ipcutil.h>
+#include <asm/intel_mid_gpadc.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <sound/tlv.h>
 #include "../codecs/sn95031.h"
+#include "mfld_common.h"
 
-#define MFLD_JACK_INSERT 0x04
-#define HEADSET_DET_PIN 77
-
-enum soc_mic_bias_zones {
-       MFLD_MV_START = 0,
-       /* mic bias volutage range for Headphones*/
-       MFLD_MV_HP = 400,
-       /* mic bias volutage range for American Headset*/
-       MFLD_MV_AM_HS = 650,
-       /* mic bias volutage range for Headset*/
-       MFLD_MV_HS = 2000,
-       MFLD_MV_UNDEFINED,
-};
-
-struct mfld_mc_private {
-       void __iomem *int_base;
-       u8 jack_interrupt_status;
-       u8 oc_interrupt_status;
-       spinlock_t lock; /* lock for interrupt status and jack debounce */
-       int pcm1_master_mode;
-       unsigned int hs_switch;
-#ifdef CONFIG_HAS_WAKELOCK
-       struct wake_lock wake_lock;
-#endif
-};
-
-static struct snd_soc_jack mfld_jack;
+/* The GI jack is different, debounce time needs to be more */
+#define MFLD_JACK_DEBOUNCE_TIME          700 /* mS */
 
 /* jack detection voltage zones */
 static struct snd_soc_jack_zone mfld_zones[] = {
@@ -78,161 +54,197 @@ static struct snd_soc_jack_zone mfld_zones[] = {
        {MFLD_MV_AM_HS, MFLD_MV_HS, SND_JACK_HEADSET},
 };
 
-/* sound card controls */
-static const char *headset_switch_text[] = {"Earpiece", "Headset"};
-
-static const struct soc_enum headset_enum =
-       SOC_ENUM_SINGLE_EXT(2, headset_switch_text);
-
-static const char *sn95031_pcm1_mode_text[] = {"Slave", "Master"};
-
-static const struct soc_enum SN95031_pcm1_mode_config_enum =
-       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(sn95031_pcm1_mode_text),
-                                                       sn95031_pcm1_mode_text);
-
-static int headset_get_switch(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static void mfld_jack_enable_mic_bias(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-       ucontrol->value.integer.value[0] = mc_drv_ctx->hs_switch;
-       return 0;
+       pr_debug("enable mic bias\n");
+       mutex_lock(&codec->mutex);
+       /* FIXME: GI has micbias swapped, change this when HW is fixed */
+       snd_soc_dapm_force_enable_pin(&codec->dapm, "AMIC2Bias");
+       snd_soc_dapm_sync(&codec->dapm);
+       mutex_unlock(&codec->mutex);
 }
 
-static int headset_set_switch(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+static void mfld_jack_disable_mic_bias(struct snd_soc_codec *codec)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
-
-       if (ucontrol->value.integer.value[0] == mc_drv_ctx->hs_switch)
-               return 0;
-
-       if (ucontrol->value.integer.value[0]) {
-               pr_debug("hs_set HS path\n");
-               snd_soc_dapm_enable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_disable_pin(&codec->dapm, "EPOUT");
-       } else {
-               pr_debug("hs_set EP path\n");
-               snd_soc_dapm_disable_pin(&codec->dapm, "Headphones");
-               snd_soc_dapm_enable_pin(&codec->dapm, "EPOUT");
-       }
+       pr_debug("disable mic bias\n");
        mutex_lock(&codec->mutex);
+       snd_soc_dapm_disable_pin(&codec->dapm, "AMIC2Bias");
        snd_soc_dapm_sync(&codec->dapm);
        mutex_unlock(&codec->mutex);
-       mc_drv_ctx->hs_switch = ucontrol->value.integer.value[0];
-
-       return 0;
 }
 
-static int sn95031_get_pcm1_mode(struct snd_kcontrol *kcontrol,
-                                       struct snd_ctl_elem_value *ucontrol)
+static int mfld_get_headset_state(struct snd_soc_jack *jack)
 {
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
+       int micbias, jack_type;
 
-       pr_debug("PCM1 master mode: %d\n", mc_drv_ctx->pcm1_master_mode);
-       ucontrol->value.integer.value[0] = mc_drv_ctx->pcm1_master_mode;
-       return 0;
-}
+       mfld_jack_enable_mic_bias(jack->codec);
+       micbias = mfld_jack_read_voltage(jack);
 
-static int sn95031_set_pcm1_mode(struct snd_kcontrol *kcontrol,
-                               struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_codec *codec =  snd_kcontrol_chip(kcontrol);
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
+       jack_type = snd_soc_jack_get_type(jack, micbias);
+       pr_debug("jack type detected = %d, micbias = %d\n", jack_type, micbias);
 
-       mc_drv_ctx->pcm1_master_mode = ucontrol->value.integer.value[0];
-       return 0;
+       if (jack_type == SND_JACK_HEADSET)
+               /* enable btn press detection */
+               snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(0), 1);
+       else
+               mfld_jack_disable_mic_bias(jack->codec);
+
+       return jack_type;
 }
 
-static int mfld_vibra_enable_clk(struct snd_soc_dapm_widget *w,
-                       struct snd_kcontrol *k, int event)
+static void mfld_jack_report(struct snd_soc_jack *jack, unsigned int status)
 {
-       int clk_id = 0;
-
-       if (!strcmp(w->name, "Vibra1Clock"))
-               clk_id = CLK0_VIBRA1;
-       else if (!strcmp(w->name, "Vibra2Clock"))
-               clk_id = CLK0_VIBRA2;
-
-       if (SND_SOC_DAPM_EVENT_ON(event))
-               intel_scu_ipc_set_osc_clk0(true, clk_id);
-       else if (SND_SOC_DAPM_EVENT_OFF(event))
-               intel_scu_ipc_set_osc_clk0(false, clk_id);
-       return 0;
+       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_HEADSET;
+
+       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,
+                * this is required for jack detection to work well */
+               snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(1), 0);
+       } 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
+       /* report to the switch driver as well */
+       if (status) {
+               if (status == SND_JACK_HEADPHONE)
+                       mid_headset_report((1<<1));
+               else if (status == SND_JACK_HEADSET)
+                       mid_headset_report(1);
+       } else {
+               mid_headset_report(0);
+       }
+#endif
 }
 
-/* Callback to set volume for *VOLCTRL regs. Needs to be implemented separately
- * since clock and VAUDA need to be on before value can be written to the regs
- */
-static int sn95031_set_vol_2r(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
+void mfld_jack_wq(struct work_struct *work)
 {
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int err;
-       unsigned int val, val2, val_mask;
-       int sst_pll_mode_saved;
-
-       val_mask = mask << shift;
-       val = (ucontrol->value.integer.value[0] & mask);
-       val2 = (ucontrol->value.integer.value[1] & mask);
-
-       if (invert) {
-               val = max - val;
-               val2 = max - val2;
+       unsigned int mask = SND_JACK_BTN_0 | SND_JACK_HEADSET;
+       struct mfld_mc_private *ctx =
+               container_of(work, struct mfld_mc_private, jack_work.work.work);
+       struct mfld_jack_work *jack_work = &ctx->jack_work;
+       struct snd_soc_jack *jack = jack_work->jack;
+       unsigned int voltage, status = 0, intr_id = jack_work->intr_id;
+
+       pr_debug("jack status in wq: 0x%x\n", intr_id);
+       if (intr_id & SN95031_JACK_INSERTED) {
+               status = mfld_get_headset_state(jack);
+               /* unmask button press interrupts */
+               if (status == SND_JACK_HEADSET)
+                       snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK,
+                                                       BIT(1)|BIT(0), 0);
+       } else if (intr_id & SN95031_JACK_REMOVED) {
+               pr_debug("reporting jack as removed\n");
+               snd_soc_update_bits(jack->codec, SN95031_BTNCTRL2, BIT(0), 0);
+               snd_soc_update_bits(jack->codec, SN95031_ACCDETMASK, BIT(2), 0);
+               mfld_jack_disable_mic_bias(jack->codec);
+               jack_work->intr_id = 0;
+               cancel_delayed_work(&ctx->jack_work.work);
+       } else if (intr_id & SN95031_JACK_BTN0) {
+               if (ctx->mfld_jack_lp_flag) {
+                       snd_soc_jack_report(jack, SND_JACK_HEADSET, mask);
+                       ctx->mfld_jack_lp_flag = 0;
+                       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");
+                       snd_soc_jack_report(jack, status, mask);
+                       /* send explicit button release */
+                       if (status & SND_JACK_BTN_0)
+                               snd_soc_jack_report(jack,
+                                               SND_JACK_HEADSET, mask);
+                       return;
+               }
+       } else if (intr_id & SN95031_JACK_BTN1) {
+               /* we get spurious interrupts if jack key is held down
+               * so we ignore them until key is released by checking the
+               * voltage level */
+               if (ctx->mfld_jack_lp_flag) {
+                       voltage = mfld_jack_read_voltage(jack);
+                       if (voltage > MFLD_LP_THRESHOLD_VOLTAGE) {
+                               snd_soc_jack_report(jack,
+                                               SND_JACK_HEADSET, mask);
+                               ctx->mfld_jack_lp_flag = 0;
+                               pr_debug("button released after long press\n");
+                       }
+                       return;
+               }
+               /* Codec sends separate long press event after button pressed
+                * for a specified time. Need to send separate button pressed
+                * and released events for Android */
+               status = SND_JACK_HEADSET | SND_JACK_BTN_0;
+               ctx->mfld_jack_lp_flag = 1;
+               pr_debug("long press detected\n");
+       } else {
+               pr_err("Invalid intr_id:0x%x\n", intr_id);
+               return;
        }
+       mfld_jack_report(jack, status);
+}
 
-       val = val << shift;
-       val2 = val2 << shift;
-
-       pr_debug("enabling PLL and VAUDA to change volume\n");
-       mutex_lock(&codec->mutex);
-       sst_pll_mode_saved = intel_scu_ipc_set_osc_clk0(true, CLK0_QUERY);
-       intel_scu_ipc_set_osc_clk0(true, CLK0_MSIC);
-       snd_soc_dapm_force_enable_pin(&codec->dapm, "VirtBias");
-       snd_soc_dapm_sync(&codec->dapm);
+static int mfld_schedule_jack_wq(struct mfld_jack_work *jack_work)
+{
+       return schedule_delayed_work(&jack_work->work,
+                       msecs_to_jiffies(MFLD_JACK_DEBOUNCE_TIME));
+}
 
-       err = snd_soc_update_bits(codec, reg, val_mask, val);
-       if (err < 0)
-               goto restore_state;
+static void mfld_jack_detection(unsigned int intr_id,
+                               struct mfld_jack_work *jack_work)
+{
+       int retval;
+       struct mfld_mc_private *ctx =
+               container_of(jack_work, struct mfld_mc_private, jack_work);
+
+       pr_debug("interrupt id read in sram = 0x%x\n", intr_id);
+
+       if (intr_id & SN95031_JACK_INSERTED ||
+                               intr_id & SN95031_JACK_REMOVED) {
+               ctx->jack_work.intr_id = intr_id;
+               retval = mfld_schedule_jack_wq(jack_work);
+               if (!retval)
+                       pr_debug("jack inserted/removed,intr already queued\n");
+               /* mask button press interrupts until jack is reported*/
+               snd_soc_update_bits(ctx->mfld_jack.codec,
+                    SN95031_ACCDETMASK, BIT(1)|BIT(0), BIT(1)|BIT(0));
+               return;
+       }
 
-       err = snd_soc_update_bits(codec, reg2, val_mask, val2);
-restore_state:
-       snd_soc_dapm_disable_pin(&codec->dapm, "VirtBias");
-       if (!(sst_pll_mode_saved & CLK0_MSIC))
-               intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
-       mutex_unlock(&codec->mutex);
-       return err;
+       if (intr_id & SN95031_JACK_BTN0 ||
+                               intr_id & SN95031_JACK_BTN1) {
+               if ((ctx->mfld_jack.status & SND_JACK_HEADSET) != 0) {
+                       ctx->jack_work.intr_id = intr_id;
+                       retval = mfld_schedule_jack_wq(jack_work);
+                       if (!retval) {
+                               pr_debug("spurious btn press, lp_flag:%d\n",
+                                               ctx->mfld_jack_lp_flag);
+                               return;
+                       }
+                       pr_debug("BTN_Press detected\n");
+               } else {
+                       pr_debug("BTN_press received, but jack is removed\n");
+               }
+       }
 }
 
 static const DECLARE_TLV_DB_SCALE(out_tlv, -6200, 100, 0);
 
 static const struct snd_kcontrol_new mfld_snd_controls[] = {
-       SOC_ENUM_EXT("Playback Switch", headset_enum,
-                       headset_get_switch, headset_set_switch),
-       SOC_ENUM_EXT("PCM1 Mode", SN95031_pcm1_mode_config_enum,
-                       sn95031_get_pcm1_mode, sn95031_set_pcm1_mode),
+       SOC_ENUM_EXT("Playback Switch", mfld_headset_enum,
+                       mfld_headset_get_switch, mfld_headset_set_switch),
+       SOC_ENUM_EXT("PCM1 Mode", sn95031_pcm1_mode_config_enum,
+                       mfld_get_pcm1_mode, mfld_set_pcm1_mode),
        /* Add digital volume and mute controls for Headphone/Headset*/
        SOC_DOUBLE_R_EXT_TLV("Headphone Playback Volume", SN95031_HSLVOLCTRL,
                                SN95031_HSRVOLCTRL, 0, 71, 1,
-                               snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+                               snd_soc_get_volsw_2r, mfld_set_vol_2r,
                                out_tlv),
        SOC_DOUBLE_R_EXT_TLV("Speaker Playback Volume", SN95031_IHFLVOLCTRL,
                                SN95031_IHFRVOLCTRL, 0, 71, 1,
-                               snd_soc_get_volsw_2r, sn95031_set_vol_2r,
+                               snd_soc_get_volsw_2r, mfld_set_vol_2r,
                                out_tlv),
 };
 
@@ -262,31 +274,11 @@ static const struct snd_soc_dapm_route mfld_map[] = {
        {"VIB2SPI", NULL, "Vibra2Clock"},
 };
 
-static void mfld_jack_check(unsigned int intr_status)
-{
-       struct mfld_jack_data jack_data;
-
-       jack_data.mfld_jack = &mfld_jack;
-       jack_data.intr_id = intr_status;
-
-       sn95031_jack_detection(&jack_data);
-       /* TODO: add american headset detection post gpiolib support */
-}
-
-static unsigned int async_param;
-static LIST_HEAD(mfld_jack_async_list);
-static void mfld_jack_check_async(void *ptr, async_cookie_t cookie)
-{
-       mfld_jack_check(*(unsigned int *)ptr);
-       return;
-}
-
 static int mfld_init(struct snd_soc_pcm_runtime *runtime)
 {
        struct snd_soc_codec *codec = runtime->codec;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       struct mfld_mc_private *mc_drv_ctx =
-                       snd_soc_card_get_drvdata(codec->card);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
        int ret_val;
 
        /* Add jack sense widgets */
@@ -306,7 +298,7 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime)
        /* default is lineout NC, userspace sets it explcitly */
        snd_soc_dapm_disable_pin(dapm, "LINEOUTL");
        snd_soc_dapm_disable_pin(dapm, "LINEOUTR");
-       mc_drv_ctx->hs_switch = 0;
+       ctx->hs_switch = 0;
        /* we dont use linein in this so set to NC */
        snd_soc_dapm_disable_pin(dapm, "LINEINL");
        snd_soc_dapm_disable_pin(dapm, "LINEINR");
@@ -332,26 +324,26 @@ static int mfld_init(struct snd_soc_pcm_runtime *runtime)
        mutex_unlock(&codec->mutex);
        /* Headset and button jack detection */
        ret_val = snd_soc_jack_new(codec, "Intel(R) MID Audio Jack",
-                       SND_JACK_HEADSET | SND_JACK_BTN_0 |
-                       SND_JACK_BTN_1, &mfld_jack);
+                       SND_JACK_HEADSET | SND_JACK_BTN_0,
+                       &ctx->mfld_jack);
        if (ret_val) {
                pr_err("jack creation failed\n");
                return ret_val;
        }
 
-       ret_val = snd_soc_jack_add_zones(&mfld_jack,
+       ret_val = snd_soc_jack_add_zones(&ctx->mfld_jack,
                        ARRAY_SIZE(mfld_zones), mfld_zones);
        if (ret_val) {
                pr_err("adding jack zones failed\n");
                return ret_val;
        }
 
+       ctx->jack_work.jack = &ctx->mfld_jack;
        /* we want to check if anything is inserted at boot,
         * so send a fake event to codec and it will read adc
         * to find if anything is there or not */
-       async_param = MFLD_JACK_INSERT;
-       async_schedule_domain(mfld_jack_check_async,
-                       &async_param, &mfld_jack_async_list);
+       ctx->jack_work.intr_id = MFLD_JACK_INSERT_ID;
+       mfld_schedule_jack_wq(&ctx->jack_work);
        return ret_val;
 }
 
@@ -385,7 +377,7 @@ static int mfld_media_hw_params(struct snd_pcm_substream *substream,
        snd_soc_dapm_sync(&codec->dapm);
        mutex_unlock(&codec->mutex);
        usleep_range(5000, 6000);
-       sn95031_configure_pll(codec, ENABLE_PLL);
+       sn95031_configure_pll(codec, SN95031_ENABLE_PLL);
 
        /* enable PCM2 */
        snd_soc_dai_set_tristate(rtd->codec_dai, 0);
@@ -398,10 +390,10 @@ static int mfld_voice_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *soc_card = rtd->card;
-       struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(soc_card);
        pr_debug("%s\n", __func__);
 
-       if (mc_drv_ctx->pcm1_master_mode) { /* VOIP call */
+       if (ctx->sn95031_pcm1_mode) { /* VOIP call */
                snd_soc_codec_set_pll(codec, 0, SN95031_PLLIN, 1, 1);
                snd_soc_dai_set_fmt(rtd->codec_dai, SND_SOC_DAIFMT_CBM_CFM
                                                | SND_SOC_DAIFMT_DSP_A);
@@ -421,7 +413,7 @@ static int mfld_voice_hw_params(struct snd_pcm_substream *substream,
        snd_soc_dapm_sync(&codec->dapm);
        mutex_unlock(&codec->mutex);
        usleep_range(5000, 6000);
-       sn95031_configure_pll(codec, ENABLE_PLL);
+       sn95031_configure_pll(codec, SN95031_ENABLE_PLL);
        return 0;
 }
 
@@ -553,7 +545,7 @@ static int mfld_card_stream_event(struct snd_soc_dapm_context *dapm, int event)
        pr_debug("machine stream event: %d\n", event);
        if (event == SND_SOC_DAPM_STREAM_STOP) {
                if (!codec->active) {
-                       sn95031_configure_pll(codec, DISABLE_PLL);
+                       sn95031_configure_pll(codec, SN95031_DISABLE_PLL);
                        return intel_scu_ipc_set_osc_clk0(false, CLK0_MSIC);
                }
        }
@@ -569,18 +561,17 @@ static struct snd_soc_card snd_soc_card_mfld = {
 
 static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
 {
-       struct mfld_mc_private *mc_private = (struct mfld_mc_private *) dev;
+       struct mfld_mc_private *ctx = (struct mfld_mc_private *) dev;
        u16 intr_status = 0;
 
-       memcpy_fromio(&intr_status, ((void *)(mc_private->int_base)),
-                       sizeof(u16));
+       memcpy_fromio(&intr_status, ((void *)(ctx->int_base)), sizeof(u16));
        /* not overwrite status here */
-       spin_lock(&mc_private->lock);
+       spin_lock(&ctx->lock);
        /*To retrieve the jack_interrupt_status value (MSB)*/
-       mc_private->jack_interrupt_status |= 0x0F & (intr_status >> 8);
+       ctx->jack_interrupt_status |= 0x0F & (intr_status >> 8);
        /*To retrieve the oc_interrupt_status value (LSB)*/
-       mc_private->oc_interrupt_status |= 0x1F & intr_status;
-       spin_unlock(&mc_private->lock);
+       ctx->oc_interrupt_status |= 0x1F & intr_status;
+       spin_unlock(&ctx->lock);
 #ifdef CONFIG_HAS_WAKELOCK
        /*
         * We don't have any call back from the jack detection completed.
@@ -588,44 +579,43 @@ static irqreturn_t snd_mfld_jack_intr_handler(int irq, void *dev)
         * to finish. Jack detection is happening rarely so this doesn't
         * have big impact to power consumption.
         */
-       wake_lock_timeout(&mc_private->wake_lock, 2*HZ);
+       wake_lock_timeout(ctx->jack_wake_lock, 2*HZ);
 #endif
        return IRQ_WAKE_THREAD;
 }
 
 static irqreturn_t snd_mfld_codec_intr_detection(int irq, void *data)
 {
-       struct mfld_mc_private *mc_drv_ctx = (struct mfld_mc_private *) data;
+       struct mfld_mc_private *ctx = (struct mfld_mc_private *) data;
        unsigned long flags;
        u8 jack_int_value = 0;
 
-       if (mfld_jack.codec == NULL) {
+       if (ctx->mfld_jack.codec == NULL) {
                pr_debug("codec NULL returning..");
-               spin_lock_irqsave(&mc_drv_ctx->lock, flags);
-               mc_drv_ctx->jack_interrupt_status = 0;
-               mc_drv_ctx->oc_interrupt_status = 0;
-               spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
+               spin_lock_irqsave(&ctx->lock, flags);
+               ctx->jack_interrupt_status = 0;
+               ctx->oc_interrupt_status = 0;
+               spin_unlock_irqrestore(&ctx->lock, flags);
                goto ret;
        }
-       spin_lock_irqsave(&mc_drv_ctx->lock, flags);
-       if (!((mc_drv_ctx->jack_interrupt_status) ||
-                       (mc_drv_ctx->oc_interrupt_status))) {
-               spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
+       spin_lock_irqsave(&ctx->lock, flags);
+       if (!(ctx->jack_interrupt_status || ctx->oc_interrupt_status)) {
+               spin_unlock_irqrestore(&ctx->lock, flags);
                pr_err("OC and Jack Intr with status 0, return....\n");
                goto ret;
        }
-       if (mc_drv_ctx->oc_interrupt_status) {
-               pr_info("OC int value: %d\n", mc_drv_ctx->oc_interrupt_status);
-               mc_drv_ctx->oc_interrupt_status = 0;
+       if (ctx->oc_interrupt_status) {
+               pr_info("OC int value: %d\n", ctx->oc_interrupt_status);
+               ctx->oc_interrupt_status = 0;
        }
-       if (mc_drv_ctx->jack_interrupt_status) {
-               jack_int_value = mc_drv_ctx->jack_interrupt_status;
-               mc_drv_ctx->jack_interrupt_status = 0;
+       if (ctx->jack_interrupt_status) {
+               jack_int_value = ctx->jack_interrupt_status;
+               ctx->jack_interrupt_status = 0;
        }
-       spin_unlock_irqrestore(&mc_drv_ctx->lock, flags);
+       spin_unlock_irqrestore(&ctx->lock, flags);
 
        if (jack_int_value)
-               mfld_jack_check(jack_int_value);
+               mfld_jack_detection(jack_int_value, &ctx->jack_work);
 
 ret:
        return IRQ_HANDLED;
@@ -634,7 +624,7 @@ ret:
 static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
 {
        int ret_val = 0, irq;
-       struct mfld_mc_private *mc_drv_ctx;
+       struct mfld_mc_private *ctx;
        struct resource *irq_mem;
 
        pr_debug("snd_mfld_mc_probe called\n");
@@ -644,43 +634,39 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
 
        /* audio interrupt base of SRAM location where
         * interrupts are stored by System FW */
-       mc_drv_ctx = kzalloc(sizeof(*mc_drv_ctx), GFP_ATOMIC);
-       if (!mc_drv_ctx) {
+       ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+       if (!ctx) {
                pr_err("allocation failed\n");
                return -ENOMEM;
        }
-       spin_lock_init(&mc_drv_ctx->lock);
+       spin_lock_init(&ctx->lock);
 #ifdef CONFIG_HAS_WAKELOCK
-       wake_lock_init(&mc_drv_ctx->wake_lock,
+       ctx->jack_wake_lock =
+               kzalloc(sizeof(*(ctx->jack_wake_lock)), GFP_ATOMIC);
+       wake_lock_init(ctx->jack_wake_lock,
                       WAKE_LOCK_SUSPEND, "jack_detect");
 #endif
 
-       irq_mem = ipc_get_resource_byname(
-                               ipcdev, IORESOURCE_MEM, "IRQ_BASE");
+       irq_mem = ipc_get_resource_byname(ipcdev, IORESOURCE_MEM, "IRQ_BASE");
        if (!irq_mem) {
                pr_err("no mem resource given\n");
                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;
-               }
+       /*GPADC handle for audio_detection*/
+       ctx->audio_adc_handle =
+               intel_mid_gpadc_alloc(MFLD_AUDIO_SENSOR,
+                                     MFLD_AUDIO_DETECT_CODE);
+       if (!ctx->audio_adc_handle) {
+               pr_err("invalid ADC handle\n");
+               ret_val = -ENOMEM;
+               goto unalloc;
        }
+       INIT_DELAYED_WORK(&ctx->jack_work.work, mfld_jack_wq);
 
-       mc_drv_ctx->int_base = ioremap_nocache(irq_mem->start,
-                                       resource_size(irq_mem));
-       if (!mc_drv_ctx->int_base) {
+       ctx->int_base = ioremap_nocache(irq_mem->start, resource_size(irq_mem));
+       if (!ctx->int_base) {
                pr_err("Mapping of cache failed\n");
                ret_val = -ENOMEM;
                goto unalloc;
@@ -689,7 +675,7 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
        ret_val = request_threaded_irq(irq, snd_mfld_jack_intr_handler,
                        snd_mfld_codec_intr_detection,
                        IRQF_SHARED | IRQF_NO_SUSPEND,
-                       ipcdev->dev.driver->name, mc_drv_ctx);
+                       ipcdev->dev.driver->name, ctx);
        if (ret_val) {
                pr_err("cannot register IRQ\n");
                goto unalloc;
@@ -697,7 +683,7 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
        /* register the soc card */
        snd_soc_card_mfld.dev = &ipcdev->dev;
        snd_soc_card_mfld.dapm.stream_event = mfld_card_stream_event;
-       snd_soc_card_set_drvdata(&snd_soc_card_mfld, mc_drv_ctx);
+       snd_soc_card_set_drvdata(&snd_soc_card_mfld, ctx);
        ret_val = snd_soc_register_card(&snd_soc_card_mfld);
        if (ret_val) {
                pr_debug("snd_soc_register_card failed %d\n", ret_val);
@@ -708,26 +694,27 @@ static int __devinit snd_mfld_mc_probe(struct ipc_device *ipcdev)
        return ret_val;
 
 freeirq:
-       free_irq(irq, mc_drv_ctx);
+       free_irq(irq, ctx);
 unalloc:
-       kfree(mc_drv_ctx);
+       kfree(ctx);
        return ret_val;
 }
 
 static int __devexit snd_mfld_mc_remove(struct ipc_device *ipcdev)
 {
        struct snd_soc_card *soc_card = ipc_get_drvdata(ipcdev);
-       struct mfld_mc_private *mc_drv_ctx = snd_soc_card_get_drvdata(soc_card);
+       struct mfld_mc_private *ctx = snd_soc_card_get_drvdata(soc_card);
        pr_debug("snd_mfld_mc_remove called\n");
-       free_irq(ipc_get_irq(ipcdev, 0), mc_drv_ctx);
+       free_irq(ipc_get_irq(ipcdev, 0), ctx);
 #ifdef CONFIG_HAS_WAKELOCK
-       if (wake_lock_active(&mc_drv_ctx->wake_lock))
-               wake_unlock(&mc_drv_ctx->wake_lock);
-       wake_lock_destroy(&mc_drv_ctx->wake_lock);
+       if (wake_lock_active(ctx->jack_wake_lock))
+               wake_unlock(ctx->jack_wake_lock);
+       wake_lock_destroy(ctx->jack_wake_lock);
+       kfree(ctx->jack_wake_lock);
 #endif
-       kfree(mc_drv_ctx);
-       if (mfld_board_id() == MFLD_BID_PR3)
-               gpio_free(HEADSET_DET_PIN);
+       cancel_delayed_work(&ctx->jack_work.work);
+       intel_mid_gpadc_free(ctx->audio_adc_handle);
+       kfree(ctx);
        snd_soc_card_set_drvdata(soc_card, NULL);
        snd_soc_unregister_card(soc_card);
        ipc_set_drvdata(ipcdev, NULL);
@@ -759,7 +746,6 @@ late_initcall(snd_mfld_driver_init);
 static void __exit snd_mfld_driver_exit(void)
 {
        pr_debug("snd_mfld_driver_exit called\n");
-       async_synchronize_full_domain(&mfld_jack_async_list);
        ipc_driver_unregister(&snd_mfld_mc_driver);
 }
 module_exit(snd_mfld_driver_exit);
index b79309a..dca2284 100644 (file)
@@ -65,7 +65,7 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = {
        .fifo_size = SST_FIFO_SIZE,
 };
 
-#ifdef CONFIG_SND_CLV_MACHINE
+#if (defined(CONFIG_SND_CLV_MACHINE) || defined(CONFIG_SND_CLV_MACHINE_MODULE))
 static unsigned int    lpe_mixer_input_ihf;
 static unsigned int    lpe_mixer_input_hs;
 
@@ -595,7 +595,7 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
        return retval;
 }
 
-#ifdef CONFIG_SND_CLV_MACHINE
+#if (defined(CONFIG_SND_CLV_MACHINE) || defined(CONFIG_SND_CLV_MACHINE_MODULE))
 static int sst_soc_probe(struct snd_soc_platform *platform)
 {
        pr_debug("%s called\n", __func__);
@@ -622,7 +622,7 @@ static int sst_soc_probe(struct snd_soc_platform *platform)
 #endif
 
 static struct snd_soc_platform_driver sst_soc_platform_drv = {
-#ifdef CONFIG_SND_CLV_MACHINE
+#if (defined(CONFIG_SND_CLV_MACHINE) || defined(CONFIG_SND_CLV_MACHINE_MODULE))
        .probe          = sst_soc_probe,
 #endif
        .ops            = &sst_platform_ops,