summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/codecs/sn95031.c302
-rw-r--r--sound/soc/codecs/sn95031.h22
-rw-r--r--sound/soc/mid-x86/Makefile4
-rw-r--r--sound/soc/mid-x86/mfld_common.c258
-rw-r--r--sound/soc/mid-x86/mfld_common.h112
-rw-r--r--sound/soc/mid-x86/mfld_machine.c578
-rw-r--r--sound/soc/mid-x86/mfld_machine_gi.c474
-rw-r--r--sound/soc/mid-x86/sst_platform.c6
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,