Merge "msm: board-8064: Place the movable zone" into msm-3.0
diff --git a/arch/arm/mach-msm/board-copper.c b/arch/arm/mach-msm/board-copper.c
index a5016f5..b5ab440 100644
--- a/arch/arm/mach-msm/board-copper.c
+++ b/arch/arm/mach-msm/board-copper.c
@@ -353,12 +353,18 @@
return 0;
}
+static struct platform_device android_usb_device = {
+ .name = "android_usb",
+ .id = -1,
+};
+
void __init msm_copper_add_devices(void)
{
#ifdef CONFIG_ION_MSM
platform_device_register(&ion_dev);
#endif
platform_device_register(&msm_device_smd_copper);
+ platform_device_register(&android_usb_device);
platform_add_devices(msm_copper_stub_regulator_devices,
msm_copper_stub_regulator_devices_len);
}
diff --git a/arch/arm/mach-msm/mpm-8625.c b/arch/arm/mach-msm/mpm-8625.c
index 63de944..8ef7d61 100644
--- a/arch/arm/mach-msm/mpm-8625.c
+++ b/arch/arm/mach-msm/mpm-8625.c
@@ -352,6 +352,7 @@
*/
int msm_gic_irq_enter_sleep2(bool modem_wake, int from_idle)
{
+ int i;
if (from_idle && !modem_wake)
return 0;
@@ -370,11 +371,17 @@
}
if (modem_wake) {
- irq_set_irq_type(MSM8625_INT_A9_M2A_6, IRQF_TRIGGER_RISING);
/* save the contents of GIC CPU interface and Distributor */
msm_gic_save();
+ /* Disable all the Interrupts, if we enter from idle pc */
+ if (from_idle) {
+ for (i = 0; (i * 32) < max_irqs; i++)
+ writel_relaxed(0xffffffff, dist_base
+ + GIC_DIST_ENABLE_CLEAR + i * 4);
+ }
+ irq_set_irq_type(MSM8625_INT_A9_M2A_6, IRQF_TRIGGER_RISING);
enable_irq(MSM8625_INT_A9_M2A_6);
- pr_info("%s Good to go for sleep now\n", __func__);
+ pr_debug("%s going for sleep now\n", __func__);
}
return 0;
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
index c1febcf..ba3c1e8 100644
--- a/drivers/mfd/wcd9xxx-irq.c
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -100,7 +100,7 @@
}
EXPORT_SYMBOL_GPL(wcd9xxx_pm_cmpxchg);
-void wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx)
+bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx)
{
enum wcd9xxx_pm_state os;
@@ -111,10 +111,12 @@
* so need to embrace wlock_holders with mutex.
*/
mutex_lock(&wcd9xxx->pm_lock);
- if (wcd9xxx->wlock_holders++ == 0)
+ if (wcd9xxx->wlock_holders++ == 0) {
+ pr_debug("%s: holding wake lock\n", __func__);
wake_lock(&wcd9xxx->wlock);
+ }
mutex_unlock(&wcd9xxx->pm_lock);
- while (!wait_event_timeout(wcd9xxx->pm_wq,
+ if (!wait_event_timeout(wcd9xxx->pm_wq,
((os = wcd9xxx_pm_cmpxchg(wcd9xxx, WCD9XXX_PM_SLEEPABLE,
WCD9XXX_PM_AWAKE)) ==
WCD9XXX_PM_SLEEPABLE ||
@@ -123,9 +125,12 @@
pr_err("%s: system didn't resume within 5000ms, state %d, "
"wlock %d\n", __func__, wcd9xxx->pm_state,
wcd9xxx->wlock_holders);
- WARN_ON_ONCE(1);
+ WARN_ON(1);
+ wcd9xxx_unlock_sleep(wcd9xxx);
+ return false;
}
wake_up_all(&wcd9xxx->pm_wq);
+ return true;
}
EXPORT_SYMBOL_GPL(wcd9xxx_lock_sleep);
@@ -134,6 +139,7 @@
mutex_lock(&wcd9xxx->pm_lock);
if (--wcd9xxx->wlock_holders == 0) {
wcd9xxx->pm_state = WCD9XXX_PM_SLEEPABLE;
+ pr_debug("%s: releasing wake lock\n", __func__);
wake_unlock(&wcd9xxx->wlock);
}
mutex_unlock(&wcd9xxx->pm_lock);
@@ -166,7 +172,10 @@
u8 status[WCD9XXX_NUM_IRQ_REGS];
int i;
- wcd9xxx_lock_sleep(wcd9xxx);
+ if (unlikely(wcd9xxx_lock_sleep(wcd9xxx) == false)) {
+ dev_err(wcd9xxx->dev, "Failed to hold suspend\n");
+ return IRQ_NONE;
+ }
ret = wcd9xxx_bulk_read(wcd9xxx, TABLA_A_INTR_STATUS0,
WCD9XXX_NUM_IRQ_REGS, status);
if (ret < 0) {
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 362bdbe..7d34bfd 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -122,17 +122,21 @@
if (mrq->data) {
#ifdef CONFIG_MMC_PERF_PROFILING
- diff = ktime_sub(ktime_get(), host->perf.start);
- if (mrq->data->flags == MMC_DATA_READ) {
- host->perf.rbytes_drv +=
+ if (host->perf_enable) {
+ diff = ktime_sub(ktime_get(), host->perf.start);
+ if (mrq->data->flags == MMC_DATA_READ) {
+ host->perf.rbytes_drv +=
+ mrq->data->bytes_xfered;
+ host->perf.rtime_drv =
+ ktime_add(host->perf.rtime_drv,
+ diff);
+ } else {
+ host->perf.wbytes_drv +=
mrq->data->bytes_xfered;
- host->perf.rtime_drv =
- ktime_add(host->perf.rtime_drv, diff);
- } else {
- host->perf.wbytes_drv +=
- mrq->data->bytes_xfered;
- host->perf.wtime_drv =
- ktime_add(host->perf.wtime_drv, diff);
+ host->perf.wtime_drv =
+ ktime_add(host->perf.wtime_drv,
+ diff);
+ }
}
#endif
pr_debug("%s: %d bytes transferred: %d\n",
@@ -210,7 +214,8 @@
mrq->stop->mrq = mrq;
}
#ifdef CONFIG_MMC_PERF_PROFILING
- host->perf.start = ktime_get();
+ if (host->perf_enable)
+ host->perf.start = ktime_get();
#endif
}
mmc_host_clk_hold(host);
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 1985745..a162586 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -399,18 +399,23 @@
{
int64_t value;
struct mmc_host *host = dev_get_drvdata(dev);
+
sscanf(buf, "%lld", &value);
+ spin_lock(&host->lock);
if (!value) {
- spin_lock(&host->lock);
memset(&host->perf, 0, sizeof(host->perf));
- spin_unlock(&host->lock);
+ host->perf_enable = false;
+ } else {
+ host->perf_enable = true;
}
+ spin_unlock(&host->lock);
return count;
}
static DEVICE_ATTR(perf, S_IRUGO | S_IWUSR,
show_perf, set_perf);
+
#endif
static struct attribute *dev_attrs[] = {
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index e91cf3e..69ec647 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -1184,7 +1184,9 @@
{
if (status & MCI_DATACRCFAIL) {
if (!(data->mrq->cmd->opcode == MMC_BUS_TEST_W
- || data->mrq->cmd->opcode == MMC_BUS_TEST_R)) {
+ || data->mrq->cmd->opcode == MMC_BUS_TEST_R
+ || data->mrq->cmd->opcode ==
+ MMC_SEND_TUNING_BLOCK_HS200)) {
pr_err("%s: Data CRC error\n",
mmc_hostname(host->mmc));
pr_err("%s: opcode 0x%.8x\n", __func__,
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index eb727b8..9f7affc 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -40,7 +40,7 @@
case CI13XXX_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
- writel(0, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
break;
default:
dev_dbg(dev, "unknown ci13xxx_udc event\n");
diff --git a/drivers/usb/gadget/ci13xxx_msm_hsic.c b/drivers/usb/gadget/ci13xxx_msm_hsic.c
index 3736367..95a70d5 100644
--- a/drivers/usb/gadget/ci13xxx_msm_hsic.c
+++ b/drivers/usb/gadget/ci13xxx_msm_hsic.c
@@ -570,7 +570,7 @@
case CI13XXX_CONTROLLER_RESET_EVENT:
dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
writel_relaxed(0, USB_AHBBURST);
- writel_relaxed(0, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
break;
case CI13XXX_CONTROLLER_CONNECT_EVENT:
dev_dbg(dev, "CI13XXX_CONTROLLER_CONNECT_EVENT received\n");
diff --git a/drivers/usb/host/ehci-msm-hsic.c b/drivers/usb/host/ehci-msm-hsic.c
index 9d1cef7..347711b 100644
--- a/drivers/usb/host/ehci-msm-hsic.c
+++ b/drivers/usb/host/ehci-msm-hsic.c
@@ -596,7 +596,7 @@
/* bursts of unspecified length. */
writel_relaxed(0, USB_AHBBURST);
/* Use the AHB transactor */
- writel_relaxed(0, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
/* Disable streaming mode and select host mode */
writel_relaxed(0x13, USB_USBMODE);
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 5649fd0..6a86632 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -1,6 +1,6 @@
/* ehci-msm.c - HSUSB Host Controller Driver Implementation
*
- * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
*
* Partly derived from ehci-fsl.c and ehci-hcd.c
* Copyright (c) 2000-2004 by David Brownell
@@ -67,7 +67,7 @@
/* bursts of unspecified length. */
writel(0, USB_AHBBURST);
/* Use the AHB transactor */
- writel(0, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
/* Disable streaming mode and select host mode */
writel(0x13, USB_USBMODE);
diff --git a/drivers/usb/host/ehci-msm2.c b/drivers/usb/host/ehci-msm2.c
index fc43a0d..17fa7b1 100644
--- a/drivers/usb/host/ehci-msm2.c
+++ b/drivers/usb/host/ehci-msm2.c
@@ -515,7 +515,7 @@
msleep(100);
writel_relaxed(0x0, USB_AHBBURST);
- writel_relaxed(0x00, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
/* Ensure that RESET operation is completed before turning off clock */
mb();
@@ -703,7 +703,7 @@
/* bursts of unspecified length. */
writel_relaxed(0, USB_AHBBURST);
/* Use the AHB transactor */
- writel_relaxed(0, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
/* Disable streaming mode and select host mode */
writel_relaxed(0x13, USB_USBMODE);
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index e596b3f..bf3c6d5 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -515,7 +515,7 @@
/* select ULPI phy */
writel_relaxed(0x80000000, USB_PORTSC);
writel_relaxed(0x0, USB_AHBBURST);
- writel_relaxed(0x00, USB_AHBMODE);
+ writel_relaxed(0x08, USB_AHBMODE);
return 0;
}
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
index 1a04773..fca9a94 100644
--- a/include/linux/mfd/wcd9xxx/core.h
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -119,6 +119,7 @@
wait_queue_head_t pm_wq;
struct wake_lock wlock;
int wlock_holders;
+
int num_rx_port;
int num_tx_port;
};
@@ -138,7 +139,7 @@
int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la);
int wcd9xxx_get_intf_type(void);
-void wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx);
+bool wcd9xxx_lock_sleep(struct wcd9xxx *wcd9xxx);
void wcd9xxx_unlock_sleep(struct wcd9xxx *wcd9xxx);
enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(struct wcd9xxx *wcd9xxx,
enum wcd9xxx_pm_state o,
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 46f71a1..e19225e 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -352,6 +352,7 @@
ktime_t wtime_drv; /* Wr time MMC Host */
ktime_t start;
} perf;
+ bool perf_enable;
#endif
struct mmc_async_req *areq; /* active async req */
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 851722a..2c43792 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -30,6 +30,8 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
#include "wcd9310.h"
#define WCD9310_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
@@ -49,6 +51,8 @@
};
#define MBHC_NUM_DCE_PLUG_DETECT 3
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
#define TABLA_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR)
@@ -77,14 +81,24 @@
#define TABLA_MBHC_BUTTON_MIN 0x8000
-#define PLUG_TYPE_HEADPHONE (1 << 0)
-#define PLUG_TYPE_HEADSET (1 << 1)
-
#define TABLA_MBHC_FAKE_INSERT_LOW 10
-#define TABLA_MBHC_FAKE_INSERT_HIGH 150
+#define TABLA_MBHC_FAKE_INSERT_HIGH 80
+#define TABLA_MBHC_FAKE_INS_HIGH_NO_GPIO 150
#define TABLA_MBHC_STATUS_REL_DETECTION 0x0C
+#define TABLA_MBHC_GPIO_REL_DEBOUNCE_TIME_MS 200
+
+#define TABLA_MBHC_FAKE_INSERT_VOLT_DELTA_MV 200
+
+#define TABLA_HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define TABLA_HS_DETECT_PLUG_INERVAL_MS 100
+
+#define TABLA_GPIO_IRQ_DEBOUNCE_TIME_US 5000
+
+#define TABLA_ACQUIRE_LOCK(x) do { mutex_lock(&x); } while (0)
+#define TABLA_RELEASE_LOCK(x) do { mutex_unlock(&x); } while (0)
+
static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -181,10 +195,23 @@
u16 micb_4_mbhc;
};
+enum tabla_mbhc_plug_type {
+ PLUG_TYPE_NONE = 0,
+ PLUG_TYPE_HEADSET,
+ PLUG_TYPE_HEADPHONE,
+ PLUG_TYPE_HIGH_HPH,
+};
+
+enum tabla_mbhc_state {
+ MBHC_STATE_NONE = -1,
+ MBHC_STATE_POTENTIAL,
+ MBHC_STATE_POTENTIAL_RECOVERY,
+ MBHC_STATE_RELEASE,
+};
+
struct tabla_priv {
struct snd_soc_codec *codec;
struct tabla_reg_address reg_addr;
- u32 mclk_freq;
u32 adc_count;
u32 cfilt1_cnt;
u32 cfilt2_cnt;
@@ -197,23 +224,10 @@
bool mbhc_polling_active;
unsigned long mbhc_fake_ins_start;
int buttons_pressed;
- int mbhc_state;
-
- enum tabla_micbias_num micbias;
- /* void* calibration contains:
- * struct tabla_mbhc_general_cfg generic;
- * struct tabla_mbhc_plug_detect_cfg plug_det;
- * struct tabla_mbhc_plug_type_cfg plug_type;
- * struct tabla_mbhc_btn_detect_cfg btn_det;
- * struct tabla_mbhc_imped_detect_cfg imped_det;
- * Note: various size depends on btn_det->num_btn
- */
- void *calibration;
+ enum tabla_mbhc_state mbhc_state;
+ struct tabla_mbhc_config mbhc_cfg;
struct mbhc_internal_cal_data mbhc_data;
- struct snd_soc_jack *headset_jack;
- struct snd_soc_jack *button_jack;
-
struct wcd9xxx_pdata *pdata;
u32 anc_slot;
@@ -242,9 +256,6 @@
u8 hphlocp_cnt; /* headphone left ocp retry */
u8 hphrocp_cnt; /* headphone right ocp retry */
- /* Callback function to enable MCLK */
- int (*mclk_cb) (struct snd_soc_codec*, int);
-
/* Work to perform MBHC Firmware Read */
struct delayed_work mbhc_firmware_dwork;
const struct firmware *mbhc_fw;
@@ -261,9 +272,26 @@
u8 aux_l_gain;
u8 aux_r_gain;
- struct mutex mbhc_mutex;
struct delayed_work mbhc_insert_dwork;
unsigned long mbhc_last_resume; /* in jiffies */
+
+ u8 current_plug;
+ struct work_struct hs_correct_plug_work;
+ bool hs_detect_work_stop;
+ bool hs_polling_irq_prepared;
+ bool lpi_enabled; /* low power insertion detection */
+ bool in_gpio_handler;
+ /* Currently, only used for mbhc purpose, to protect
+ * concurrent execution of mbhc threaded irq handlers and
+ * kill race between DAPM and MBHC.But can serve as a
+ * general lock to protect codec resource
+ */
+ struct mutex codec_resource_lock;
+
+ /* Used to override the rule "invalid headset
+ * when microphone voltage is too high"
+ */
+ bool mbhc_inval_hs_range_override;
};
#ifdef CONFIG_DEBUG_FS
@@ -1406,7 +1434,7 @@
SOC_DAPM_SINGLE("Switch", TABLA_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
static void tabla_codec_enable_adc_block(struct snd_soc_codec *codec,
- int enable)
+ int enable)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
@@ -1414,19 +1442,12 @@
if (enable) {
tabla->adc_count++;
- snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0xE0);
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL, 0x2, 0x2);
} else {
tabla->adc_count--;
- if (!tabla->adc_count) {
+ if (!tabla->adc_count)
snd_soc_update_bits(codec, TABLA_A_CDC_CLK_OTHR_CTL,
0x2, 0x0);
- mutex_lock(&tabla->mbhc_mutex);
- if (!tabla->mbhc_polling_active)
- snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS,
- 0xE0, 0x0);
- mutex_unlock(&tabla->mbhc_mutex);
- }
}
}
@@ -1549,9 +1570,9 @@
static int tabla_codec_mclk_index(const struct tabla_priv *tabla)
{
- if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ)
+ if (tabla->mbhc_cfg.mclk_rate == TABLA_MCLK_RATE_12288KHZ)
return 0;
- else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ)
+ else if (tabla->mbhc_cfg.mclk_rate == TABLA_MCLK_RATE_9600KHZ)
return 1;
else {
BUG_ON(1);
@@ -1941,14 +1962,7 @@
return 0;
}
-
-static void tabla_codec_disable_button_presses(struct snd_soc_codec *codec)
-{
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0x80);
- snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL, 0x00);
-}
-
-/* called after acquiring mbhc_mutex */
+/* called under codec_resource_lock acquisition */
static void tabla_codec_start_hs_polling(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
@@ -1956,21 +1970,16 @@
int mbhc_state = tabla->mbhc_state;
pr_debug("%s: enter\n", __func__);
- snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
- wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
- if (!tabla->no_mic_headset_override) {
- wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_MBHC_POTENTIAL);
- wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_MBHC_RELEASE);
- } else {
- tabla_codec_disable_button_presses(codec);
+ if (!tabla->mbhc_polling_active) {
+ pr_debug("Polling is not active, do not start polling\n");
+ return;
}
+ snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
if (!tabla->no_mic_headset_override) {
- if (mbhc_state == TABLA_IRQ_MBHC_POTENTIAL) {
- pr_debug("%s:%d recovering MBHC state macine\n",
- __func__, __LINE__);
+ if (mbhc_state == MBHC_STATE_POTENTIAL) {
+ pr_debug("%s recovering MBHC state macine\n", __func__);
+ tabla->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
/* set to max button press threshold */
snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL,
0x7F);
@@ -1995,20 +2004,18 @@
pr_debug("%s: leave\n", __func__);
}
-/* called after acquiring mbhc_mutex */
+/* called under codec_resource_lock acquisition */
static void tabla_codec_pause_hs_polling(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
pr_debug("%s: enter\n", __func__);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
- if (!tabla->no_mic_headset_override) {
- wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_MBHC_POTENTIAL);
- wcd9xxx_disable_irq(codec->control_data,
- TABLA_IRQ_MBHC_RELEASE);
+ if (!tabla->mbhc_polling_active) {
+ pr_debug("polling not active, nothing to pause\n");
+ return;
}
+
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
pr_debug("%s: leave\n", __func__);
}
@@ -2027,7 +2034,7 @@
tabla->mbhc_bias_regs.cfilt_ctl) & 0x40;
if (cur_mode_val != reg_mode_val) {
- mutex_lock(&tabla->mbhc_mutex);
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
if (tabla->mbhc_polling_active) {
tabla_codec_pause_hs_polling(codec);
mbhc_was_polling = true;
@@ -2036,7 +2043,7 @@
tabla->mbhc_bias_regs.cfilt_ctl, 0x40, reg_mode_val);
if (mbhc_was_polling)
tabla_codec_start_hs_polling(codec);
- mutex_unlock(&tabla->mbhc_mutex);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
cur_mode_val, reg_mode_val);
} else {
@@ -2152,14 +2159,14 @@
return (hph_reg_val & 0xC0) ? true : false;
}
+/* called under codec_resource_lock acquisition */
static void tabla_codec_switch_micbias(struct snd_soc_codec *codec,
- int vddio_switch)
+ int vddio_switch)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
int cfilt_k_val;
bool mbhc_was_polling = false;
- mutex_lock(&tabla->mbhc_mutex);
switch (vddio_switch) {
case 1:
if (tabla->mbhc_micbias_switched == 0 &&
@@ -2209,7 +2216,6 @@
}
break;
}
- mutex_unlock(&tabla->mbhc_mutex);
}
static int tabla_codec_enable_micbias(struct snd_soc_dapm_widget *w,
@@ -2255,8 +2261,11 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/* Decide whether to switch the micbias for MBHC */
- if (w->reg == tabla->mbhc_bias_regs.ctl_reg)
+ if (w->reg == tabla->mbhc_bias_regs.ctl_reg) {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_switch_micbias(codec, 0);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ }
snd_soc_update_bits(codec, w->reg, 0x0E, 0x0A);
tabla_codec_update_cfilt_usage(codec, cfilt_sel_val, 1);
@@ -2271,19 +2280,21 @@
break;
case SND_SOC_DAPM_POST_PMU:
if (tabla->mbhc_polling_active &&
- tabla->micbias == micb_line) {
- mutex_lock(&tabla->mbhc_mutex);
+ tabla->mbhc_cfg.micbias == micb_line) {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_pause_hs_polling(codec);
tabla_codec_start_hs_polling(codec);
- mutex_unlock(&tabla->mbhc_mutex);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
}
break;
case SND_SOC_DAPM_POST_PMD:
-
if ((w->reg == tabla->mbhc_bias_regs.ctl_reg) &&
- tabla_is_hph_pa_on(codec))
+ tabla_is_hph_pa_on(codec)) {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_switch_micbias(codec, 1);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ }
if (strnstr(w->name, internal1_text, 30))
snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
@@ -2408,12 +2419,13 @@
return;
}
- pr_info("%s: clear ocp status %x\n", __func__, jack_status);
+ pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
codec = tabla->codec;
if (tabla->hph_status & jack_status) {
tabla->hph_status &= ~jack_status;
- if (tabla->headset_jack)
- tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
+ if (tabla->mbhc_cfg.headset_jack)
+ tabla_snd_soc_jack_report(tabla,
+ tabla->mbhc_cfg.headset_jack,
tabla->hph_status,
TABLA_JACK_MASK);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x00);
@@ -2456,9 +2468,11 @@
mbhc_micb_ctl_val = snd_soc_read(codec,
tabla->mbhc_bias_regs.ctl_reg);
- if (!(mbhc_micb_ctl_val & 0x80))
+ if (!(mbhc_micb_ctl_val & 0x80)) {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_switch_micbias(codec, 1);
-
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ }
break;
case SND_SOC_DAPM_POST_PMD:
@@ -2483,12 +2497,13 @@
schedule_work(&tabla->hphrocp_work);
}
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_switch_micbias(codec, 0);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
pr_debug("%s: sleep 10 ms after %s PA disable.\n", __func__,
w->name);
usleep_range(10000, 10000);
-
break;
}
return 0;
@@ -2500,7 +2515,7 @@
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
unsigned int cfilt;
- switch (tabla->micbias) {
+ switch (tabla->mbhc_cfg.micbias) {
case TABLA_MICBIAS1:
cfilt = tabla->pdata->micbias.bias1_cfilt_sel;
micbias_regs->mbhc_reg = TABLA_A_MICB_1_MBHC;
@@ -3065,7 +3080,7 @@
u8 *n_ready, *n_cic;
struct tabla_mbhc_btn_detect_cfg *btn_det;
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->mbhc_cfg.calibration);
snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL,
tabla->mbhc_data.v_ins_hu & 0xFF);
@@ -3132,12 +3147,14 @@
}
}
-int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable)
+int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
pr_debug("%s() mclk_enable = %u\n", __func__, mclk_enable);
+ if (dapm)
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
if (mclk_enable) {
tabla->mclk_enabled = true;
@@ -3156,6 +3173,8 @@
} else {
if (!tabla->mclk_enabled) {
+ if (dapm)
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
pr_err("Error, MCLK already diabled\n");
return -EINVAL;
}
@@ -3179,6 +3198,8 @@
TABLA_BANDGAP_OFF);
}
}
+ if (dapm)
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
return 0;
}
@@ -4019,13 +4040,24 @@
return bias_value;
}
-static short tabla_codec_sta_dce(struct snd_soc_codec *codec, int dce)
+static void tabla_turn_onoff_rel_detection(struct snd_soc_codec *codec, bool on)
{
- struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __tabla_codec_sta_dce(struct snd_soc_codec *codec, int dce,
+ bool override_bypass, bool noreldetection)
+{
short bias_value;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ if (noreldetection)
+ tabla_turn_onoff_rel_detection(codec, false);
/* Turn on the override */
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ if (!override_bypass)
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
if (dce) {
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x4);
@@ -4050,36 +4082,42 @@
snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x0);
}
/* Turn off the override after measuring mic voltage */
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+ if (!override_bypass)
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+
+ if (noreldetection)
+ tabla_turn_onoff_rel_detection(codec, true);
+ wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
return bias_value;
}
+static short tabla_codec_sta_dce(struct snd_soc_codec *codec, int dce,
+ bool norel)
+{
+ return __tabla_codec_sta_dce(codec, dce, false, norel);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
static short tabla_codec_setup_hs_polling(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
short bias_value;
u8 cfilt_mode;
- if (!tabla->calibration) {
+ if (!tabla->mbhc_cfg.calibration) {
pr_err("Error, no tabla calibration\n");
return -ENODEV;
}
- mutex_lock(&tabla->mbhc_mutex);
- tabla->mbhc_polling_active = true;
-
if (!tabla->mclk_enabled) {
tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_MBHC_MODE);
tabla_enable_rx_bias(codec, 1);
tabla_codec_enable_clock_block(codec, 1);
}
- mutex_unlock(&tabla->mbhc_mutex);
snd_soc_update_bits(codec, TABLA_A_CLK_BUFF_EN1, 0x05, 0x01);
- snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0xE0);
-
/* Make sure CFILT is in fast mode, save current mode */
cfilt_mode = snd_soc_read(codec, tabla->mbhc_bias_regs.cfilt_ctl);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x70, 0x00);
@@ -4102,9 +4140,8 @@
tabla_codec_calibrate_hs_polling(codec);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x00);
- bias_value = tabla_codec_sta_dce(codec, 1);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+ /* don't flip override */
+ bias_value = __tabla_codec_sta_dce(codec, 1, true, true);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x40,
cfilt_mode);
snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
@@ -4112,10 +4149,28 @@
return bias_value;
}
-/* called after acquiring codec mutex */
-static void tabla_set_pa_dac_state(struct snd_soc_codec *codec)
+static int tabla_cancel_btn_work(struct tabla_priv *tabla)
+{
+ int r = 0;
+ struct wcd9xxx *core = dev_get_drvdata(tabla->codec->dev->parent);
+
+ if (cancel_delayed_work_sync(&tabla->mbhc_btn_dwork)) {
+ /* if scheduled mbhc_btn_dwork is canceled from here,
+ * we have to unlock from here instead btn_work */
+ wcd9xxx_unlock_sleep(core);
+ r = 1;
+ }
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+void tabla_set_and_turnoff_hph_padac(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ u8 wg_time;
+
+ wg_time = snd_soc_read(codec, TABLA_A_RX_HPH_CNP_WG_TIME) ;
+ wg_time += 1;
/* If headphone PA is on, check if userspace receives
* removal event to sync-up PA's state */
@@ -4131,6 +4186,118 @@
set_bit(TABLA_HPHL_DAC_OFF_ACK, &tabla->hph_pa_dac_state);
if (tabla_is_hph_dac_on(codec, 0))
set_bit(TABLA_HPHR_DAC_OFF_ACK, &tabla->hph_pa_dac_state);
+
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_CNP_EN, 0x30, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_DAC_CTL,
+ 0xC0, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_DAC_CTL,
+ 0xC0, 0x00);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+}
+
+static void tabla_clr_and_turnon_hph_padac(struct tabla_priv *tabla)
+{
+ bool pa_turned_on = false;
+ struct snd_soc_codec *codec = tabla->codec;
+ u8 wg_time;
+
+ wg_time = snd_soc_read(codec, TABLA_A_RX_HPH_CNP_WG_TIME) ;
+ wg_time += 1;
+
+ if (test_and_clear_bit(TABLA_HPHR_DAC_OFF_ACK,
+ &tabla->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_R_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+ if (test_and_clear_bit(TABLA_HPHL_DAC_OFF_ACK,
+ &tabla->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+ snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_L_DAC_CTL,
+ 0xC0, 0xC0);
+ }
+
+ if (test_and_clear_bit(TABLA_HPHR_PA_OFF_ACK,
+ &tabla->hph_pa_dac_state)) {
+ pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_CNP_EN, 0x10,
+ 1 << 4);
+ pa_turned_on = true;
+ }
+ if (test_and_clear_bit(TABLA_HPHL_PA_OFF_ACK,
+ &tabla->hph_pa_dac_state)) {
+ pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+ snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_CNP_EN, 0x20,
+ 1 << 5);
+ pa_turned_on = true;
+ }
+
+ if (pa_turned_on) {
+ pr_debug("%s: PA was turned off by MBHC and not by DAPM\n",
+ __func__);
+ usleep_range(wg_time * 1000, wg_time * 1000);
+ }
+}
+
+/* called under codec_resource_lock acquisition */
+static void tabla_codec_report_plug(struct snd_soc_codec *codec, int insertion,
+ enum snd_jack_types jack_type)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+
+ if (!insertion) {
+ /* Report removal */
+ tabla->hph_status &= ~jack_type;
+ if (tabla->mbhc_cfg.headset_jack) {
+ /* cancel possibly scheduled btn work and
+ * report release if we reported button press */
+ if (tabla_cancel_btn_work(tabla)) {
+ pr_debug("%s: button press is canceled\n",
+ __func__);
+ } else if (tabla->buttons_pressed) {
+ pr_debug("%s: Reporting release for reported "
+ "button press %d\n", __func__,
+ jack_type);
+ tabla_snd_soc_jack_report(tabla,
+ tabla->mbhc_cfg.button_jack, 0,
+ tabla->buttons_pressed);
+ tabla->buttons_pressed &=
+ ~TABLA_JACK_BUTTON_MASK;
+ }
+ pr_debug("%s: Reporting removal %d\n", __func__,
+ jack_type);
+ tabla_snd_soc_jack_report(tabla,
+ tabla->mbhc_cfg.headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
+ }
+ tabla_set_and_turnoff_hph_padac(codec);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHR,
+ TABLA_IRQ_HPH_PA_OCPR_FAULT);
+ hphocp_off_report(tabla, SND_JACK_OC_HPHL,
+ TABLA_IRQ_HPH_PA_OCPL_FAULT);
+ tabla->current_plug = PLUG_TYPE_NONE;
+ tabla->mbhc_polling_active = false;
+ } else {
+ /* Report insertion */
+ tabla->hph_status |= jack_type;
+
+ if (jack_type == SND_JACK_HEADPHONE)
+ tabla->current_plug = PLUG_TYPE_HEADPHONE;
+ else if (jack_type == SND_JACK_HEADSET) {
+ tabla->mbhc_polling_active = true;
+ tabla->current_plug = PLUG_TYPE_HEADSET;
+ }
+ if (tabla->mbhc_cfg.headset_jack) {
+ pr_debug("%s: Reporting insertion %d\n", __func__,
+ jack_type);
+ tabla_snd_soc_jack_report(tabla,
+ tabla->mbhc_cfg.headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
+ }
+ tabla_clr_and_turnon_hph_padac(tabla);
+ }
}
static int tabla_codec_enable_hs_detect(struct snd_soc_codec *codec,
@@ -4140,12 +4307,11 @@
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
int central_bias_enabled = 0;
const struct tabla_mbhc_general_cfg *generic =
- TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
+ TABLA_MBHC_CAL_GENERAL_PTR(tabla->mbhc_cfg.calibration);
const struct tabla_mbhc_plug_detect_cfg *plug_det =
- TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->calibration);
- u8 wg_time;
+ TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->mbhc_cfg.calibration);
- if (!tabla->calibration) {
+ if (!tabla->mbhc_cfg.calibration) {
pr_err("Error, no tabla calibration\n");
return -EINVAL;
}
@@ -4159,23 +4325,11 @@
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
if (insertion) {
-
- wg_time = snd_soc_read(codec, TABLA_A_RX_HPH_CNP_WG_TIME) ;
- wg_time += 1;
tabla_codec_switch_micbias(codec, 0);
/* DAPM can manipulate PA/DAC bits concurrently */
if (padac_off == true) {
- mutex_lock(&codec->mutex);
- tabla_set_pa_dac_state(codec);
- snd_soc_update_bits(codec, TABLA_A_RX_HPH_CNP_EN, 0x30,
- 0x00);
- snd_soc_update_bits(codec, TABLA_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0x00);
- snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0x00);
- usleep_range(wg_time * 1000, wg_time * 1000);
- mutex_unlock(&codec->mutex);
+ tabla_set_and_turnoff_hph_padac(codec);
}
if (trigger == MBHC_USE_HPHL_TRIGGER) {
@@ -4227,7 +4381,7 @@
}
if (snd_soc_read(codec, TABLA_A_CDC_MBHC_B1_CTL) & 0x4) {
- mutex_lock(&tabla->mbhc_mutex);
+ /* called called by interrupt */
if (!(tabla->clock_active)) {
tabla_codec_enable_config_mode(codec, 1);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
@@ -4238,7 +4392,6 @@
} else
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
0x06, 0);
- mutex_unlock(&tabla->mbhc_mutex);
}
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.int_rbias, 0x80, 0);
@@ -4263,7 +4416,7 @@
}
snd_soc_update_bits(codec, tabla->reg_addr.micb_4_mbhc, 0x3,
- tabla->micbias);
+ tabla->mbhc_cfg.micbias);
wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
@@ -4336,7 +4489,7 @@
core = dev_get_drvdata(tabla->codec->dev->parent);
if (tabla) {
- if (tabla->button_jack) {
+ if (tabla->mbhc_cfg.button_jack) {
bias_value = tabla_codec_read_sta_result(tabla->codec);
sta_mv = tabla_codec_sta_dce_v(tabla->codec, 0,
bias_value);
@@ -4346,7 +4499,8 @@
pr_debug("%s: Reporting long button press event"
" STA: %d, DCE: %d\n", __func__,
sta_mv, dce_mv);
- tabla_snd_soc_jack_report(tabla, tabla->button_jack,
+ tabla_snd_soc_jack_report(tabla,
+ tabla->mbhc_cfg.button_jack,
tabla->buttons_pressed,
tabla->buttons_pressed);
}
@@ -4354,6 +4508,7 @@
pr_err("%s: Bad tabla private data\n", __func__);
}
+ pr_debug("%s: leave\n", __func__);
wcd9xxx_unlock_sleep(core);
}
@@ -4366,19 +4521,24 @@
u32 mclk_rate;
u32 dce_wait, sta_wait;
u8 *n_cic;
+ void *calibration;
tabla = snd_soc_codec_get_drvdata(codec);
+ calibration = tabla->mbhc_cfg.calibration;
+
+ wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ tabla_turn_onoff_rel_detection(codec, false);
/* First compute the DCE / STA wait times
* depending on tunable parameters.
* The value is computed in microseconds
*/
- btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(calibration);
n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
ncic = n_cic[tabla_codec_mclk_index(tabla)];
- nmeas = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration)->n_meas;
- navg = TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration)->mbhc_navg;
- mclk_rate = tabla->mclk_freq;
+ nmeas = TABLA_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+ navg = TABLA_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+ mclk_rate = tabla->mbhc_cfg.mclk_rate;
dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (mclk_rate / 1000);
sta_wait = (1000 * 128 * (navg + 1)) / (mclk_rate / 1000);
@@ -4398,7 +4558,7 @@
* to perform ADC calibration
*/
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x60,
- tabla->micbias << 5);
+ tabla->mbhc_cfg.micbias << 5);
snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x60, 0x60);
snd_soc_write(codec, TABLA_A_TX_7_MBHC_TEST_CTL, 0x78);
@@ -4448,6 +4608,9 @@
snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
usleep_range(100, 100);
+
+ wcd9xxx_enable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ tabla_turn_onoff_rel_detection(codec, true);
}
void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg* btn_det,
@@ -4485,19 +4648,20 @@
int i;
tabla = snd_soc_codec_get_drvdata(codec);
- btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
- plug_type = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->calibration);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->mbhc_cfg.calibration);
+ plug_type = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
n_ready = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_READY);
- if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ) {
+ if (tabla->mbhc_cfg.mclk_rate == TABLA_MCLK_RATE_12288KHZ) {
tabla->mbhc_data.npoll = 4;
tabla->mbhc_data.nbounce_wait = 30;
- } else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ) {
+ } else if (tabla->mbhc_cfg.mclk_rate == TABLA_MCLK_RATE_9600KHZ) {
tabla->mbhc_data.npoll = 7;
tabla->mbhc_data.nbounce_wait = 23;
}
- tabla->mbhc_data.t_sta_dce = ((1000 * 256) / (tabla->mclk_freq / 1000) *
+ tabla->mbhc_data.t_sta_dce = ((1000 * 256) /
+ (tabla->mbhc_cfg.mclk_rate / 1000) *
n_ready[tabla_codec_mclk_index(tabla)]) +
10;
tabla->mbhc_data.v_ins_hu =
@@ -4537,8 +4701,8 @@
struct wcd9xxx *tabla_core = dev_get_drvdata(codec->dev->parent);
tabla = snd_soc_codec_get_drvdata(codec);
- generic = TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
- btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
+ generic = TABLA_MBHC_CAL_GENERAL_PTR(tabla->mbhc_cfg.calibration);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->mbhc_cfg.calibration);
for (n = 0; n < 8; n++) {
if ((!TABLA_IS_1_X(tabla_core->version)) || n != 7) {
@@ -4613,60 +4777,6 @@
return true;
}
-static void mbhc_fw_read(struct work_struct *work)
-{
- struct delayed_work *dwork;
- struct tabla_priv *tabla;
- struct snd_soc_codec *codec;
- const struct firmware *fw;
- int ret = -1, retry = 0, rc;
-
- dwork = to_delayed_work(work);
- tabla = container_of(dwork, struct tabla_priv,
- mbhc_firmware_dwork);
- codec = tabla->codec;
-
- while (retry < MBHC_FW_READ_ATTEMPTS) {
- retry++;
- pr_info("%s:Attempt %d to request MBHC firmware\n",
- __func__, retry);
- ret = request_firmware(&fw, "wcd9310/wcd9310_mbhc.bin",
- codec->dev);
-
- if (ret != 0) {
- usleep_range(MBHC_FW_READ_TIMEOUT,
- MBHC_FW_READ_TIMEOUT);
- } else {
- pr_info("%s: MBHC Firmware read succesful\n", __func__);
- break;
- }
- }
-
- if (ret != 0) {
- pr_err("%s: Cannot load MBHC firmware use default cal\n",
- __func__);
- } else if (tabla_mbhc_fw_validate(fw) == false) {
- pr_err("%s: Invalid MBHC cal data size use default cal\n",
- __func__);
- release_firmware(fw);
- } else {
- tabla->calibration = (void *)fw->data;
- tabla->mbhc_fw = fw;
- }
-
- tabla->mclk_cb(codec, 1);
- tabla_mbhc_init(codec);
- tabla_mbhc_cal(codec);
- tabla_mbhc_calc_thres(codec);
- tabla->mclk_cb(codec, 0);
- tabla_codec_calibrate_hs_polling(codec);
- rc = tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER, false);
-
- if (IS_ERR_VALUE(rc))
- pr_err("%s: Failed to setup MBHC detection\n", __func__);
-
-}
-
static int tabla_determine_button(const struct tabla_priv *priv,
const s32 bias_mv)
{
@@ -4674,7 +4784,7 @@
struct tabla_mbhc_btn_detect_cfg *btn_det;
int i, btn = -1;
- btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(priv->calibration);
+ btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
v_btn_low = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_V_BTN_LOW);
v_btn_high = tabla_mbhc_cal_btn_det_mp(btn_det,
TABLA_BTN_DET_V_BTN_HIGH);
@@ -4727,28 +4837,46 @@
static irqreturn_t tabla_dce_handler(int irq, void *data)
{
int i, mask;
- short dce, sta;
- s32 mv, stamv;
+ short dce, sta, bias_value_dce;
+ s32 mv, stamv, bias_mv_dce;
int btn = -1, meas = 0;
struct tabla_priv *priv = data;
const struct tabla_mbhc_btn_detect_cfg *d =
- TABLA_MBHC_CAL_BTN_DET_PTR(priv->calibration);
+ TABLA_MBHC_CAL_BTN_DET_PTR(priv->mbhc_cfg.calibration);
short btnmeas[d->n_btn_meas + 1];
struct snd_soc_codec *codec = priv->codec;
struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
int n_btn_meas = d->n_btn_meas;
u8 mbhc_status = snd_soc_read(codec, TABLA_A_CDC_MBHC_B1_STATUS) & 0x3E;
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ pr_debug("%s: enter\n", __func__);
- mutex_lock(&priv->mbhc_mutex);
- priv->mbhc_state = TABLA_IRQ_MBHC_POTENTIAL;
- mutex_unlock(&priv->mbhc_mutex);
+ TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
+ if (priv->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+ pr_debug("%s: mbhc is being recovered, skip button press\n",
+ __func__);
+ goto done;
+ }
+
+ priv->mbhc_state = MBHC_STATE_POTENTIAL;
+
+ if (!priv->mbhc_polling_active) {
+ pr_warn("%s: mbhc polling is not active, skip button press\n",
+ __func__);
+ goto done;
+ }
dce = tabla_codec_read_dce_result(codec);
mv = tabla_codec_sta_dce_v(codec, 1, dce);
+ /* If GPIO interrupt already kicked in, ignore button press */
+ if (priv->in_gpio_handler) {
+ pr_debug("%s: GPIO State Changed, ignore button press\n",
+ __func__);
+ btn = -1;
+ goto done;
+ }
+
if (mbhc_status != TABLA_MBHC_STATUS_REL_DETECTION) {
if (priv->mbhc_last_resume &&
!time_after(jiffies, priv->mbhc_last_resume + HZ)) {
@@ -4773,12 +4901,13 @@
meas - 1, dce, mv, btnmeas[meas - 1]);
if (n_btn_meas == 0)
btn = btnmeas[0];
- for (; ((n_btn_meas) && (meas < (n_btn_meas + 1))); meas++) {
- dce = tabla_codec_sta_dce(codec, 1);
- mv = tabla_codec_sta_dce_v(codec, 1, dce);
- btnmeas[meas] = tabla_determine_button(priv, mv);
+ for (; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1))); meas++) {
+ bias_value_dce = tabla_codec_sta_dce(codec, 1, false);
+ bias_mv_dce = tabla_codec_sta_dce_v(codec, 1, bias_value_dce);
+ btnmeas[meas] = tabla_determine_button(priv, bias_mv_dce);
pr_debug("%s: meas %d - DCE %d,%d, button %d\n",
- __func__, meas, dce, mv, btnmeas[meas]);
+ __func__, meas, bias_value_dce, bias_mv_dce,
+ btnmeas[meas]);
/* if large enough measurements are collected,
* start to check if last all n_btn_con measurements were
* in same button low/high range */
@@ -4800,9 +4929,13 @@
}
if (btn >= 0) {
+ if (priv->in_gpio_handler) {
+ pr_debug("%s: GPIO already triggered, ignore button "
+ "press\n", __func__);
+ goto done;
+ }
mask = tabla_get_button_mask(btn);
priv->buttons_pressed |= mask;
- msleep(100);
wcd9xxx_lock_sleep(core);
if (schedule_delayed_work(&priv->mbhc_btn_dwork,
msecs_to_jiffies(400)) == 0) {
@@ -4816,6 +4949,8 @@
}
done:
+ pr_debug("%s: leave\n", __func__);
+ TABLA_RELEASE_LOCK(priv->codec_resource_lock);
return IRQ_HANDLED;
}
@@ -4830,7 +4965,7 @@
for (i = 0; i < dces; i++) {
usleep_range(10000, 10000);
if (i == 0) {
- mb_v = tabla_codec_sta_dce(codec, 0);
+ mb_v = tabla_codec_sta_dce(codec, 0, true);
pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
tabla_codec_sta_dce_v(codec, 0, mb_v));
if (mb_v < (short)priv->mbhc_data.v_b1_hu ||
@@ -4839,7 +4974,7 @@
break;
}
} else {
- mb_v = tabla_codec_sta_dce(codec, 1);
+ mb_v = tabla_codec_sta_dce(codec, 1, true);
pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
tabla_codec_sta_dce_v(codec, 1, mb_v));
if (mb_v < (short)priv->mbhc_data.v_b1_h ||
@@ -4858,42 +4993,41 @@
int ret;
struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
pr_debug("%s: enter\n", __func__);
- mutex_lock(&priv->mbhc_mutex);
- priv->mbhc_state = TABLA_IRQ_MBHC_RELEASE;
- mutex_unlock(&priv->mbhc_mutex);
-
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
+ TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
+ priv->mbhc_state = MBHC_STATE_RELEASE;
if (priv->buttons_pressed & TABLA_JACK_BUTTON_MASK) {
- ret = cancel_delayed_work(&priv->mbhc_btn_dwork);
+ ret = tabla_cancel_btn_work(priv);
if (ret == 0) {
pr_debug("%s: Reporting long button release event\n",
__func__);
- if (priv->button_jack)
+ if (priv->mbhc_cfg.button_jack)
tabla_snd_soc_jack_report(priv,
- priv->button_jack, 0,
- priv->buttons_pressed);
+ priv->mbhc_cfg.button_jack, 0,
+ priv->buttons_pressed);
} else {
- /* if scheduled mbhc_btn_dwork is canceled from here,
- * we have to unlock from here instead btn_work */
- wcd9xxx_unlock_sleep(core);
if (tabla_is_fake_press(priv)) {
pr_debug("%s: Fake button press interrupt\n",
__func__);
- } else if (priv->button_jack) {
- pr_debug("%s: Reporting short button 0 "
- "press and release\n", __func__);
- tabla_snd_soc_jack_report(priv,
- priv->button_jack,
- priv->buttons_pressed,
- priv->buttons_pressed);
- tabla_snd_soc_jack_report(priv,
- priv->button_jack, 0,
- priv->buttons_pressed);
+ } else if (priv->mbhc_cfg.button_jack) {
+ if (priv->in_gpio_handler) {
+ pr_debug("%s: GPIO kicked in, ignore\n",
+ __func__);
+ } else {
+ pr_debug("%s: Reporting short button 0 "
+ "press and release\n",
+ __func__);
+ tabla_snd_soc_jack_report(priv,
+ priv->mbhc_cfg.button_jack,
+ priv->buttons_pressed,
+ priv->buttons_pressed);
+ tabla_snd_soc_jack_report(priv,
+ priv->mbhc_cfg.button_jack, 0,
+ priv->buttons_pressed);
+ }
}
}
@@ -4902,10 +5036,13 @@
tabla_codec_calibrate_hs_polling(codec);
- mutex_lock(&priv->mbhc_mutex);
- tabla_codec_start_hs_polling(codec);
- mutex_unlock(&priv->mbhc_mutex);
+ if (priv->mbhc_cfg.gpio)
+ msleep(TABLA_MBHC_GPIO_REL_DEBOUNCE_TIME_MS);
+ tabla_codec_start_hs_polling(codec);
+
+ pr_debug("%s: leave\n", __func__);
+ TABLA_RELEASE_LOCK(priv->codec_resource_lock);
return IRQ_HANDLED;
}
@@ -4913,9 +5050,8 @@
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
const struct tabla_mbhc_general_cfg *generic =
- TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
+ TABLA_MBHC_CAL_GENERAL_PTR(tabla->mbhc_cfg.calibration);
- mutex_lock(&tabla->mbhc_mutex);
if (!tabla->mclk_enabled && !tabla->mbhc_polling_active)
tabla_codec_enable_config_mode(codec, 1);
@@ -4930,26 +5066,23 @@
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
if (!tabla->mclk_enabled && !tabla->mbhc_polling_active)
tabla_codec_enable_config_mode(codec, 0);
- mutex_unlock(&tabla->mbhc_mutex);
snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x00);
}
-static void tabla_codec_shutdown_hs_polling(struct snd_soc_codec *codec)
+static void tabla_codec_cleanup_hs_polling(struct snd_soc_codec *codec)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
tabla_codec_shutdown_hs_removal_detect(codec);
- mutex_lock(&tabla->mbhc_mutex);
if (!tabla->mclk_enabled) {
- snd_soc_update_bits(codec, TABLA_A_TX_COM_BIAS, 0xE0, 0x00);
tabla_codec_disable_clock_block(codec);
tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
}
tabla->mbhc_polling_active = false;
- mutex_unlock(&tabla->mbhc_mutex);
+ tabla->mbhc_state = MBHC_STATE_NONE;
}
static irqreturn_t tabla_hphl_ocp_irq(int irq, void *data)
@@ -4972,11 +5105,11 @@
TABLA_IRQ_HPH_PA_OCPL_FAULT);
tabla->hphlocp_cnt = 0;
tabla->hph_status |= SND_JACK_OC_HPHL;
- if (tabla->headset_jack)
+ if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
- tabla->headset_jack,
- tabla->hph_status,
- TABLA_JACK_MASK);
+ tabla->mbhc_cfg.headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
}
} else {
pr_err("%s: Bad tabla private data\n", __func__);
@@ -5005,11 +5138,11 @@
TABLA_IRQ_HPH_PA_OCPR_FAULT);
tabla->hphrocp_cnt = 0;
tabla->hph_status |= SND_JACK_OC_HPHR;
- if (tabla->headset_jack)
+ if (tabla->mbhc_cfg.headset_jack)
tabla_snd_soc_jack_report(tabla,
- tabla->headset_jack,
- tabla->hph_status,
- TABLA_JACK_MASK);
+ tabla->mbhc_cfg.headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
}
} else {
pr_err("%s: Bad tabla private data\n", __func__);
@@ -5018,66 +5151,292 @@
return IRQ_HANDLED;
}
-static void tabla_sync_hph_state(struct tabla_priv *tabla)
-{
- if (test_and_clear_bit(TABLA_HPHR_PA_OFF_ACK,
- &tabla->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
- snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_CNP_EN, 0x10,
- 1 << 4);
- }
- if (test_and_clear_bit(TABLA_HPHL_PA_OFF_ACK,
- &tabla->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
- snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_CNP_EN, 0x20,
- 1 << 5);
- }
-
- if (test_and_clear_bit(TABLA_HPHR_DAC_OFF_ACK,
- &tabla->hph_pa_dac_state)) {
- pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_R_DAC_CTL,
- 0xC0, 0xC0);
- }
- if (test_and_clear_bit(TABLA_HPHL_DAC_OFF_ACK,
- &tabla->hph_pa_dac_state)) {
- pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
- snd_soc_update_bits(tabla->codec, TABLA_A_RX_HPH_L_DAC_CTL,
- 0xC0, 0xC0);
- }
-}
-
-static bool mbhc_is_fake_insertion(struct snd_soc_codec *codec, s32 mic_volt)
+static bool tabla_is_invalid_insertion_range(struct snd_soc_codec *codec,
+ s32 mic_volt)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
struct tabla_mbhc_plug_type_cfg *plug_type =
- TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->calibration);
+ TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+ int fake_insert_high;
+ bool invalid = false;
- if (mic_volt > plug_type->v_hs_max ||
- (mic_volt < TABLA_MBHC_FAKE_INSERT_HIGH &&
- mic_volt > TABLA_MBHC_FAKE_INSERT_LOW))
+ if (tabla->mbhc_cfg.gpio)
+ fake_insert_high = TABLA_MBHC_FAKE_INSERT_HIGH;
+ else
+ fake_insert_high = TABLA_MBHC_FAKE_INS_HIGH_NO_GPIO;
+
+ /* Perform this check only when the high voltage headphone
+ * needs to be considered as invalid
+ */
+ if (!tabla->mbhc_inval_hs_range_override
+ && (mic_volt > plug_type->v_hs_max)) {
+ invalid = true;
+ } else if (mic_volt < fake_insert_high
+ && (mic_volt > TABLA_MBHC_FAKE_INSERT_LOW)) {
+ invalid = true;
+ }
+
+ return invalid;
+}
+
+static bool tabla_is_invalid_insert_delta(struct snd_soc_codec *codec,
+ int mic_volt, int mic_volt_prev)
+{
+ int delta = abs(mic_volt - mic_volt_prev);
+ if (delta > TABLA_MBHC_FAKE_INSERT_VOLT_DELTA_MV) {
+ pr_debug("%s: volt delta %dmv\n", __func__, delta);
return true;
-
+ }
return false;
}
-static void tabla_codec_detect_plug_type(struct snd_soc_codec *codec)
+static bool tabla_codec_is_invalid_plug(struct snd_soc_codec *codec,
+ s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT],
+ enum tabla_mbhc_plug_type
+ plug_type[MBHC_NUM_DCE_PLUG_DETECT])
+{
+ int i;
+ bool r = false;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct tabla_mbhc_plug_type_cfg *plug_type_ptr =
+ TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+
+ for (i = 0 ; i < MBHC_NUM_DCE_PLUG_DETECT && !r; i++) {
+ if (mic_mv[i] < plug_type_ptr->v_no_mic)
+ plug_type[i] = PLUG_TYPE_HEADPHONE;
+ else if (mic_mv[i] < plug_type_ptr->v_hs_max)
+ plug_type[i] = PLUG_TYPE_HEADSET;
+ else if (mic_mv[i] > plug_type_ptr->v_hs_max)
+ plug_type[i] = PLUG_TYPE_HIGH_HPH;
+
+ r = tabla_is_invalid_insertion_range(codec, mic_mv[i]);
+ if (!r && i > 0) {
+ if (plug_type[i-1] != plug_type[i])
+ r = true;
+ else
+ r = tabla_is_invalid_insert_delta(codec,
+ mic_mv[i],
+ mic_mv[i - 1]);
+ }
+ }
+
+ return r;
+}
+
+/* called under codec_resource_lock acquisition */
+void tabla_find_plug_and_report(struct snd_soc_codec *codec,
+ enum tabla_mbhc_plug_type plug_type)
{
struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
- const struct tabla_mbhc_plug_detect_cfg *plug_det =
- TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->calibration);
- int ldo_h_on, micb_cfilt_on, i;
+
+ if (plug_type == PLUG_TYPE_HEADPHONE
+ && tabla->current_plug == PLUG_TYPE_NONE) {
+ /* Nothing was reported previously
+ * reporte a headphone
+ */
+ tabla_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+ tabla_codec_cleanup_hs_polling(codec);
+ } else if (plug_type == PLUG_TYPE_HEADSET) {
+ /* If Headphone was reported previously, this will
+ * only report the mic line
+ */
+ tabla_codec_report_plug(codec, 1, SND_JACK_HEADSET);
+ msleep(100);
+ tabla_codec_start_hs_polling(codec);
+ } else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+ if (tabla->current_plug == PLUG_TYPE_NONE)
+ tabla_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+ tabla_codec_cleanup_hs_polling(codec);
+ pr_debug("setup mic trigger for further detection\n");
+ tabla->lpi_enabled = true;
+ tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
+ false);
+ }
+}
+
+/* should be called under interrupt context that hold suspend */
+static void tabla_schedule_hs_detect_plug(struct tabla_priv *tabla)
+{
+ pr_debug("%s: scheduling tabla_hs_correct_gpio_plug\n", __func__);
+ tabla->hs_detect_work_stop = false;
+ wcd9xxx_lock_sleep(tabla->codec->control_data);
+ schedule_work(&tabla->hs_correct_plug_work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void tabla_cancel_hs_detect_plug(struct tabla_priv *tabla)
+{
+ pr_debug("%s: canceling hs_correct_plug_work\n", __func__);
+ tabla->hs_detect_work_stop = true;
+ wmb();
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ if (cancel_work_sync(&tabla->hs_correct_plug_work)) {
+ pr_debug("%s: hs_correct_plug_work is canceled\n", __func__);
+ wcd9xxx_unlock_sleep(tabla->codec->control_data);
+ }
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+}
+
+static void tabla_turn_onoff_override(struct snd_soc_codec *codec, bool on)
+{
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, on << 2);
+}
+
+static bool tabla_hs_gpio_level_remove(struct tabla_priv *tabla)
+{
+ return (gpio_get_value_cansleep(tabla->mbhc_cfg.gpio) !=
+ tabla->mbhc_cfg.gpio_level_insert);
+}
+
+static void tabla_hs_correct_gpio_plug(struct work_struct *work)
+{
+ struct tabla_priv *tabla;
+ struct snd_soc_codec *codec;
+ int retry = 0, i;
+ bool correction = false;
+ s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
+ short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
+ enum tabla_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
+ unsigned long timeout;
+
+ tabla = container_of(work, struct tabla_priv, hs_correct_plug_work);
+ codec = tabla->codec;
+
+ pr_debug("%s: enter\n", __func__);
+ tabla->mbhc_cfg.mclk_cb_fn(codec, 1, false);
+
+ /* Keep override on during entire plug type correction work.
+ *
+ * This is okay under the assumption that any GPIO irqs which use
+ * MBHC block cancel and sync this work so override is off again
+ * prior to GPIO interrupt handler's MBHC block usage.
+ * Also while this correction work is running, we can guarantee
+ * DAPM doesn't use any MBHC block as this work only runs with
+ * headphone detection.
+ */
+ tabla_turn_onoff_override(codec, true);
+
+ timeout = jiffies + msecs_to_jiffies(TABLA_HS_DETECT_PLUG_TIME_MS);
+ while (!time_after(jiffies, timeout)) {
+ ++retry;
+ rmb();
+ if (tabla->hs_detect_work_stop) {
+ pr_debug("%s: stop requested\n", __func__);
+ break;
+ }
+
+ msleep(TABLA_HS_DETECT_PLUG_INERVAL_MS);
+ if (tabla_hs_gpio_level_remove(tabla)) {
+ pr_debug("%s: GPIO value is low\n", __func__);
+ break;
+ }
+
+ /* can race with removal interrupt */
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+ for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = __tabla_codec_sta_dce(codec, 1, true, true);
+ mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
+ pr_debug("%s : DCE run %d, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i], mb_v[i]);
+ }
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+
+ if (tabla_codec_is_invalid_plug(codec, mic_mv, plug_type)) {
+ pr_debug("Invalid plug in attempt # %d\n", retry);
+ if (retry == NUM_ATTEMPTS_TO_REPORT &&
+ tabla->current_plug == PLUG_TYPE_NONE) {
+ tabla_codec_report_plug(codec, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else if (!tabla_codec_is_invalid_plug(codec, mic_mv,
+ plug_type) &&
+ plug_type[0] == PLUG_TYPE_HEADPHONE) {
+ pr_debug("Good headphone detected, continue polling mic\n");
+ if (tabla->current_plug == PLUG_TYPE_NONE) {
+ tabla_codec_report_plug(codec, 1,
+ SND_JACK_HEADPHONE);
+ }
+ } else {
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+ /* Turn off override */
+ tabla_turn_onoff_override(codec, false);
+ tabla_find_plug_and_report(codec, plug_type[0]);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ pr_debug("Attempt %d found correct plug %d\n", retry,
+ plug_type[0]);
+ correction = true;
+ break;
+ }
+ }
+
+ /* Turn off override */
+ if (!correction)
+ tabla_turn_onoff_override(codec, false);
+
+ tabla->mbhc_cfg.mclk_cb_fn(codec, 0, false);
+ pr_debug("%s: leave\n", __func__);
+ /* unlock sleep */
+ wcd9xxx_unlock_sleep(tabla->codec->control_data);
+}
+
+/* called under codec_resource_lock acquisition */
+static void tabla_codec_decide_gpio_plug(struct snd_soc_codec *codec)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
- u8 plug_type[MBHC_NUM_DCE_PLUG_DETECT];
+ enum tabla_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
+ int i;
+
+ pr_debug("%s: enter\n", __func__);
+
+ tabla_turn_onoff_override(codec, true);
+ mb_v[0] = tabla_codec_setup_hs_polling(codec);
+ mic_mv[0] = tabla_codec_sta_dce_v(codec, 1, mb_v[0]);
+ pr_debug("%s: DCE run 1, mic_mv = %d\n", __func__, mic_mv[0]);
+
+ for (i = 1; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = __tabla_codec_sta_dce(codec, 1, true, true);
+ mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
+ pr_debug("%s: DCE run %d, mic_mv = %d\n", __func__, i + 1,
+ mic_mv[i]);
+ }
+ tabla_turn_onoff_override(codec, false);
+
+ if (tabla_hs_gpio_level_remove(tabla)) {
+ pr_debug("%s: GPIO value is low when determining plug\n",
+ __func__);
+ return;
+ }
+
+ if (tabla_codec_is_invalid_plug(codec, mic_mv, plug_type)) {
+ tabla_schedule_hs_detect_plug(tabla);
+ } else if (plug_type[0] == PLUG_TYPE_HEADPHONE) {
+ tabla_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+
+ tabla_schedule_hs_detect_plug(tabla);
+ } else {
+ pr_debug("%s: Valid plug found, determine plug type\n",
+ __func__);
+ tabla_find_plug_and_report(codec, plug_type[0]);
+ }
+}
+
+/* called under codec_resource_lock acquisition */
+static void tabla_codec_detect_plug_type(struct snd_soc_codec *codec)
+{
+ int i;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ const struct tabla_mbhc_plug_detect_cfg *plug_det =
+ TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->mbhc_cfg.calibration);
+ short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
+ s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
+ enum tabla_mbhc_plug_type plug_type[MBHC_NUM_DCE_PLUG_DETECT];
struct tabla_mbhc_plug_type_cfg *plug_type_ptr;
- ldo_h_on = snd_soc_read(codec, TABLA_A_LDO_H_MODE_1) & 0x80;
- micb_cfilt_on = snd_soc_read(codec, tabla->mbhc_bias_regs.cfilt_ctl) &
- 0x80;
-
- /* Turn on the override */
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+ /* Turn on the override,
+ * tabla_codec_setup_hs_polling requires override on */
+ tabla_turn_onoff_override(codec, true);
if (plug_det->t_ins_complete > 20)
msleep(plug_det->t_ins_complete);
@@ -5085,6 +5444,17 @@
usleep_range(plug_det->t_ins_complete * 1000,
plug_det->t_ins_complete * 1000);
+ if (tabla->mbhc_cfg.gpio) {
+ /* Turn off the override */
+ tabla_turn_onoff_override(codec, false);
+ if (tabla_hs_gpio_level_remove(tabla))
+ pr_debug("%s: GPIO value is low when determining "
+ "plug\n", __func__);
+ else
+ tabla_codec_decide_gpio_plug(codec);
+ return;
+ }
+
/*
* First DCE measurement,
* IF this is fake, discontinue detection
@@ -5092,14 +5462,15 @@
*/
mb_v[0] = tabla_codec_setup_hs_polling(codec);
mic_mv[0] = tabla_codec_sta_dce_v(codec, 1, mb_v[0]);
- if (mbhc_is_fake_insertion(codec, mic_mv[0])) {
+ if (tabla_is_invalid_insertion_range(codec, mic_mv[0])) {
pr_debug("%s: Detect attempt 1, detected Fake\n", __func__);
- tabla_codec_shutdown_hs_polling(codec);
+ tabla_turn_onoff_override(codec, false);
+ tabla_codec_cleanup_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
false);
return;
}
- pr_debug("%s: Run 1, DCE %x, mic_mv = %d\n", __func__, mb_v[0],
+ pr_debug("%s: DCE run 1, %x, mic_mv = %d\n", __func__, mb_v[0],
mic_mv[0]);
/*
@@ -5107,27 +5478,30 @@
* IF any of them is fake, discontinue detection
* and restart insertion detection
*/
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x00);
for (i = 1; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
- mb_v[i] = tabla_codec_sta_dce(codec, 1);
+ mb_v[i] = __tabla_codec_sta_dce(codec, 1, true, true);
mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
- pr_debug("%s: Run %d, DCE %x, mic_mv = %d\n", __func__, i + 1,
+ pr_debug("%s: DCE run %d, %x, mic_mv = %d\n", __func__, i + 1,
mb_v[1], mic_mv[i]);
- if (mbhc_is_fake_insertion(codec, mic_mv[i])) {
+ if (tabla_is_invalid_insertion_range(codec, mic_mv[i])
+ || tabla_is_invalid_insert_delta(codec, mic_mv[i],
+ mic_mv[i - 1])) {
pr_debug("%s: Detect attempt %d, detected Fake\n",
__func__, i + 1);
+ tabla_turn_onoff_override(codec, false);
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
0x02, 0x02);
- tabla_codec_shutdown_hs_polling(codec);
+ tabla_codec_cleanup_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 1,
MBHC_USE_MB_TRIGGER,
false);
return;
}
}
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+ tabla_turn_onoff_override(codec, false);
- plug_type_ptr = TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->calibration);
+ plug_type_ptr =
+ TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
/*
* If we are here, means none of the three
@@ -5149,7 +5523,7 @@
if (i > 0 && (plug_type[i - 1] != plug_type[i])) {
pr_err("%s: Detect attempt %d and %d are not same",
__func__, i - 1, i);
- tabla_codec_shutdown_hs_polling(codec);
+ tabla_codec_cleanup_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 1,
MBHC_USE_MB_TRIGGER,
false);
@@ -5159,84 +5533,70 @@
if (plug_type[0] == PLUG_TYPE_HEADPHONE) {
pr_debug("%s: Headphone Detected\n", __func__);
- tabla->hph_status |= SND_JACK_HEADPHONE;
- if (tabla->headset_jack) {
- pr_debug("%s: Reporting insertion %d\n", __func__,
- SND_JACK_HEADPHONE);
- tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
- tabla->hph_status,
- TABLA_JACK_MASK);
- }
- tabla_codec_shutdown_hs_polling(codec);
+ tabla_codec_report_plug(codec, 1, SND_JACK_HEADPHONE);
+ tabla_codec_cleanup_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 0, 0, false);
- tabla_sync_hph_state(tabla);
} else if (plug_type[0] == PLUG_TYPE_HEADSET) {
pr_debug("%s: Headset detected\n", __func__);
- tabla->hph_status |= SND_JACK_HEADSET;
- if (tabla->headset_jack) {
- pr_err("%s: Reporting insertion %d\n", __func__,
- SND_JACK_HEADSET);
- tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
- tabla->hph_status,
- TABLA_JACK_MASK);
- }
+ tabla_codec_report_plug(codec, 1, SND_JACK_HEADSET);
+
/* avoid false button press detect */
msleep(50);
- mutex_lock(&tabla->mbhc_mutex);
tabla_codec_start_hs_polling(codec);
- mutex_unlock(&tabla->mbhc_mutex);
- tabla_sync_hph_state(tabla);
}
}
-static irqreturn_t tabla_hs_insert_irq(int irq, void *data)
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void tabla_hs_insert_irq_gpio(struct tabla_priv *priv, bool is_removal)
{
- struct tabla_priv *priv = data;
struct snd_soc_codec *codec = priv->codec;
- u8 is_removal, is_mb_trigger;
- struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
+
+ if (!is_removal) {
+ pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+ rmb();
+ if (priv->lpi_enabled)
+ msleep(100);
+
+ rmb();
+ if (!priv->lpi_enabled) {
+ pr_debug("%s: lpi is disabled\n", __func__);
+ } else if (gpio_get_value_cansleep(priv->mbhc_cfg.gpio) ==
+ priv->mbhc_cfg.gpio_level_insert) {
+ pr_debug("%s: Valid insertion, "
+ "detect plug type\n", __func__);
+ tabla_codec_decide_gpio_plug(codec);
+ } else {
+ pr_debug("%s: Invalid insertion, "
+ "stop plug detection\n", __func__);
+ }
+ } else {
+ pr_err("%s: GPIO used, invalid MBHC Removal\n", __func__);
+ }
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void tabla_hs_insert_irq_nogpio(struct tabla_priv *priv, bool is_removal,
+ bool is_mb_trigger)
+{
int ret;
-
- pr_debug("%s: enter\n", __func__);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
-
- is_removal = snd_soc_read(codec, TABLA_A_CDC_MBHC_INT_CTL) & 0x02;
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
-
- is_mb_trigger = snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
- 0x10;
-
- /* Turn off both HPH and MIC line schmitt triggers */
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
- snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
- snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+ struct snd_soc_codec *codec = priv->codec;
+ struct wcd9xxx *core = dev_get_drvdata(priv->codec->dev->parent);
if (is_removal) {
+ /* cancel possiblely running hs detect work */
+ tabla_cancel_hs_detect_plug(priv);
+
/*
* If headphone is removed while playback is in progress,
* it is possible that micbias will be switched to VDDIO.
*/
tabla_codec_switch_micbias(codec, 0);
- priv->hph_status &= ~SND_JACK_HEADPHONE;
- if (priv->headset_jack) {
- pr_debug("%s: Reporting removal\n", __func__);
- tabla_snd_soc_jack_report(priv, priv->headset_jack,
- priv->hph_status,
- TABLA_JACK_MASK);
- }
-
- hphocp_off_report(priv, SND_JACK_OC_HPHR,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
- hphocp_off_report(priv, SND_JACK_OC_HPHL,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
-
+ tabla_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
tabla_codec_shutdown_hs_removal_detect(codec);
tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
true);
- return IRQ_HANDLED;
- }
-
- if (is_mb_trigger && !is_removal) {
+ } else if (is_mb_trigger && !is_removal) {
pr_debug("%s: Waiting for Headphone left trigger\n",
__func__);
wcd9xxx_lock_sleep(core);
@@ -5248,85 +5608,220 @@
}
tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_HPHL_TRIGGER,
false);
- return IRQ_HANDLED;
+ } else {
+ wcd9xxx_lock_sleep(core);
+ ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
+ if (ret != 0) {
+ pr_debug("%s: Complete plug insertion, Detecting plug "
+ "type\n", __func__);
+ tabla_codec_detect_plug_type(codec);
+ wcd9xxx_unlock_sleep(core);
+ } else {
+ wcd9xxx_enable_irq(codec->control_data,
+ TABLA_IRQ_MBHC_INSERTION);
+ pr_err("%s: Error detecting plug insertion\n",
+ __func__);
+ }
}
+}
- ret = cancel_delayed_work(&priv->mbhc_insert_dwork);
- if (ret != 0) {
- pr_debug("%s: Complete plug insertion, Detecting plug type\n",
- __func__);
- tabla_codec_detect_plug_type(codec);
- wcd9xxx_unlock_sleep(core);
- } else {
- wcd9xxx_enable_irq(codec->control_data,
- TABLA_IRQ_MBHC_INSERTION);
- pr_err("%s: Error detecting plug insertion\n", __func__);
- }
+static irqreturn_t tabla_hs_insert_irq(int irq, void *data)
+{
+ bool is_mb_trigger, is_removal;
+ struct tabla_priv *priv = data;
+ struct snd_soc_codec *codec = priv->codec;
+ pr_debug("%s: enter\n", __func__);
+ TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
+ wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+
+ is_mb_trigger = !!(snd_soc_read(codec, priv->mbhc_bias_regs.mbhc_reg) &
+ 0x10);
+ is_removal = !!(snd_soc_read(codec, TABLA_A_CDC_MBHC_INT_CTL) & 0x02);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+ /* Turn off both HPH and MIC line schmitt triggers */
+ snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
+ snd_soc_update_bits(codec, priv->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+ if (priv->mbhc_cfg.gpio)
+ tabla_hs_insert_irq_gpio(priv, is_removal);
+ else
+ tabla_hs_insert_irq_nogpio(priv, is_removal, is_mb_trigger);
+
+ TABLA_RELEASE_LOCK(priv->codec_resource_lock);
return IRQ_HANDLED;
}
-static irqreturn_t tabla_hs_remove_irq(int irq, void *data)
+static bool is_valid_mic_voltage(struct snd_soc_codec *codec, s32 mic_mv)
+{
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct tabla_mbhc_plug_type_cfg *plug_type =
+ TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+
+ return (!(mic_mv > 10 && mic_mv < 80) && (mic_mv > plug_type->v_no_mic)
+ && (mic_mv < plug_type->v_hs_max)) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed */
+static bool tabla_hs_remove_settle(struct snd_soc_codec *codec)
+{
+ int i;
+ bool timedout, settled = false;
+ s32 mic_mv[MBHC_NUM_DCE_PLUG_DETECT];
+ short mb_v[MBHC_NUM_DCE_PLUG_DETECT];
+ unsigned long retry = 0, timeout;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ struct tabla_mbhc_plug_type_cfg *plug_type =
+ TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla->mbhc_cfg.calibration);
+
+ timeout = jiffies + msecs_to_jiffies(TABLA_HS_DETECT_PLUG_TIME_MS);
+ while (!(timedout = time_after(jiffies, timeout))) {
+ retry++;
+ if (tabla->mbhc_cfg.gpio && tabla_hs_gpio_level_remove(tabla)) {
+ pr_debug("%s: GPIO indicates removal\n", __func__);
+ break;
+ }
+
+ if (tabla->mbhc_cfg.gpio) {
+ if (retry > 1)
+ msleep(250);
+ else
+ msleep(50);
+ }
+
+ if (tabla->mbhc_cfg.gpio && tabla_hs_gpio_level_remove(tabla)) {
+ pr_debug("%s: GPIO indicates removal\n", __func__);
+ break;
+ }
+
+ for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++) {
+ mb_v[i] = tabla_codec_sta_dce(codec, 1, true);
+ mic_mv[i] = tabla_codec_sta_dce_v(codec, 1 , mb_v[i]);
+ pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+ __func__, retry, mic_mv[i], mb_v[i]);
+ }
+
+ if (tabla->mbhc_cfg.gpio && tabla_hs_gpio_level_remove(tabla)) {
+ pr_debug("%s: GPIO indicates removal\n", __func__);
+ break;
+ }
+
+ if (tabla->current_plug == PLUG_TYPE_NONE) {
+ pr_debug("%s : headset/headphone is removed\n",
+ __func__);
+ break;
+ }
+
+ for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
+ if (!is_valid_mic_voltage(codec, mic_mv[i]))
+ break;
+
+ if (i == MBHC_NUM_DCE_PLUG_DETECT) {
+ pr_debug("%s: MIC voltage settled\n", __func__);
+ settled = true;
+ msleep(200);
+ break;
+ }
+
+ /* only for non-GPIO remove irq */
+ if (!tabla->mbhc_cfg.gpio) {
+ for (i = 0; i < MBHC_NUM_DCE_PLUG_DETECT; i++)
+ if (mic_mv[i] < plug_type->v_hs_max)
+ break;
+ if (i == MBHC_NUM_DCE_PLUG_DETECT) {
+ pr_debug("%s: Headset is removed\n", __func__);
+ break;
+ }
+ }
+ }
+
+ if (timedout)
+ pr_debug("%s: Microphone did not settle in %d seconds\n",
+ __func__, TABLA_HS_DETECT_PLUG_TIME_MS);
+ return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void tabla_hs_remove_irq_gpio(struct tabla_priv *priv)
+{
+ struct snd_soc_codec *codec = priv->codec;
+
+ if (tabla_hs_remove_settle(codec))
+ tabla_codec_start_hs_polling(codec);
+ pr_debug("%s: remove settle done\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void tabla_hs_remove_irq_nogpio(struct tabla_priv *priv)
{
short bias_value;
- struct tabla_priv *priv = data;
+ bool removed = true;
struct snd_soc_codec *codec = priv->codec;
const struct tabla_mbhc_general_cfg *generic =
- TABLA_MBHC_CAL_GENERAL_PTR(priv->calibration);
+ TABLA_MBHC_CAL_GENERAL_PTR(priv->mbhc_cfg.calibration);
int min_us = TABLA_FAKE_REMOVAL_MIN_PERIOD_MS * 1000;
- int fake_removal = 0;
- pr_debug("%s: enter, removal interrupt\n", __func__);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
+ if (priv->current_plug != PLUG_TYPE_HEADSET) {
+ pr_debug("%s(): Headset is not inserted, ignore removal\n",
+ __func__);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ return;
+ }
usleep_range(generic->t_shutdown_plug_rem,
generic->t_shutdown_plug_rem);
do {
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x00);
- bias_value = tabla_codec_sta_dce(codec, 1);
- snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+ bias_value = tabla_codec_sta_dce(codec, 1, true);
pr_debug("%s: DCE %d,%d, %d us left\n", __func__, bias_value,
tabla_codec_sta_dce_v(codec, 1, bias_value), min_us);
if (bias_value < (short)priv->mbhc_data.v_ins_h) {
- fake_removal = 1;
+ pr_debug("%s: checking false removal\n", __func__);
+ msleep(500);
+ removed = !tabla_hs_remove_settle(codec);
+ pr_debug("%s: headset %sactually removed\n", __func__,
+ removed ? "" : "not ");
break;
}
min_us -= priv->mbhc_data.t_dce;
} while (min_us > 0);
- if (fake_removal) {
- pr_debug("False alarm, headset not actually removed\n");
- mutex_lock(&priv->mbhc_mutex);
- tabla_codec_start_hs_polling(codec);
- mutex_unlock(&priv->mbhc_mutex);
- } else {
+ if (removed) {
+ /* cancel possiblely running hs detect work */
+ tabla_cancel_hs_detect_plug(priv);
/*
* If this removal is not false, first check the micbias
* switch status and switch it to LDOH if it is already
* switched to VDDIO.
*/
tabla_codec_switch_micbias(codec, 0);
- priv->hph_status &= ~SND_JACK_HEADSET;
- if (priv->headset_jack) {
- pr_debug("%s: Reporting removal\n", __func__);
- tabla_snd_soc_jack_report(priv, priv->headset_jack, 0,
- TABLA_JACK_MASK);
- }
- hphocp_off_report(priv, SND_JACK_OC_HPHR,
- TABLA_IRQ_HPH_PA_OCPR_FAULT);
- hphocp_off_report(priv, SND_JACK_OC_HPHL,
- TABLA_IRQ_HPH_PA_OCPL_FAULT);
-
- tabla_codec_shutdown_hs_polling(codec);
-
+ tabla_codec_report_plug(codec, 0, SND_JACK_HEADSET);
+ tabla_codec_cleanup_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
true);
+ } else {
+ tabla_codec_start_hs_polling(codec);
}
+}
+static irqreturn_t tabla_hs_remove_irq(int irq, void *data)
+{
+ struct tabla_priv *priv = data;
+ pr_debug("%s: enter, removal interrupt\n", __func__);
+
+ TABLA_ACQUIRE_LOCK(priv->codec_resource_lock);
+ if (priv->mbhc_cfg.gpio)
+ tabla_hs_remove_irq_gpio(priv);
+ else
+ tabla_hs_remove_irq_nogpio(priv);
+
+ TABLA_RELEASE_LOCK(priv->codec_resource_lock);
return IRQ_HANDLED;
}
@@ -5338,8 +5833,7 @@
struct wcd9xxx *tabla_core;
dwork = to_delayed_work(work);
- tabla = container_of(dwork, struct tabla_priv,
- mbhc_insert_dwork);
+ tabla = container_of(dwork, struct tabla_priv, mbhc_insert_dwork);
codec = tabla->codec;
tabla_core = dev_get_drvdata(codec->dev->parent);
@@ -5354,37 +5848,196 @@
wcd9xxx_unlock_sleep(tabla_core);
}
+static void tabla_hs_gpio_handler(struct snd_soc_codec *codec)
+{
+ bool insert;
+ struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
+ bool is_removed = false;
+
+ pr_debug("%s: enter\n", __func__);
+
+ tabla->in_gpio_handler = true;
+ /* Wait here for debounce time */
+ usleep_range(TABLA_GPIO_IRQ_DEBOUNCE_TIME_US,
+ TABLA_GPIO_IRQ_DEBOUNCE_TIME_US);
+
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
+
+ /* cancel pending button press */
+ if (tabla_cancel_btn_work(tabla))
+ pr_debug("%s: button press is canceled\n", __func__);
+
+ insert = (gpio_get_value_cansleep(tabla->mbhc_cfg.gpio) ==
+ tabla->mbhc_cfg.gpio_level_insert);
+ if ((tabla->current_plug == PLUG_TYPE_NONE) && insert) {
+ tabla->lpi_enabled = false;
+ wmb();
+
+ /* cancel detect plug */
+ tabla_cancel_hs_detect_plug(tabla);
+
+ /* Disable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x00);
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x01, 0x00);
+ tabla_codec_detect_plug_type(codec);
+ } else if ((tabla->current_plug != PLUG_TYPE_NONE) && !insert) {
+ tabla->lpi_enabled = false;
+ wmb();
+
+ /* cancel detect plug */
+ tabla_cancel_hs_detect_plug(tabla);
+
+ if (tabla->current_plug == PLUG_TYPE_HEADPHONE) {
+ tabla_codec_report_plug(codec, 0, SND_JACK_HEADPHONE);
+ is_removed = true;
+ } else if (tabla->current_plug == PLUG_TYPE_HEADSET) {
+ tabla_codec_pause_hs_polling(codec);
+ tabla_codec_cleanup_hs_polling(codec);
+ tabla_codec_report_plug(codec, 0, SND_JACK_HEADSET);
+ is_removed = true;
+ }
+
+ if (is_removed) {
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ tabla->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x01,
+ 0x01);
+ /* Make sure mic trigger is turned off */
+ snd_soc_update_bits(codec,
+ tabla->mbhc_bias_regs.ctl_reg,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ tabla->mbhc_bias_regs.mbhc_reg,
+ 0x90, 0x00);
+ /* Reset MBHC State Machine */
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x08);
+ snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL,
+ 0x08, 0x00);
+ /* Turn off override */
+ tabla_turn_onoff_override(codec, false);
+ }
+ }
+
+ tabla->in_gpio_handler = false;
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
+ pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t tabla_mechanical_plug_detect_irq(int irq, void *data)
+{
+ int r = IRQ_HANDLED;
+ struct snd_soc_codec *codec = data;
+
+ if (unlikely(wcd9xxx_lock_sleep(codec->control_data) == false)) {
+ pr_warn("%s: failed to hold suspend\n", __func__);
+ r = IRQ_NONE;
+ } else {
+ tabla_hs_gpio_handler(codec);
+ wcd9xxx_unlock_sleep(codec->control_data);
+ }
+
+ return r;
+}
+
+static void mbhc_fw_read(struct work_struct *work)
+{
+ struct delayed_work *dwork;
+ struct tabla_priv *tabla;
+ struct snd_soc_codec *codec;
+ const struct firmware *fw;
+ int ret = -1, retry = 0, rc;
+
+ dwork = to_delayed_work(work);
+ tabla = container_of(dwork, struct tabla_priv,
+ mbhc_firmware_dwork);
+ codec = tabla->codec;
+
+ while (retry < MBHC_FW_READ_ATTEMPTS) {
+ retry++;
+ pr_info("%s:Attempt %d to request MBHC firmware\n",
+ __func__, retry);
+ ret = request_firmware(&fw, "wcd9310/wcd9310_mbhc.bin",
+ codec->dev);
+
+ if (ret != 0) {
+ usleep_range(MBHC_FW_READ_TIMEOUT,
+ MBHC_FW_READ_TIMEOUT);
+ } else {
+ pr_info("%s: MBHC Firmware read succesful\n", __func__);
+ break;
+ }
+ }
+
+ if (ret != 0) {
+ pr_err("%s: Cannot load MBHC firmware use default cal\n",
+ __func__);
+ } else if (tabla_mbhc_fw_validate(fw) == false) {
+ pr_err("%s: Invalid MBHC cal data size use default cal\n",
+ __func__);
+ release_firmware(fw);
+ } else {
+ tabla->mbhc_cfg.calibration = (void *)fw->data;
+ tabla->mbhc_fw = fw;
+ }
+
+ tabla->mbhc_cfg.mclk_cb_fn(codec, 1, false);
+ tabla_mbhc_init(codec);
+ tabla_mbhc_cal(codec);
+ tabla_mbhc_calc_thres(codec);
+ tabla->mbhc_cfg.mclk_cb_fn(codec, 0, false);
+ tabla_codec_calibrate_hs_polling(codec);
+ if (!tabla->mbhc_cfg.gpio) {
+ tabla->mbhc_inval_hs_range_override = false;
+ rc = tabla_codec_enable_hs_detect(codec, 1, MBHC_USE_MB_TRIGGER,
+ false);
+
+ if (IS_ERR_VALUE(rc))
+ pr_err("%s: Failed to setup MBHC detection\n",
+ __func__);
+ } else {
+ tabla->mbhc_inval_hs_range_override = true;
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ tabla->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x01,
+ 0x01);
+ INIT_WORK(&tabla->hs_correct_plug_work,
+ tabla_hs_correct_gpio_plug);
+ }
+
+}
+
int tabla_hs_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *headset_jack,
- struct snd_soc_jack *button_jack,
- void *calibration, enum tabla_micbias_num micbias,
- int (*mclk_cb_fn) (struct snd_soc_codec*, int),
- int read_fw_bin, u32 mclk_rate)
+ const struct tabla_mbhc_config *cfg)
{
struct tabla_priv *tabla;
int rc = 0;
- if (!codec || !calibration) {
+ if (!codec || !cfg->calibration) {
pr_err("Error: no codec or calibration\n");
return -EINVAL;
}
- if (mclk_rate != TABLA_MCLK_RATE_12288KHZ) {
- if (mclk_rate == TABLA_MCLK_RATE_9600KHZ)
+ if (cfg->mclk_rate != TABLA_MCLK_RATE_12288KHZ) {
+ if (cfg->mclk_rate == TABLA_MCLK_RATE_9600KHZ)
pr_err("Error: clock rate %dHz is not yet supported\n",
- mclk_rate);
+ cfg->mclk_rate);
else
- pr_err("Error: unsupported clock rate %d\n", mclk_rate);
+ pr_err("Error: unsupported clock rate %d\n",
+ cfg->mclk_rate);
return -EINVAL;
}
tabla = snd_soc_codec_get_drvdata(codec);
- tabla->headset_jack = headset_jack;
- tabla->button_jack = button_jack;
- tabla->micbias = micbias;
- tabla->calibration = calibration;
- tabla->mclk_cb = mclk_cb_fn;
- tabla->mclk_freq = mclk_rate;
+ tabla->mbhc_cfg = *cfg;
+ tabla->in_gpio_handler = false;
+ tabla->current_plug = PLUG_TYPE_NONE;
+ tabla->lpi_enabled = false;
tabla_get_mbhc_micbias_regs(codec, &tabla->mbhc_bias_regs);
/* Put CFILT in fast mode by default */
@@ -5396,15 +6049,29 @@
INIT_WORK(&tabla->hphrocp_work, hphrocp_off_report);
INIT_DELAYED_WORK(&tabla->mbhc_insert_dwork, mbhc_insert_work);
- if (!read_fw_bin) {
- tabla->mclk_cb(codec, 1);
+ if (!tabla->mbhc_cfg.read_fw_bin) {
+ tabla->mbhc_cfg.mclk_cb_fn(codec, 1, false);
tabla_mbhc_init(codec);
tabla_mbhc_cal(codec);
tabla_mbhc_calc_thres(codec);
- tabla->mclk_cb(codec, 0);
+ tabla->mbhc_cfg.mclk_cb_fn(codec, 0, false);
tabla_codec_calibrate_hs_polling(codec);
- rc = tabla_codec_enable_hs_detect(codec, 1,
- MBHC_USE_MB_TRIGGER, false);
+ if (!tabla->mbhc_cfg.gpio) {
+ tabla->mbhc_inval_hs_range_override = false;
+ rc = tabla_codec_enable_hs_detect(codec, 1,
+ MBHC_USE_MB_TRIGGER,
+ false);
+ } else {
+ tabla->mbhc_inval_hs_range_override = true;
+ /* Enable Mic Bias pull down and HPH Switch to GND */
+ snd_soc_update_bits(codec,
+ tabla->mbhc_bias_regs.ctl_reg, 0x01,
+ 0x01);
+ snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x01,
+ 0x01);
+ INIT_WORK(&tabla->hs_correct_plug_work,
+ tabla_hs_correct_gpio_plug);
+ }
} else {
schedule_delayed_work(&tabla->mbhc_firmware_dwork,
usecs_to_jiffies(MBHC_FW_READ_TIMEOUT));
@@ -5418,6 +6085,19 @@
TABLA_IRQ_HPH_PA_OCPR_FAULT);
}
+ if (!IS_ERR_VALUE(rc) && tabla->mbhc_cfg.gpio) {
+ rc = request_threaded_irq(tabla->mbhc_cfg.gpio_irq, NULL,
+ tabla_mechanical_plug_detect_irq,
+ (IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING),
+ "tabla-gpio", codec);
+ if (!IS_ERR_VALUE(rc)) {
+ rc = enable_irq_wake(tabla->mbhc_cfg.gpio_irq);
+ /* Bootup time detection */
+ tabla_hs_gpio_handler(codec);
+ }
+ }
+
return rc;
}
EXPORT_SYMBOL_GPL(tabla_hs_detect);
@@ -5812,9 +6492,10 @@
tabla->mbhc_polling_active = false;
tabla->mbhc_fake_ins_start = 0;
tabla->no_mic_headset_override = false;
+ tabla->hs_polling_irq_prepared = false;
+ mutex_init(&tabla->codec_resource_lock);
tabla->codec = codec;
- mutex_init(&tabla->mbhc_mutex);
- tabla->mbhc_state = -1;
+ tabla->mbhc_state = MBHC_STATE_NONE;
tabla->mbhc_last_resume = 0;
for (i = 0; i < COMPANDER_MAX; i++) {
tabla->comp_enabled[i] = 0;
@@ -5890,7 +6571,6 @@
TABLA_IRQ_MBHC_REMOVAL);
goto err_remove_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL,
tabla_dce_handler, "DC Estimation detect", tabla);
@@ -5899,7 +6579,6 @@
TABLA_IRQ_MBHC_POTENTIAL);
goto err_potential_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE,
tabla_release_handler, "Button Release detect", tabla);
@@ -5908,7 +6587,6 @@
TABLA_IRQ_MBHC_RELEASE);
goto err_release_irq;
}
- wcd9xxx_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
ret = wcd9xxx_request_irq(codec->control_data, TABLA_IRQ_SLIMBUS,
tabla_slimbus_irq, "SLIMBUS Slave", tabla);
@@ -5980,7 +6658,7 @@
wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
err_insert_irq:
err_pdata:
- mutex_destroy(&tabla->mbhc_mutex);
+ mutex_destroy(&tabla->codec_resource_lock);
kfree(tabla);
return ret;
}
@@ -5993,15 +6671,15 @@
wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL, tabla);
wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL, tabla);
wcd9xxx_free_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION, tabla);
- mutex_lock(&tabla->mbhc_mutex);
+ TABLA_ACQUIRE_LOCK(tabla->codec_resource_lock);
tabla_codec_disable_clock_block(codec);
- mutex_unlock(&tabla->mbhc_mutex);
+ TABLA_RELEASE_LOCK(tabla->codec_resource_lock);
tabla_codec_enable_bandgap(codec, TABLA_BANDGAP_OFF);
if (tabla->mbhc_fw)
release_firmware(tabla->mbhc_fw);
- mutex_destroy(&tabla->mbhc_mutex);
for (i = 0; i < ARRAY_SIZE(tabla_dai); i++)
kfree(tabla->dai[i].ch_num);
+ mutex_destroy(&tabla->codec_resource_lock);
kfree(tabla);
return 0;
}
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index d1d0c17..6e80284 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -39,7 +39,7 @@
extern const u8 tabla_reg_defaults[TABLA_CACHE_SIZE];
enum tabla_micbias_num {
- TABLA_MICBIAS1,
+ TABLA_MICBIAS1 = 0,
TABLA_MICBIAS2,
TABLA_MICBIAS3,
TABLA_MICBIAS4,
@@ -156,19 +156,37 @@
u16 _beta[3];
} __packed;
+struct tabla_mbhc_config {
+ struct snd_soc_jack *headset_jack;
+ struct snd_soc_jack *button_jack;
+ bool read_fw_bin;
+ /* void* calibration contains:
+ * struct tabla_mbhc_general_cfg generic;
+ * struct tabla_mbhc_plug_detect_cfg plug_det;
+ * struct tabla_mbhc_plug_type_cfg plug_type;
+ * struct tabla_mbhc_btn_detect_cfg btn_det;
+ * struct tabla_mbhc_imped_detect_cfg imped_det;
+ * Note: various size depends on btn_det->num_btn
+ */
+ void *calibration;
+ enum tabla_micbias_num micbias;
+ int (*mclk_cb_fn) (struct snd_soc_codec*, int, bool);
+ unsigned int mclk_rate;
+ unsigned int gpio;
+ unsigned int gpio_irq;
+ int gpio_level_insert;
+};
+
extern int tabla_hs_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *headset_jack,
- struct snd_soc_jack *button_jack,
- void *calibration, enum tabla_micbias_num micbis,
- int (*mclk_cb_fn) (struct snd_soc_codec*, int),
- int read_fw_bin, u32 mclk_rate);
+ const struct tabla_mbhc_config *cfg);
struct anc_header {
u32 reserved[3];
u32 num_anc_slots;
};
-extern int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable);
+extern int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
+ bool dapm);
extern void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg
*btn_det,
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 9a8fdba..3388261 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -87,7 +87,21 @@
static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack;
-static void *tabla_mbhc_cal;
+static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct tabla_mbhc_config mbhc_cfg = {
+ .headset_jack = &hs_jack,
+ .button_jack = &button_jack,
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = TABLA_MICBIAS2,
+ .mclk_cb_fn = msm_enable_codec_ext_clk,
+ .mclk_rate = TABLA_EXT_CLK_RATE,
+ .gpio = 0, /* MBHC GPIO is not configured */
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+};
static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
{
@@ -310,8 +324,8 @@
return 0;
}
-int msm_enable_codec_ext_clk(
- struct snd_soc_codec *codec, int enable)
+static int msm_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
{
pr_debug("%s: enable = %d\n", __func__, enable);
if (enable) {
@@ -323,7 +337,7 @@
if (codec_clk) {
clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
clk_enable(codec_clk);
- tabla_mclk_enable(codec, 1);
+ tabla_mclk_enable(codec, 1, dapm);
} else {
pr_err("%s: Error setting Tabla MCLK\n", __func__);
clk_users--;
@@ -338,7 +352,7 @@
pr_debug("%s: disabling MCLK. clk_users = %d\n",
__func__, clk_users);
clk_disable(codec_clk);
- tabla_mclk_enable(codec, 0);
+ tabla_mclk_enable(codec, 0, dapm);
}
}
return 0;
@@ -361,7 +375,7 @@
if (codec_clk) {
clk_set_rate(codec_clk, 12288000);
clk_enable(codec_clk);
- tabla_mclk_enable(w->codec, 1);
+ tabla_mclk_enable(w->codec, 1, true);
} else {
pr_err("%s: Error setting Tabla MCLK\n", __func__);
@@ -383,7 +397,7 @@
__func__, clk_users);
clk_disable(codec_clk);
- tabla_mclk_enable(w->codec, 0);
+ tabla_mclk_enable(w->codec, 0, true);
}
break;
}
@@ -840,9 +854,7 @@
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
- err = tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
- TABLA_MICBIAS2, msm_enable_codec_ext_clk, 0,
- TABLA_EXT_CLK_RATE);
+ err = tabla_hs_detect(codec, &mbhc_cfg);
return err;
}
@@ -1432,8 +1444,8 @@
return -ENODEV;
}
- tabla_mbhc_cal = def_tabla_mbhc_cal();
- if (!tabla_mbhc_cal) {
+ mbhc_cfg.calibration = def_tabla_mbhc_cal();
+ if (!mbhc_cfg.calibration) {
pr_err("Calibration data allocation failed\n");
return -ENOMEM;
}
@@ -1441,7 +1453,7 @@
msm_snd_device = platform_device_alloc("soc-audio", 0);
if (!msm_snd_device) {
pr_err("Platform device allocation failed\n");
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return -ENOMEM;
}
@@ -1449,7 +1461,7 @@
ret = platform_device_add(msm_snd_device);
if (ret) {
platform_device_put(msm_snd_device);
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return ret;
}
@@ -1472,6 +1484,7 @@
}
msm_free_headset_mic_gpios();
platform_device_unregister(msm_snd_device);
+ kfree(mbhc_cfg.calibration);
}
module_exit(msm_audio_exit);
diff --git a/sound/soc/msm/mdm9615.c b/sound/soc/msm/mdm9615.c
index fff978f..b122f64 100644
--- a/sound/soc/msm/mdm9615.c
+++ b/sound/soc/msm/mdm9615.c
@@ -75,7 +75,20 @@
static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack;
-static void *tabla_mbhc_cal;
+static int mdm9615_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+static struct tabla_mbhc_config mbhc_cfg = {
+ .headset_jack = &hs_jack,
+ .button_jack = &button_jack,
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = TABLA_MICBIAS2,
+ .mclk_cb_fn = mdm9615_enable_codec_ext_clk,
+ .mclk_rate = TABLA_EXT_CLK_RATE,
+ .gpio = 0,
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+};
static void mdm9615_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
{
@@ -296,8 +309,8 @@
}
return 0;
}
-int mdm9615_enable_codec_ext_clk(
- struct snd_soc_codec *codec, int enable)
+static int mdm9615_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
{
pr_debug("%s: enable = %d\n", __func__, enable);
if (enable) {
@@ -309,7 +322,7 @@
if (codec_clk) {
clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
clk_enable(codec_clk);
- tabla_mclk_enable(codec, 1);
+ tabla_mclk_enable(codec, 1, dapm);
} else {
pr_err("%s: Error setting Tabla MCLK\n", __func__);
clk_users--;
@@ -324,7 +337,7 @@
pr_debug("%s: disabling MCLK. clk_users = %d\n",
__func__, clk_users);
clk_disable(codec_clk);
- tabla_mclk_enable(codec, 0);
+ tabla_mclk_enable(codec, 0, dapm);
}
}
return 0;
@@ -337,9 +350,9 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return mdm9615_enable_codec_ext_clk(w->codec, 1);
+ return mdm9615_enable_codec_ext_clk(w->codec, 1, true);
case SND_SOC_DAPM_POST_PMD:
- return mdm9615_enable_codec_ext_clk(w->codec, 0);
+ return mdm9615_enable_codec_ext_clk(w->codec, 0, true);
}
return 0;
}
@@ -742,11 +755,10 @@
return err;
}
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
- tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
- TABLA_MICBIAS2, mdm9615_enable_codec_ext_clk, 0,
- TABLA_EXT_CLK_RATE);
- return 0;
+ err = tabla_hs_detect(codec, &mbhc_cfg);
+
+ return err;
}
static struct snd_soc_dsp_link fe_media = {
@@ -1185,8 +1197,8 @@
return -ENODEV ;
}
- tabla_mbhc_cal = def_tabla_mbhc_cal();
- if (!tabla_mbhc_cal) {
+ mbhc_cfg.calibration = def_tabla_mbhc_cal();
+ if (!mbhc_cfg.calibration) {
pr_err("Calibration data allocation failed\n");
return -ENOMEM;
}
@@ -1194,7 +1206,7 @@
mdm9615_snd_device = platform_device_alloc("soc-audio", 0);
if (!mdm9615_snd_device) {
pr_err("Platform device allocation failed\n");
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return -ENOMEM;
}
@@ -1206,7 +1218,7 @@
ret = platform_device_add(mdm9615_snd_device);
if (ret) {
platform_device_put(mdm9615_snd_device);
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return ret;
}
@@ -1229,7 +1241,7 @@
}
mdm9615_free_headset_mic_gpios();
platform_device_unregister(mdm9615_snd_device);
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
}
module_exit(mdm9615_audio_exit);
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index a8aa05d..0d45e82 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -15,7 +15,6 @@
#include <linux/gpio.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/platform_device.h>
-#include <linux/gpio.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -26,12 +25,14 @@
#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/socinfo.h>
+#include <linux/mfd/wcd9xxx/core.h>
#include "msm-pcm-routing.h"
#include "../codecs/wcd9310.h"
/* 8960 machine driver */
#define PM8921_GPIO_BASE NR_GPIO_IRQS
+#define PM8921_IRQ_BASE (NR_MSM_IRQS + NR_GPIO_IRQS)
#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
#define MSM8960_SPK_ON 1
@@ -58,6 +59,10 @@
#define TABLA_MBHC_DEF_BUTTONS 8
#define TABLA_MBHC_DEF_RLOADS 5
+#define JACK_DETECT_GPIO 38
+#define JACK_DETECT_INT PM8921_GPIO_IRQ(PM8921_IRQ_BASE, JACK_DETECT_GPIO)
+#define GPIO_DETECT_USED false
+
static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
static int msm8960_spk_control;
@@ -77,7 +82,23 @@
static struct snd_soc_jack hs_jack;
static struct snd_soc_jack button_jack;
-static void *tabla_mbhc_cal;
+static int msm8960_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm);
+
+static struct tabla_mbhc_config mbhc_cfg = {
+ .headset_jack = &hs_jack,
+ .button_jack = &button_jack,
+ .read_fw_bin = false,
+ .calibration = NULL,
+ .micbias = TABLA_MICBIAS2,
+ .mclk_cb_fn = msm8960_enable_codec_ext_clk,
+ .mclk_rate = TABLA_EXT_CLK_RATE,
+ .gpio = 0,
+ .gpio_irq = 0,
+ .gpio_level_insert = 1,
+};
+
+static struct mutex cdc_mclk_mutex;
static void msm8960_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
{
@@ -299,38 +320,46 @@
}
return 0;
}
-int msm8960_enable_codec_ext_clk(
- struct snd_soc_codec *codec, int enable)
+
+static int msm8960_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
+ bool dapm)
{
+ int r = 0;
pr_debug("%s: enable = %d\n", __func__, enable);
+
+ mutex_lock(&cdc_mclk_mutex);
if (enable) {
clk_users++;
pr_debug("%s: clk_users = %d\n", __func__, clk_users);
- if (clk_users != 1)
- return 0;
-
- if (codec_clk) {
- clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
- clk_enable(codec_clk);
- tabla_mclk_enable(codec, 1);
- } else {
- pr_err("%s: Error setting Tabla MCLK\n", __func__);
- clk_users--;
- return -EINVAL;
+ if (clk_users == 1) {
+ if (codec_clk) {
+ clk_set_rate(codec_clk, TABLA_EXT_CLK_RATE);
+ clk_enable(codec_clk);
+ tabla_mclk_enable(codec, 1, dapm);
+ } else {
+ pr_err("%s: Error setting Tabla MCLK\n",
+ __func__);
+ clk_users--;
+ r = -EINVAL;
+ }
}
} else {
- pr_debug("%s: clk_users = %d\n", __func__, clk_users);
- if (clk_users == 0)
- return 0;
- clk_users--;
- if (!clk_users) {
- pr_debug("%s: disabling MCLK. clk_users = %d\n",
+ if (clk_users > 0) {
+ clk_users--;
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+ if (clk_users == 0) {
+ pr_debug("%s: disabling MCLK. clk_users = %d\n",
__func__, clk_users);
- clk_disable(codec_clk);
- tabla_mclk_enable(codec, 0);
+ clk_disable(codec_clk);
+ tabla_mclk_enable(codec, 0, dapm);
+ }
+ } else {
+ pr_err("%s: Error releasing Tabla MCLK\n", __func__);
+ r = -EINVAL;
}
}
- return 0;
+ mutex_unlock(&cdc_mclk_mutex);
+ return r;
}
static int msm8960_mclk_event(struct snd_soc_dapm_widget *w,
@@ -340,9 +369,9 @@
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- return msm8960_enable_codec_ext_clk(w->codec, 1);
+ return msm8960_enable_codec_ext_clk(w->codec, 1, true);
case SND_SOC_DAPM_POST_PMD:
- return msm8960_enable_codec_ext_clk(w->codec, 0);
+ return msm8960_enable_codec_ext_clk(w->codec, 0, true);
}
return 0;
}
@@ -707,6 +736,13 @@
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct pm_gpio jack_gpio_cfg = {
+ .direction = PM_GPIO_DIR_IN,
+ .pull = PM_GPIO_PULL_UP_1P5,
+ .function = PM_GPIO_FUNC_NORMAL,
+ .vin_sel = 2,
+ .inv_int_pol = 0,
+ };
pr_debug("%s(), dev_name%s\n", __func__, dev_name(cpu_dai->dev));
@@ -753,9 +789,20 @@
codec_clk = clk_get(cpu_dai->dev, "osr_clk");
- err = tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
- TABLA_MICBIAS2, msm8960_enable_codec_ext_clk, 0,
- TABLA_EXT_CLK_RATE);
+ if (GPIO_DETECT_USED) {
+ mbhc_cfg.gpio = PM8921_GPIO_PM_TO_SYS(JACK_DETECT_GPIO);
+ mbhc_cfg.gpio_irq = JACK_DETECT_INT;
+ }
+
+ if (mbhc_cfg.gpio) {
+ err = pm8xxx_gpio_config(mbhc_cfg.gpio, &jack_gpio_cfg);
+ if (err) {
+ pr_err("%s: pm8xxx_gpio_config failed %d\n", __func__,
+ err);
+ return err;
+ }
+ }
+ err = tabla_hs_detect(codec, &mbhc_cfg);
return err;
}
@@ -1396,8 +1443,8 @@
return -ENODEV ;
}
- tabla_mbhc_cal = def_tabla_mbhc_cal();
- if (!tabla_mbhc_cal) {
+ mbhc_cfg.calibration = def_tabla_mbhc_cal();
+ if (!mbhc_cfg.calibration) {
pr_err("Calibration data allocation failed\n");
return -ENOMEM;
}
@@ -1405,7 +1452,7 @@
msm8960_snd_device = platform_device_alloc("soc-audio", 0);
if (!msm8960_snd_device) {
pr_err("Platform device allocation failed\n");
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return -ENOMEM;
}
@@ -1417,14 +1464,14 @@
ret = platform_device_add(msm8960_snd_device);
if (ret) {
platform_device_put(msm8960_snd_device);
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return ret;
}
msm8960_snd_tabla1x_device = platform_device_alloc("soc-audio", 1);
if (!msm8960_snd_tabla1x_device) {
pr_err("Platform device allocation failed\n");
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return -ENOMEM;
}
@@ -1438,7 +1485,7 @@
ret = platform_device_add(msm8960_snd_tabla1x_device);
if (ret) {
platform_device_put(msm8960_snd_tabla1x_device);
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
return ret;
}
@@ -1448,6 +1495,7 @@
} else
msm8960_headset_gpios_configured = 1;
+ mutex_init(&cdc_mclk_mutex);
return ret;
}
@@ -1462,7 +1510,8 @@
msm8960_free_headset_mic_gpios();
platform_device_unregister(msm8960_snd_device);
platform_device_unregister(msm8960_snd_tabla1x_device);
- kfree(tabla_mbhc_cal);
+ kfree(mbhc_cfg.calibration);
+ mutex_destroy(&cdc_mclk_mutex);
}
module_exit(msm8960_audio_exit);