diff options
author | Omair Mohammed Abdullah <omair.m.abdullah@linux.intel.com> | 2012-04-02 14:22:55 +0530 |
---|---|---|
committer | buildbot <buildbot@intel.com> | 2012-05-11 03:37:29 -0700 |
commit | 93c61dfe6fdface771b45a6ad8fbfb0772871689 (patch) | |
tree | 5e28c481420f112c357c61b939f20278a13ad835 /sound | |
parent | 3ebca466c76dc2b64d1df8519339d5f6c4127932 (diff) | |
download | kernel-mfld-blackbay-93c61dfe6fdface771b45a6ad8fbfb0772871689.tar.gz kernel-mfld-blackbay-93c61dfe6fdface771b45a6ad8fbfb0772871689.tar.bz2 kernel-mfld-blackbay-93c61dfe6fdface771b45a6ad8fbfb0772871689.zip |
audio: sn95031: cleanup jack detection code
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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/codecs/sn95031.c | 302 | ||||
-rw-r--r-- | sound/soc/codecs/sn95031.h | 22 | ||||
-rw-r--r-- | sound/soc/mid-x86/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/mid-x86/mfld_common.c | 258 | ||||
-rw-r--r-- | sound/soc/mid-x86/mfld_common.h | 112 | ||||
-rw-r--r-- | sound/soc/mid-x86/mfld_machine.c | 578 | ||||
-rw-r--r-- | sound/soc/mid-x86/mfld_machine_gi.c | 474 | ||||
-rw-r--r-- | sound/soc/mid-x86/sst_platform.c | 6 |
8 files changed, 877 insertions, 879 deletions
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c index 24a3fb1aaff..877c021cc3f 100644 --- a/sound/soc/codecs/sn95031.c +++ b/sound/soc/codecs/sn95031.c @@ -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> @@ -43,86 +43,13 @@ #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, diff --git a/sound/soc/codecs/sn95031.h b/sound/soc/codecs/sn95031.h index 8d3481e0836..c6d3c838939 100644 --- a/sound/soc/codecs/sn95031.h +++ b/sound/soc/codecs/sn95031.h @@ -121,13 +121,6 @@ #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 @@ -144,8 +137,8 @@ #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 diff --git a/sound/soc/mid-x86/Makefile b/sound/soc/mid-x86/Makefile index 313b1480d9c..a6425e0c639 100644 --- a/sound/soc/mid-x86/Makefile +++ b/sound/soc/mid-x86/Makefile @@ -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 index 00000000000..fae765fa89b --- /dev/null +++ b/sound/soc/mid-x86/mfld_common.c @@ -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 index 00000000000..f106cdec685 --- /dev/null +++ b/sound/soc/mid-x86/mfld_common.h @@ -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 diff --git a/sound/soc/mid-x86/mfld_machine.c b/sound/soc/mid-x86/mfld_machine.c index e974e860cb6..79eb59d5ec7 100644 --- a/sound/soc/mid-x86/mfld_machine.c +++ b/sound/soc/mid-x86/mfld_machine.c @@ -35,41 +35,17 @@ #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); diff --git a/sound/soc/mid-x86/mfld_machine_gi.c b/sound/soc/mid-x86/mfld_machine_gi.c index 8482c6a2b08..7d50cbb374c 100644 --- a/sound/soc/mid-x86/mfld_machine_gi.c +++ b/sound/soc/mid-x86/mfld_machine_gi.c @@ -33,44 +33,20 @@ #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); diff --git a/sound/soc/mid-x86/sst_platform.c b/sound/soc/mid-x86/sst_platform.c index b79309a25cb..dca22841644 100644 --- a/sound/soc/mid-x86/sst_platform.c +++ b/sound/soc/mid-x86/sst_platform.c @@ -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, |