Merge "msm: smd: Clear Apps SMSM_RESET for Subsytem Restart" into msm-3.0
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 84cd633..cc9a1b7 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -332,6 +332,7 @@
 CONFIG_ION_MSM=y
 CONFIG_MSM_KGSL=y
 CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
+CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=16
 CONFIG_VIDEO_OUTPUT_CONTROL=y
 CONFIG_FB=y
 CONFIG_FB_MSM=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 50b4b08..eb34964 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -332,6 +332,7 @@
 CONFIG_ION_MSM=y
 CONFIG_MSM_KGSL=y
 CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
+CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=16
 CONFIG_VIDEO_OUTPUT_CONTROL=y
 CONFIG_FB=y
 CONFIG_FB_MSM=y
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 9feca99..d8d2e35 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -309,6 +309,7 @@
 CONFIG_ION_MSM=y
 CONFIG_MSM_KGSL=y
 CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
+CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=16
 CONFIG_FB=y
 CONFIG_FB_VIRTUAL=y
 CONFIG_FB_MSM=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 1d506e4..5e90492 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -312,6 +312,7 @@
 CONFIG_ION_MSM=y
 CONFIG_MSM_KGSL=y
 CONFIG_KGSL_PER_PROCESS_PAGE_TABLE=y
+CONFIG_MSM_KGSL_PAGE_TABLE_COUNT=16
 CONFIG_FB=y
 CONFIG_FB_VIRTUAL=y
 CONFIG_FB_MSM=y
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index 76f86d7..738a166 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -266,6 +266,7 @@
 {
 	struct thread_info *thread = current_thread_info();
 	int ret;
+	enum bug_trap_type bug_type = BUG_TRAP_TYPE_NONE;
 
 	oops_enter();
 
@@ -273,7 +274,9 @@
 	console_verbose();
 	bust_spinlocks(1);
 	if (!user_mode(regs))
-		report_bug(regs->ARM_pc, regs);
+		bug_type = report_bug(regs->ARM_pc, regs);
+	if (bug_type != BUG_TRAP_TYPE_NONE)
+		str = "Oops - BUG";
 	ret = __die(str, err, thread, regs);
 
 	if (regs && kexec_should_crash(thread->task))
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index e6d9447..cc6f18f 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -1370,7 +1370,6 @@
 tx_mem_failed:
 	sps_deregister_bam_device(h);
 register_bam_failed:
-	iounmap(a2_virt_addr);
 ioremap_failed:
 	/*destroy_workqueue(bam_mux_workqueue);*/
 	return ret;
@@ -1409,7 +1408,6 @@
 	return 0;
 
 register_bam_failed:
-	iounmap(a2_virt_addr);
 ioremap_failed:
 	return ret;
 }
diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c
index b9ac4b8..e9c41d8 100644
--- a/arch/arm/mach-msm/board-8930-pmic.c
+++ b/arch/arm/mach-msm/board-8930-pmic.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -188,7 +188,7 @@
 
 static struct pm8xxx_pwrkey_platform_data pm8xxx_pwrkey_pdata = {
 	.pull_up		= 1,
-	.kpd_trigger_delay_us	= 970,
+	.kpd_trigger_delay_us	= 15625,
 	.wakeup			= 1,
 };
 
diff --git a/arch/arm/mach-msm/board-8930-regulator.c b/arch/arm/mach-msm/board-8930-regulator.c
index d3e19a0..f383f31 100644
--- a/arch/arm/mach-msm/board-8930-regulator.c
+++ b/arch/arm/mach-msm/board-8930-regulator.c
@@ -50,12 +50,14 @@
 };
 VREG_CONSUMERS(L9) = {
 	REGULATOR_SUPPLY("8038_l9",		NULL),
+	REGULATOR_SUPPLY("vdd_ana",		"3-004a"),
 };
 VREG_CONSUMERS(L10) = {
 	REGULATOR_SUPPLY("8038_l10",		NULL),
 };
 VREG_CONSUMERS(L11) = {
 	REGULATOR_SUPPLY("8038_l11",		NULL),
+	REGULATOR_SUPPLY("vdd_dig",		"3-004a"),
 };
 VREG_CONSUMERS(L12) = {
 	REGULATOR_SUPPLY("8038_l12",		NULL),
@@ -128,6 +130,7 @@
 };
 VREG_CONSUMERS(LVS2) = {
 	REGULATOR_SUPPLY("8038_lvs2",		NULL),
+	REGULATOR_SUPPLY("vcc_i2c",		"3-004a"),
 };
 VREG_CONSUMERS(EXT_5V) = {
 	REGULATOR_SUPPLY("ext_5v",		NULL),
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index fb4cc52..1b4b999 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -1338,6 +1338,9 @@
 	.x_size			= 1067,
 	.y_size			= 566,
 	.irqflags		= IRQF_TRIGGER_FALLING,
+#ifdef MSM8930_PHASE_2
+	.digital_pwr_regulator	= true,
+#endif
 	.i2c_pull_up		= true,
 	.reset_gpio		= MXT_TS_RESET_GPIO,
 	.irq_gpio		= MXT_TS_GPIO_IRQ,
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index 71713829..0dfa1c6 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -84,7 +84,7 @@
 	{
 		.func = GPIOMUX_FUNC_GPIO,
 		.drv = GPIOMUX_DRV_2MA,
-		.pull = GPIOMUX_PULL_NONE,
+		.pull = GPIOMUX_PULL_DOWN,
 	},
 	/* Active state */
 	{
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index f6212af..f36c3a1 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -499,7 +499,7 @@
 	RPM_SMPS(S1,	 1, 1, 0, 1225000, 1225000, NULL, 100000, 3p20),
 	RPM_SMPS(S2,	 0, 1, 0, 1300000, 1300000, NULL, 0,	  1p60),
 	RPM_SMPS(S3,	 0, 1, 1,  500000, 1150000, NULL, 100000, 4p80),
-	RPM_SMPS(S4,	 1, 1, 0, 1800000, 1800000, NULL, 100000, 3p20),
+	RPM_SMPS(S4,	 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60),
 	RPM_SMPS(S7,	 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20),
 	RPM_SMPS(S8,	 1, 1, 1, 2100000, 2100000, NULL, 100000, 1p60),
 
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 02659b2..3881019 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -1362,6 +1362,8 @@
 	/* T15 Object */
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0,
+	/* T18 Object */
+	0, 0,
 	/* T22 Object */
 	5, 0, 0, 0, 0, 0, 0, 0, 30, 0,
 	0, 0, 5, 8, 10, 13, 0,
diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c
index 0fb64dc..cdb43ad 100644
--- a/arch/arm/mach-msm/devices-msm7x27.c
+++ b/arch/arm/mach-msm/devices-msm7x27.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2008 Google, Inc.
- * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -18,6 +18,7 @@
 #include <linux/msm_kgsl.h>
 #include <linux/regulator/machine.h>
 #include <linux/dma-mapping.h>
+#include <linux/init.h>
 #include <asm/clkdev.h>
 #include <mach/irqs.h>
 #include <mach/msm_iomap.h>
@@ -839,3 +840,29 @@
 	FS_PCOM(FS_GFX3D,  "fs_gfx3d"),
 };
 unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
+
+static struct resource gpio_resources[] = {
+	{
+		.start  = INT_GPIO_GROUP1,
+		.flags  = IORESOURCE_IRQ,
+	},
+	{
+		.start  = INT_GPIO_GROUP2,
+		.flags  = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device msm_device_gpio = {
+	.name	   = "msmgpio",
+	.id	     = -1,
+	.resource       = gpio_resources,
+	.num_resources  = ARRAY_SIZE(gpio_resources),
+};
+
+static int __init msm7627_init_gpio(void)
+{
+	platform_device_register(&msm_device_gpio);
+	return 0;
+}
+
+postcore_initcall(msm7627_init_gpio);
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 8a090f8..917dfc7 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/msm_kgsl.h>
 #include <linux/regulator/machine.h>
+#include <linux/init.h>
 #include <mach/irqs.h>
 #include <mach/msm_iomap.h>
 #include <mach/board.h>
@@ -696,6 +697,31 @@
 	.id     = 0,
 };
 
+static struct resource gpio_resources[] = {
+	{
+		.start	= INT_GPIO_GROUP1,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.start	= INT_GPIO_GROUP2,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device msm_device_gpio = {
+	.name		= "msmgpio",
+	.id		= -1,
+	.resource	= gpio_resources,
+	.num_resources	= ARRAY_SIZE(gpio_resources),
+};
+
+static int msm7627a_init_gpio(void)
+{
+	platform_device_register(&msm_device_gpio);
+	return 0;
+}
+postcore_initcall(msm7627a_init_gpio);
+
 int __init msm7x2x_misc_init(void)
 {
 	msm_clock_init(&msm7x27a_clock_init_data);
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index 2c21f57..db656f3 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2008 Google, Inc.
- * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -20,6 +20,7 @@
 #include <linux/msm_kgsl.h>
 #include <linux/android_pmem.h>
 #include <linux/regulator/machine.h>
+#include <linux/init.h>
 #include <mach/irqs.h>
 #include <mach/msm_iomap.h>
 #include <mach/dma.h>
@@ -1187,3 +1188,29 @@
 	FS_PCOM(FS_VPE,    "fs_vpe"),
 };
 unsigned msm_num_footswitch_devices = ARRAY_SIZE(msm_footswitch_devices);
+
+static struct resource gpio_resources[] = {
+	{
+		.start	= INT_GPIO_GROUP1,
+		.flags	= IORESOURCE_IRQ,
+	},
+	{
+		.start	= INT_GPIO_GROUP2,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device msm_device_gpio = {
+	.name		= "msmgpio",
+	.id		= -1,
+	.resource	= gpio_resources,
+	.num_resources	= ARRAY_SIZE(gpio_resources),
+};
+
+static int __init msm7630_init_gpio(void)
+{
+	platform_device_register(&msm_device_gpio);
+	return 0;
+}
+
+postcore_initcall(msm7630_init_gpio);
diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c
index a942862..ea9e0d5 100644
--- a/arch/arm/mach-msm/gpio.c
+++ b/arch/arm/mach-msm/gpio.c
@@ -1,7 +1,7 @@
 /* linux/arch/arm/mach-msm/gpio.c
  *
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -20,6 +20,7 @@
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <asm/mach/irq.h>
 #include <mach/gpiomux.h>
 #include "gpio_hw.h"
@@ -486,37 +487,6 @@
 	}
 }
 
-static int __init msm_init_gpio(void)
-{
-	int i, j = 0;
-
-	for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
-		if (i - FIRST_GPIO_IRQ >=
-			msm_gpio_chips[j].chip.base +
-			msm_gpio_chips[j].chip.ngpio)
-			j++;
-		irq_set_chip_data(i, &msm_gpio_chips[j]);
-		irq_set_chip_and_handler(i, &msm_gpio_irq_chip,
-					 handle_edge_irq);
-		set_irq_flags(i, IRQF_VALID);
-	}
-
-	irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler);
-	irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler);
-
-	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
-		spin_lock_init(&msm_gpio_chips[i].lock);
-		__raw_writel(0, msm_gpio_chips[i].regs.int_en);
-		gpiochip_add(&msm_gpio_chips[i].chip);
-	}
-
-	mb();
-	irq_set_irq_wake(INT_GPIO_GROUP1, 1);
-	irq_set_irq_wake(INT_GPIO_GROUP2, 2);
-	return 0;
-}
-
-postcore_initcall(msm_init_gpio);
 
 int gpio_tlmm_config(unsigned config, unsigned disable)
 {
@@ -643,3 +613,52 @@
 	*out = msm_chip->regs.out;
 	*offset = gpio - msm_chip->chip.base;
 }
+
+static int __devinit msm_gpio_probe(struct platform_device *dev)
+{
+	int i, j = 0;
+	int grp_irq;
+
+	for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) {
+		if (i - FIRST_GPIO_IRQ >=
+			msm_gpio_chips[j].chip.base +
+			msm_gpio_chips[j].chip.ngpio)
+			j++;
+		irq_set_chip_data(i, &msm_gpio_chips[j]);
+		irq_set_chip_and_handler(i, &msm_gpio_irq_chip,
+					 handle_edge_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+
+	for (i = 0; i < dev->num_resources; i++) {
+		grp_irq = platform_get_irq(dev, i);
+		if (grp_irq < 0)
+			return -ENXIO;
+
+		irq_set_chained_handler(grp_irq, msm_gpio_irq_handler);
+		irq_set_irq_wake(grp_irq, (i + 1));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) {
+		spin_lock_init(&msm_gpio_chips[i].lock);
+		__raw_writel(0, msm_gpio_chips[i].regs.int_en);
+		gpiochip_add(&msm_gpio_chips[i].chip);
+	}
+
+	mb();
+	return 0;
+}
+
+static struct platform_driver msm_gpio_driver = {
+	.probe = msm_gpio_probe,
+	.driver = {
+		.name = "msmgpio",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init msm_gpio_init(void)
+{
+	return platform_driver_register(&msm_gpio_driver);
+}
+postcore_initcall(msm_gpio_init);
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 6dfb354..8085b82 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -27,6 +27,7 @@
 
 #include "pm.h"
 #include "scm-boot.h"
+#include "spm.h"
 
 int pen_release = -1;
 
@@ -90,6 +91,8 @@
 	if (!base_ptr)
 		return -ENODEV;
 
+	msm_spm_turn_on_cpu_rail(cpu);
+
 	writel_relaxed(0x109, base_ptr+0x04);
 	writel_relaxed(0x101, base_ptr+0x04);
 	ndelay(300);
diff --git a/arch/arm/mach-msm/spm.h b/arch/arm/mach-msm/spm.h
index 5825bcf..78fd922 100644
--- a/arch/arm/mach-msm/spm.h
+++ b/arch/arm/mach-msm/spm.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011 Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -110,6 +110,7 @@
 void msm_spm_reinit(void);
 void msm_spm_allow_x_cpu_set_vdd(bool allowed);
 int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs);
+int msm_spm_turn_on_cpu_rail(unsigned int cpu);
 
 #if defined(CONFIG_MSM_L2_SPM)
 int msm_spm_l2_set_low_power_mode(unsigned int mode, bool notify_rpm);
@@ -148,6 +149,10 @@
 	/* empty */
 }
 
+static inline int msm_spm_turn_on_cpu_rail(unsigned int cpu)
+{
+	return -ENOSYS;
+}
 #endif  /*defined(CONFIG_MSM_SPM_V1) || defined (CONFIG_MSM_SPM_V2) */
 
 #endif  /* __ARCH_ARM_MACH_MSM_SPM_H */
diff --git a/arch/arm/mach-msm/spm_devices.c b/arch/arm/mach-msm/spm_devices.c
index 6ea9327..326faef 100644
--- a/arch/arm/mach-msm/spm_devices.c
+++ b/arch/arm/mach-msm/spm_devices.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -162,6 +162,40 @@
 	return ret;
 }
 
+int msm_spm_turn_on_cpu_rail(unsigned int cpu)
+{
+	uint32_t val = 0;
+	uint32_t timeout = 0;
+	void *reg = NULL;
+
+	if (cpu >= num_possible_cpus())
+		return -EINVAL;
+
+	switch (cpu) {
+	case 1:
+		reg = MSM_SAW1_BASE;
+		break;
+	case 0:
+	default:
+		return -EFAULT;
+	}
+
+	if (cpu_is_msm8960() || cpu_is_msm8930()) {
+		val = 0xB0;
+		reg += 0x14;
+		timeout = 512;
+	} else {
+		return -ENOSYS;
+	}
+
+	writel_relaxed(val, reg);
+	mb();
+	udelay(timeout);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_spm_turn_on_cpu_rail);
+
 #if defined(CONFIG_MSM_L2_SPM)
 static struct msm_spm_device msm_spm_l2_device;
 
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index f1d40ef..c13abff 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -2,7 +2,7 @@
  * drivers/gpu/ion/ion_carveout_heap.c
  *
  * Copyright (C) 2011 Google, Inc.
- * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -401,6 +401,7 @@
 	ret = gen_pool_add(carveout_heap->pool, carveout_heap->base,
 			heap_data->size, -1);
 	if (ret < 0) {
+		gen_pool_destroy(carveout_heap->pool);
 		kfree(carveout_heap);
 		return ERR_PTR(-EINVAL);
 	}
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 630de1d..2fa0b7a 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -509,7 +509,6 @@
 void mdp4_overlay_lcdc_wait4vsync(struct msm_fb_data_type *mfd);
 void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
 				struct mdp4_overlay_pipe *pipe);
-void mdp4_overlay_lcdc_set_perf(struct msm_fb_data_type *mfd);
 void mdp4_update_perf_level(u32 perf_level);
 void mdp4_set_perf_level(void);
 void mdp4_mddi_overlay_dmas_restore(void);
@@ -620,7 +619,6 @@
 void mdp4_dsi_blt_dmap_busy_wait(struct msm_fb_data_type *mfd);
 void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd,
 				struct mdp4_overlay_pipe *pipe);
-void mdp4_overlay_dsi_video_set_perf(struct msm_fb_data_type *mfd);
 #else
 static inline void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
 {
@@ -635,11 +633,6 @@
 {
 	/* empty */
 }
-static inline void mdp4_overlay_dsi_video_set_perf(
-				struct msm_fb_data_type *mfd)
-{
-	/* empty */
-}
 #endif /* MIPI_DSI */
 
 void mdp4_dsi_cmd_kickoff_ui(struct msm_fb_data_type *mfd,
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index a53102a..51dd440 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -2205,18 +2205,21 @@
 		mdp4_overlay_dtv_set(mfd, pipe);
 
 	if (new_perf_level != perf_level) {
+		u32 old_level = new_perf_level;
 		mdp4_update_perf_level(perf_level);
 
 		/* change clck base on perf level */
 		if (pipe->mixer_num == MDP4_MIXER0) {
 			if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
-				mdp4_overlay_dsi_video_set_perf(mfd);
+				if (old_level > perf_level)
+					mdp4_set_perf_level();
 			} else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD) {
 				mdp4_dsi_cmd_dma_busy_wait(mfd);
 				mdp4_dsi_blt_dmap_busy_wait(mfd);
 				mdp4_set_perf_level();
 			} else if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
-				mdp4_overlay_lcdc_set_perf(mfd);
+				if (old_level > perf_level)
+					mdp4_set_perf_level();
 			} else if (ctrl->panel_mode & MDP4_PANEL_MDDI) {
 				mdp4_mddi_dma_busy_wait(mfd);
 				mdp4_set_perf_level();
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index c5d1b0c..88582e4 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -465,13 +465,6 @@
 	pr_debug("%s: done pid=%d\n", __func__, current->pid);
 }
 
-void mdp4_overlay_dsi_video_set_perf(struct msm_fb_data_type *mfd)
-{
-	mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
-	/* change mdp clk while mdp is idle */
-	mdp4_set_perf_level();
-}
-
 void mdp4_overlay_dsi_video_vsync_push(struct msm_fb_data_type *mfd,
 			struct mdp4_overlay_pipe *pipe)
 {
@@ -499,9 +492,10 @@
 		mb();
 		mdp4_overlay_dsi_video_wait4event(mfd, INTR_DMA_P_DONE);
 	} else {
-
 		mdp4_overlay_dsi_video_wait4event(mfd, INTR_PRIMARY_VSYNC);
 	}
+
+	mdp4_set_perf_level();
 }
 
 /*
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index 592febe..f2065fd 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -358,6 +358,12 @@
 
 	mdp4_overlay_dmae_xy(pipe);	/* dma_e */
 	mdp4_overlayproc_cfg(pipe);
+
+	if (pipe->pipe_type == OVERLAY_TYPE_RGB) {
+		pipe->srcp0_addr = (uint32) mfd->ibuf.buf;
+		mdp4_overlay_rgb_setup(pipe);
+	}
+
 	mdp4_mixer_stage_up(pipe);
 
 	dtv_pipe = pipe; /* keep it */
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index bef988c..a905ec0 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -385,13 +385,6 @@
 	pr_debug("%s: done pid=%d\n", __func__, current->pid);
 }
 
-void mdp4_overlay_lcdc_set_perf(struct msm_fb_data_type *mfd)
-{
-	mdp4_overlay_lcdc_wait4event(mfd, INTR_DMA_P_DONE);
-	/* change mdp clk while mdp is idle */
-	mdp4_set_perf_level();
-}
-
 void mdp4_overlay_lcdc_vsync_push(struct msm_fb_data_type *mfd,
 			struct mdp4_overlay_pipe *pipe)
 {
@@ -421,6 +414,7 @@
 	} else {
 		mdp4_overlay_lcdc_wait4event(mfd, INTR_PRIMARY_VSYNC);
 	}
+	mdp4_set_perf_level();
 }
 
 /*
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
index cc35ea3..8eba8bd 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_interrupt_handler.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
 #include "vcd_ddl.h"
 #include "vcd_ddl_shared_mem.h"
 #include "vcd_ddl_metadata.h"
+#include "vcd_res_tracker_api.h"
 #include <linux/delay.h>
 
 static void ddl_decoder_input_done_callback(
@@ -254,6 +255,13 @@
 		DDL_MSG_LOW("HEADER_DONE");
 		vidc_1080p_get_decode_seq_start_result(&seq_hdr_info);
 		parse_hdr_size_data(ddl, &seq_hdr_info);
+		if (res_trk_get_disable_fullhd() &&
+			(seq_hdr_info.img_size_x * seq_hdr_info.img_size_y >
+				1280 * 720)) {
+			DDL_MSG_ERROR("FATAL:Resolution greater than 720P HD");
+			ddl_client_fatal_cb(ddl);
+			return process_further;
+		}
 		if (!seq_hdr_info.img_size_x || !seq_hdr_info.img_size_y) {
 			DDL_MSG_ERROR("FATAL:ZeroImageSize");
 			ddl_client_fatal_cb(ddl);
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 3a86afc..4f22706 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -441,6 +441,8 @@
 			}
 			resource_context.disable_dmx =
 			resource_context.vidc_platform_data->disable_dmx;
+			resource_context.disable_fullhd =
+			resource_context.vidc_platform_data->disable_fullhd;
 #ifdef CONFIG_MSM_BUS_SCALING
 			resource_context.vidc_bus_client_pdata =
 			resource_context.vidc_platform_data->
@@ -513,3 +515,8 @@
 	resource_context.res_mem_type = mem_type;
 	return;
 }
+
+u32 res_trk_get_disable_fullhd(void)
+{
+	return resource_context.disable_fullhd;
+}
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
index 7283991..be5045f 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -50,6 +50,7 @@
 	struct ddl_buf_addr firmware_addr;
 	struct ion_client *res_ion_client;
 	u32 disable_dmx;
+	u32 disable_fullhd;
 	enum ddl_mem_area res_mem_type;
 };
 
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
index 47fa363..fd4ca3e 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -31,6 +31,7 @@
 u32 res_trk_get_firmware_addr(struct ddl_buf_addr *firm_addr);
 u32 res_trk_get_mem_type(void);
 u32 res_trk_get_enable_ion(void);
+u32 res_trk_get_disable_fullhd(void);
 struct ion_client *res_trk_get_ion_client(void);
 u32 res_trk_get_disable_dmx(void);
 void res_trk_set_mem_type(enum ddl_mem_area mem_type);
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
index 0172427..ee76ff1 100644
--- a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -723,3 +723,8 @@
 {
 	return;
 }
+
+u32 res_trk_get_disable_fullhd(void)
+{
+	return 0;
+}
diff --git a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
index fbc91e2..34f2103 100644
--- a/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
+++ b/drivers/video/msm/vidc/720p/resource_tracker/vcd_res_tracker_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -28,6 +28,7 @@
 u32 res_trk_download_firmware(void);
 u32 res_trk_get_core_type(void);
 u32 res_trk_get_mem_type(void);
+u32 res_trk_get_disable_fullhd(void);
 u32 res_trk_get_enable_ion(void);
 struct ion_client *res_trk_get_ion_client(void);
 void res_trk_set_mem_type(enum ddl_mem_area mem_type);
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index 77affcd..bb3e652 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -2812,7 +2812,12 @@
 	u32 rc;
 	u32 frm_p_units;
 	(void)frm_size;
-
+	if (res_trk_get_disable_fullhd() && frm_size &&
+		(frm_size->width * frm_size->height > 1280 * 720)) {
+		VCD_MSG_ERROR("Frame size = %dX%d greater than 1280X720 not"
+			"supported", frm_size->width, frm_size->height);
+		return VCD_ERR_FAIL;
+	}
 	prop_hdr.prop_id = DDL_I_FRAME_PROC_UNITS;
 	prop_hdr.sz = sizeof(frm_p_units);
 	rc = ddl_get_property(cctxt->ddl_handle, &prop_hdr, &frm_p_units);
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index 8d0ca45..97f1ca7 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -134,7 +134,6 @@
 
 extern void get_online_cpus(void);
 extern void put_online_cpus(void);
-extern bool cpu_hotplug_inprogress(void);
 #define hotcpu_notifier(fn, pri)	cpu_notifier(fn, pri)
 #define register_hotcpu_notifier(nb)	register_cpu_notifier(nb)
 #define unregister_hotcpu_notifier(nb)	unregister_cpu_notifier(nb)
diff --git a/kernel/cpu.c b/kernel/cpu.c
index ad7eaf2..4047707 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -123,14 +123,6 @@
 	mutex_unlock(&cpu_hotplug.lock);
 }
 
-bool cpu_hotplug_inprogress(void)
-{
-	if (cpu_hotplug.active_writer)
-		return true;
-
-	return false;
-}
-
 #else /* #if CONFIG_HOTPLUG_CPU */
 static void cpu_hotplug_begin(void) {}
 static void cpu_hotplug_done(void) {}
diff --git a/kernel/printk.c b/kernel/printk.c
index b790764..dbaa948 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -813,11 +813,6 @@
  */
 static inline int can_use_console(unsigned int cpu)
 {
-#ifdef CONFIG_HOTPLUG_CPU
-	if (!cpu_active(cpu) && cpu_hotplug_inprogress())
-		return 0;
-#endif
-
 	return cpu_online(cpu) || have_callable_console();
 }
 
diff --git a/kernel/sched.c b/kernel/sched.c
index 232c1c0..b85f675 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -7653,7 +7653,6 @@
 {
 	int i, j, n;
 	int new_topology;
-	cpumask_var_t doms_temp;
 
 	mutex_lock(&sched_domains_mutex);
 
@@ -7663,20 +7662,14 @@
 	/* Let architecture update cpu core mappings. */
 	new_topology = arch_update_cpu_topology();
 
-	cpumask_andnot(doms_temp, cpu_active_mask, cpu_isolated_map);
-
 	n = doms_new ? ndoms_new : 0;
 
 	/* Destroy deleted domains */
 	for (i = 0; i < ndoms_cur; i++) {
-		if (!new_topology) {
-			if ((n == 0) && cpumask_subset(doms_cur[i], doms_temp))
+		for (j = 0; j < n && !new_topology; j++) {
+			if (cpumask_equal(doms_cur[i], doms_new[j])
+			    && dattrs_equal(dattr_cur, i, dattr_new, j))
 				goto match1;
-			for (j = 0; j < n; j++) {
-				if (cpumask_equal(doms_cur[i], doms_new[j])
-				    && dattrs_equal(dattr_cur, i, dattr_new, j))
-					goto match1;
-			}
 		}
 		/* no match - a current sched domain not in new doms_new[] */
 		detach_destroy_domains(doms_cur[i]);
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 33afdc9..deef8ac 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -48,6 +48,11 @@
 
 #define TABLA_OCP_ATTEMPT 1
 
+#define TABLA_MCLK_RATE_12288KHZ 12288000
+#define TABLA_MCLK_RATE_9600KHZ 9600000
+
+#define TABLA_FAKE_INS_THRESHOLD_MS 2500
+
 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);
@@ -93,8 +98,31 @@
 	TABLA_HPHR_DAC_OFF_ACK
 };
 
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+	u16 dce_z;
+	u16 dce_mb;
+	u16 sta_z;
+	u16 sta_mb;
+	u32 t_dce;
+	u32 t_sta;
+	u32 micb_mv;
+	u16 v_ins_hu;
+	u16 v_ins_h;
+	u16 v_b1_hu;
+	u16 v_b1_h;
+	u16 v_b1_huc;
+	u16 v_brh;
+	u16 v_brl;
+	u16 v_no_mic;
+	u8 nready;
+	u8 npoll;
+	u8 nbounce_wait;
+};
+
 struct tabla_priv {
 	struct snd_soc_codec *codec;
+	u32 mclk_freq;
 	u32 adc_count;
 	u32 cfilt1_cnt;
 	u32 cfilt2_cnt;
@@ -105,10 +133,20 @@
 	bool clock_active;
 	bool config_mode_active;
 	bool mbhc_polling_active;
-	bool fake_insert_context;
+	unsigned long mbhc_fake_ins_start;
 	int buttons_pressed;
 
-	struct tabla_mbhc_calibration *calibration;
+	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;
+	struct mbhc_internal_cal_data mbhc_data;
 
 	struct snd_soc_jack *headset_jack;
 	struct snd_soc_jack *button_jack;
@@ -145,6 +183,9 @@
 
 	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);
 };
 
 #ifdef CONFIG_DEBUG_FS
@@ -1409,7 +1450,7 @@
 		if (tabla->mbhc_polling_active) {
 
 			tabla_codec_pause_hs_polling(codec);
-			/* Enable Mic Bias switch to VDDIO */
+			/* VDDIO switch enabled */
 			tabla->cfilt_k_value = snd_soc_read(codec,
 					tabla->mbhc_bias_regs.cfilt_val);
 			cfilt_k_val = tabla_find_k_value(
@@ -1425,8 +1466,7 @@
 			tabla_codec_start_hs_polling(codec);
 
 			tabla->mbhc_micbias_switched = true;
-			pr_debug("%s: Enabled MBHC Mic bias to VDDIO Switch\n",
-				__func__);
+			pr_debug("%s: VDDIO switch enabled\n", __func__);
 		}
 		break;
 
@@ -1436,7 +1476,7 @@
 				tabla_codec_pause_hs_polling(codec);
 				mbhc_was_polling = true;
 			}
-			/* Disable Mic Bias switch to VDDIO */
+			/* VDDIO switch disabled */
 			if (tabla->cfilt_k_value != 0)
 				snd_soc_update_bits(codec,
 					tabla->mbhc_bias_regs.cfilt_val, 0XFC,
@@ -1450,8 +1490,7 @@
 				tabla_codec_start_hs_polling(codec);
 
 			tabla->mbhc_micbias_switched = false;
-			pr_debug("%s: Disabled MBHC Mic bias to VDDIO Switch\n",
-				__func__);
+			pr_debug("%s: VDDIO switch disabled\n", __func__);
 		}
 		break;
 	}
@@ -1516,7 +1555,7 @@
 		break;
 	case SND_SOC_DAPM_POST_PMU:
 		if (tabla->mbhc_polling_active &&
-			(tabla->calibration->bias == micb_line)) {
+		    tabla->micbias == micb_line) {
 			tabla_codec_pause_hs_polling(codec);
 			tabla_codec_start_hs_polling(codec);
 		}
@@ -1673,10 +1712,8 @@
 			tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
 						  tabla->hph_status,
 						  TABLA_JACK_MASK);
-		snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
-		0x00);
-		snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
-		0x10);
+		snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x00);
+		snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10, 0x10);
 		/* reset retry counter as PA is turned off signifying
 		 * start of new OCP detection session
 		 */
@@ -1761,10 +1798,9 @@
 		struct mbhc_micbias_regs *micbias_regs)
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	struct tabla_mbhc_calibration *calibration = tabla->calibration;
 	unsigned int cfilt;
 
-	switch (calibration->bias) {
+	switch (tabla->micbias) {
 	case TABLA_MICBIAS1:
 		cfilt = tabla->pdata->micbias.bias1_cfilt_sel;
 		micbias_regs->mbhc_reg = TABLA_A_MICB_1_MBHC;
@@ -1801,14 +1837,17 @@
 	case TABLA_CFILT1_SEL:
 		micbias_regs->cfilt_val = TABLA_A_MICB_CFILT_1_VAL;
 		micbias_regs->cfilt_ctl = TABLA_A_MICB_CFILT_1_CTL;
+		tabla->mbhc_data.micb_mv = tabla->pdata->micbias.cfilt1_mv;
 		break;
 	case TABLA_CFILT2_SEL:
 		micbias_regs->cfilt_val = TABLA_A_MICB_CFILT_2_VAL;
 		micbias_regs->cfilt_ctl = TABLA_A_MICB_CFILT_2_CTL;
+		tabla->mbhc_data.micb_mv = tabla->pdata->micbias.cfilt2_mv;
 		break;
 	case TABLA_CFILT3_SEL:
 		micbias_regs->cfilt_val = TABLA_A_MICB_CFILT_3_VAL;
 		micbias_regs->cfilt_ctl = TABLA_A_MICB_CFILT_3_CTL;
+		tabla->mbhc_data.micb_mv = tabla->pdata->micbias.cfilt3_mv;
 		break;
 	}
 }
@@ -2612,24 +2651,46 @@
 
 static void tabla_codec_calibrate_hs_polling(struct snd_soc_codec *codec)
 {
-	/* TODO store register values in calibration */
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B5_CTL, 0x20);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B6_CTL, 0xFF);
+	u8 *n_cic;
+	struct tabla_mbhc_btn_detect_cfg *btn_det;
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B10_CTL, 0xFF);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B9_CTL, 0x20);
+	btn_det = TABLA_MBHC_CAL_BTN_DET_PTR(tabla->calibration);
 
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL, 0xF8);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL, 0xEE);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL, 0xFC);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL, 0xCE);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B1_CTL,
+		      tabla->mbhc_data.v_ins_hu & 0xFF);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B2_CTL,
+		      (tabla->mbhc_data.v_ins_hu >> 8) & 0xFF);
 
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 3);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL, 9);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL, 30);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 120);
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL, 0x78, 0x58);
-	snd_soc_write(codec, TABLA_A_CDC_MBHC_B2_CTL, 11);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B3_CTL,
+		      tabla->mbhc_data.v_b1_hu & 0xFF);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B4_CTL,
+		      (tabla->mbhc_data.v_b1_hu >> 8) & 0xFF);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B5_CTL,
+		      tabla->mbhc_data.v_b1_h & 0xFF);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B6_CTL,
+		      (tabla->mbhc_data.v_b1_h >> 8) & 0xFF);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B9_CTL,
+		      tabla->mbhc_data.v_brh & 0xFF);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B10_CTL,
+		      (tabla->mbhc_data.v_brh >> 8) & 0xFF);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B11_CTL,
+		      tabla->mbhc_data.v_brl & 0xFF);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_VOLT_B12_CTL,
+		      (tabla->mbhc_data.v_brl >> 8) & 0xFF);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B1_CTL,
+		      tabla->mbhc_data.nready);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B2_CTL,
+		      tabla->mbhc_data.npoll);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B3_CTL,
+		      tabla->mbhc_data.nbounce_wait);
+
+	n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, n_cic[0]);
 }
 
 static int tabla_startup(struct snd_pcm_substream *substream,
@@ -2956,42 +3017,47 @@
 	return bias_value;
 }
 
-static short tabla_codec_measure_micbias_voltage(struct snd_soc_codec *codec,
-	int dce)
+static short tabla_codec_sta_dce(struct snd_soc_codec *codec, int dce)
 {
+	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
 	short bias_value;
 
+	/* Turn on the override */
+	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);
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
 		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x4);
-		usleep_range(60000, 60000);
+		usleep_range(tabla->mbhc_data.t_dce,
+			     tabla->mbhc_data.t_dce);
 		bias_value = tabla_codec_read_dce_result(codec);
 	} else {
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
-		usleep_range(5000, 5000);
-		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
 		usleep_range(50, 50);
+		snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x2);
+		usleep_range(tabla->mbhc_data.t_sta,
+			     tabla->mbhc_data.t_sta);
 		bias_value = tabla_codec_read_sta_result(codec);
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 		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);
 
-	pr_debug("read microphone bias value %x\n", bias_value);
+	pr_debug("read microphone bias value %04x\n", bias_value);
 	return bias_value;
 }
 
 static short tabla_codec_setup_hs_polling(struct snd_soc_codec *codec)
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	struct tabla_mbhc_calibration *calibration = tabla->calibration;
 	short bias_value;
 	u8 cfilt_mode;
 
-	if (!calibration) {
+	if (!tabla->calibration) {
 		pr_err("Error, no tabla calibration\n");
 		return -ENODEV;
 	}
@@ -3009,13 +3075,10 @@
 	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);
+	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);
 
-	snd_soc_update_bits(codec,
-		tabla->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
+	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x1F, 0x16);
 
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
 	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
@@ -3028,14 +3091,14 @@
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
 
-	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x6, 0x6);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
 
 	tabla_codec_calibrate_hs_polling(codec);
 
-	bias_value = tabla_codec_measure_micbias_voltage(codec, 0);
-	snd_soc_update_bits(codec,
-		tabla->mbhc_bias_regs.cfilt_ctl, 0x40, cfilt_mode);
+	bias_value = tabla_codec_sta_dce(codec, 0);
+	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);
 
 	return bias_value;
@@ -3045,11 +3108,14 @@
 		int insertion)
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	struct tabla_mbhc_calibration *calibration = tabla->calibration;
 	int central_bias_enabled = 0;
+	const struct tabla_mbhc_general_cfg *generic =
+	    TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
+	const struct tabla_mbhc_plug_detect_cfg *plug_det =
+	    TABLA_MBHC_CAL_PLUG_DET_PTR(tabla->calibration);
 	u8 wg_time;
 
-	if (!calibration) {
+	if (!tabla->calibration) {
 		pr_err("Error, no tabla calibration\n");
 		return -EINVAL;
 	}
@@ -3070,7 +3136,7 @@
 		/* Enable HPH Schmitt Trigger */
 		snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x11, 0x11);
 		snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x0C,
-			calibration->hph_current << 2);
+				    plug_det->hph_current << 2);
 
 		/* Turn off HPH PAs and DAC's during insertion detection to
 		 * avoid false insertion interrupts
@@ -3079,9 +3145,9 @@
 			tabla_codec_switch_micbias(codec, 0);
 		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);
+				    0xC0, 0x00);
 		snd_soc_update_bits(codec, TABLA_A_RX_HPH_R_DAC_CTL,
-			0xC0, 0x00);
+				    0xC0, 0x00);
 		usleep_range(wg_time * 1000, wg_time * 1000);
 
 		/* setup for insetion detection */
@@ -3093,10 +3159,10 @@
 
 		/* enable the mic line schmitt trigger */
 		snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x60,
-			calibration->mic_current << 5);
+				    plug_det->mic_current << 5);
 		snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
 			0x80, 0x80);
-		usleep_range(calibration->mic_pid, calibration->mic_pid);
+		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
 		snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg,
 			0x10, 0x10);
 
@@ -3109,8 +3175,8 @@
 			tabla_codec_enable_config_mode(codec, 1);
 			snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
 				0x06, 0);
-			usleep_range(calibration->shutdown_plug_removal,
-				calibration->shutdown_plug_removal);
+			usleep_range(generic->t_shutdown_plug_rem,
+				     generic->t_shutdown_plug_rem);
 			tabla_codec_enable_config_mode(codec, 0);
 		} else
 			snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL,
@@ -3122,8 +3188,8 @@
 	/* If central bandgap disabled */
 	if (!(snd_soc_read(codec, TABLA_A_PIN_CTL_OE1) & 1)) {
 		snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE1, 0x3, 0x3);
-		usleep_range(calibration->bg_fast_settle,
-			calibration->bg_fast_settle);
+		usleep_range(generic->t_bg_fast_settle,
+			     generic->t_bg_fast_settle);
 		central_bias_enabled = 1;
 	}
 
@@ -3131,14 +3197,14 @@
 	if (snd_soc_read(codec, TABLA_A_PIN_CTL_OE0) & 0x80) {
 		snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x10, 0);
 		snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x80, 0x80);
-		usleep_range(calibration->tldoh, calibration->tldoh);
+		usleep_range(generic->t_ldoh, generic->t_ldoh);
 		snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE0, 0x80, 0);
 
 		if (central_bias_enabled)
 			snd_soc_update_bits(codec, TABLA_A_PIN_CTL_OE1, 0x1, 0);
 	}
 
-	snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x3, calibration->bias);
+	snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x3, tabla->micbias);
 
 	tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
@@ -3163,10 +3229,60 @@
 	wake_up(&tabla->pm_wq);
 }
 
+static u16 tabla_codec_v_sta_dce(struct snd_soc_codec *codec, bool dce,
+				 s16 vin_mv)
+{
+	short diff, zero;
+	struct tabla_priv *tabla;
+	u32 mb_mv, in;
+
+	tabla = snd_soc_codec_get_drvdata(codec);
+	mb_mv = tabla->mbhc_data.micb_mv;
+
+	if (mb_mv == 0) {
+		pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dce) {
+		diff = tabla->mbhc_data.dce_mb - tabla->mbhc_data.dce_z;
+		zero = tabla->mbhc_data.dce_z;
+	} else {
+		diff = tabla->mbhc_data.sta_mb - tabla->mbhc_data.sta_z;
+		zero = tabla->mbhc_data.sta_z;
+	}
+	in = (u32) diff * vin_mv;
+
+	return (u16) (in / mb_mv) + zero;
+}
+
+static s32 tabla_codec_sta_dce_v(struct snd_soc_codec *codec, s8 dce,
+				 u16 bias_value)
+{
+	struct tabla_priv *tabla;
+	s32 mv;
+
+	tabla = snd_soc_codec_get_drvdata(codec);
+
+	if (dce) {
+		mv = ((s32)bias_value - (s32)tabla->mbhc_data.dce_z) *
+		     (s32)tabla->mbhc_data.micb_mv /
+		     (s32)(tabla->mbhc_data.dce_mb - tabla->mbhc_data.dce_z);
+	} else {
+		mv = ((s32)bias_value - (s32)tabla->mbhc_data.sta_z) *
+		     (s32)tabla->mbhc_data.micb_mv /
+		     (s32)(tabla->mbhc_data.sta_mb - tabla->mbhc_data.sta_z);
+	}
+
+	return mv;
+}
+
 static void btn0_lpress_fn(struct work_struct *work)
 {
 	struct delayed_work *delayed_work;
 	struct tabla_priv *tabla;
+	short bias_value;
+	int dce_mv, sta_mv;
 
 	pr_debug("%s:\n", __func__);
 
@@ -3175,8 +3291,15 @@
 
 	if (tabla) {
 		if (tabla->button_jack) {
-			pr_debug("%s: Reporting long button press event\n",
-					__func__);
+			bias_value = tabla_codec_read_sta_result(tabla->codec);
+			sta_mv = tabla_codec_sta_dce_v(tabla->codec, 0,
+						       bias_value);
+			bias_value = tabla_codec_read_dce_result(tabla->codec);
+			dce_mv = tabla_codec_sta_dce_v(tabla->codec, 1,
+						       bias_value);
+			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,
 						  SND_JACK_BTN_0,
 						  SND_JACK_BTN_0);
@@ -3188,9 +3311,246 @@
 	tabla_unlock_sleep(tabla);
 }
 
+void tabla_mbhc_cal(struct snd_soc_codec *codec)
+{
+	struct tabla_priv *tabla;
+	struct tabla_mbhc_btn_detect_cfg *btn_det;
+	u8 cfilt_mode, bg_mode;
+	u8 ncic, nmeas, navg;
+	u32 mclk_rate;
+	u32 dce_wait, sta_wait;
+	u8 *n_cic;
+
+	tabla = snd_soc_codec_get_drvdata(codec);
+
+	/* 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);
+	n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
+	ncic = n_cic[0];
+	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;
+	dce_wait = (1000 * 512 * ncic * nmeas) / (mclk_rate / 1000);
+	if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ)
+		dce_wait = dce_wait + 10000;
+	else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ)
+		dce_wait = dce_wait + 9810;
+	else
+		WARN(1, "Unsupported mclk freq %d\n", tabla->mclk_freq);
+
+	sta_wait = (1000 * 128 * navg) / (mclk_rate / 1000);
+
+	/* Add 10 microseconds to handle error margin */
+	dce_wait = dce_wait + 10;
+	sta_wait = sta_wait + 10;
+
+	tabla->mbhc_data.t_dce = dce_wait;
+	tabla->mbhc_data.t_sta = sta_wait;
+
+	/* LDOH and CFILT are already configured during pdata handling.
+	 * Only need to make sure CFILT and bandgap are in Fast mode.
+	 * Need to restore defaults once calculation is done.
+	 */
+	cfilt_mode = snd_soc_read(codec, tabla->mbhc_bias_regs.cfilt_ctl);
+	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x40, 0x00);
+	bg_mode = snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x02,
+				      0x02);
+
+	/* Micbias, CFILT, LDOH, MBHC MUX mode settings
+	 * to perform ADC calibration
+	 */
+	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.ctl_reg, 0x60,
+			    tabla->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);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x04);
+
+	/* DCE measurement for 0 volts */
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x81);
+	usleep_range(100, 100);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(tabla->mbhc_data.t_dce, tabla->mbhc_data.t_dce);
+	tabla->mbhc_data.dce_z = tabla_codec_read_dce_result(codec);
+
+	/* DCE measurment for MB voltage */
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x82);
+	usleep_range(100, 100);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(tabla->mbhc_data.t_dce, tabla->mbhc_data.t_dce);
+	tabla->mbhc_data.dce_mb = tabla_codec_read_dce_result(codec);
+
+	/* Sta measuremnt for 0 volts */
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x81);
+	usleep_range(100, 100);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(tabla->mbhc_data.t_sta, tabla->mbhc_data.t_sta);
+	tabla->mbhc_data.sta_z = tabla_codec_read_sta_result(codec);
+
+	/* STA Measurement for MB Voltage */
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x82);
+	usleep_range(100, 100);
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(tabla->mbhc_data.t_sta, tabla->mbhc_data.t_sta);
+	tabla->mbhc_data.sta_mb = tabla_codec_read_sta_result(codec);
+
+	/* Restore default settings. */
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.cfilt_ctl, 0x40,
+			    cfilt_mode);
+	snd_soc_update_bits(codec, TABLA_A_BIAS_CENTRAL_BG_CTL, 0x02, bg_mode);
+
+	snd_soc_write(codec, TABLA_A_MBHC_SCALING_MUX_1, 0x84);
+	usleep_range(100, 100);
+}
+
+void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg* btn_det,
+				const enum tabla_mbhc_btn_det_mem mem)
+{
+	void *ret = &btn_det->_v_btn_low;
+
+	switch (mem) {
+	case TABLA_BTN_DET_GAIN:
+		ret += sizeof(btn_det->_n_cic);
+	case TABLA_BTN_DET_N_CIC:
+		ret += sizeof(btn_det->_n_ready);
+	case TABLA_BTN_DET_V_N_READY:
+		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+	case TABLA_BTN_DET_V_BTN_HIGH:
+		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+	case TABLA_BTN_DET_V_BTN_LOW:
+		/* do nothing */
+		break;
+	default:
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+static void tabla_mbhc_calc_thres(struct snd_soc_codec *codec)
+{
+	struct tabla_priv *tabla;
+	s16 btn_mv = 0, btn_delta_mv;
+	struct tabla_mbhc_btn_detect_cfg *btn_det;
+	struct tabla_mbhc_plug_type_cfg *plug_type;
+	u16 *btn_high;
+	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);
+
+	if (tabla->mclk_freq == TABLA_MCLK_RATE_12288KHZ) {
+		tabla->mbhc_data.nready = 3;
+		tabla->mbhc_data.npoll = 9;
+		tabla->mbhc_data.nbounce_wait = 30;
+	} else if (tabla->mclk_freq == TABLA_MCLK_RATE_9600KHZ) {
+		tabla->mbhc_data.nready = 2;
+		tabla->mbhc_data.npoll = 7;
+		tabla->mbhc_data.nbounce_wait = 23;
+	}
+
+	tabla->mbhc_data.v_ins_hu =
+	    tabla_codec_v_sta_dce(codec, STA, plug_type->v_hs_max);
+	tabla->mbhc_data.v_ins_h =
+	    tabla_codec_v_sta_dce(codec, DCE, plug_type->v_hs_max);
+
+	btn_high = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_V_BTN_HIGH);
+	for (i = 0; i < btn_det->num_btn; i++)
+		btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+	tabla->mbhc_data.v_b1_h = tabla_codec_v_sta_dce(codec, DCE, btn_mv);
+	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_sta;
+
+	tabla->mbhc_data.v_b1_hu =
+	    tabla_codec_v_sta_dce(codec, STA, btn_delta_mv);
+
+	btn_delta_mv = btn_mv + btn_det->v_btn_press_delta_cic;
+
+	tabla->mbhc_data.v_b1_huc =
+	    tabla_codec_v_sta_dce(codec, DCE, btn_delta_mv);
+
+	tabla->mbhc_data.v_brh = tabla->mbhc_data.v_b1_h;
+	tabla->mbhc_data.v_brl = 0xFA55;
+
+	tabla->mbhc_data.v_no_mic =
+	    tabla_codec_v_sta_dce(codec, STA, plug_type->v_no_mic);
+}
+
+void tabla_mbhc_init(struct snd_soc_codec *codec)
+{
+	struct tabla_priv *tabla;
+	struct tabla_mbhc_general_cfg *generic;
+	struct tabla_mbhc_btn_detect_cfg *btn_det;
+	int n;
+	u8 tabla_ver;
+	u8 *n_cic, *gain;
+
+	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);
+
+	tabla_ver = snd_soc_read(codec, TABLA_A_CHIP_VERSION);
+	tabla_ver &= 0x1F;
+
+	for (n = 0; n < 8; n++) {
+		if ((tabla_ver != TABLA_VERSION_1_0 &&
+		     tabla_ver != TABLA_VERSION_1_1) || n != 7) {
+			snd_soc_update_bits(codec,
+					    TABLA_A_CDC_MBHC_FEATURE_B1_CFG,
+					    0x07, n);
+			snd_soc_write(codec, TABLA_A_CDC_MBHC_FEATURE_B2_CFG,
+				      btn_det->c[n]);
+		}
+	}
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x07,
+			    btn_det->nc);
+
+	n_cic = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_N_CIC);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B6_CTL, 0xFF,
+			    n_cic[0]);
+
+	gain = tabla_mbhc_cal_btn_det_mp(btn_det, TABLA_BTN_DET_GAIN);
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B2_CTL, 0x78, gain[0] << 3);
+
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+			    generic->mbhc_nsa << 4);
+
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+			    btn_det->n_meas);
+
+	snd_soc_write(codec, TABLA_A_CDC_MBHC_TIMER_B5_CTL, generic->mbhc_navg);
+
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x78,
+			    btn_det->mbhc_nsc << 3);
+
+	snd_soc_update_bits(codec, TABLA_A_MICB_4_MBHC, 0x03, TABLA_MICBIAS2);
+
+	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+}
+
 int tabla_hs_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *headset_jack, struct snd_soc_jack *button_jack,
-	struct tabla_mbhc_calibration *calibration)
+		    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)
 {
 	struct tabla_priv *tabla;
 	int rc;
@@ -3202,7 +3562,10 @@
 	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_get_mbhc_micbias_regs(codec, &tabla->mbhc_bias_regs);
 
 	/* Put CFILT in fast mode by default */
@@ -3212,7 +3575,19 @@
 	INIT_DELAYED_WORK(&tabla->btn0_dwork, btn0_lpress_fn);
 	INIT_WORK(&tabla->hphlocp_work, hphlocp_off_report);
 	INIT_WORK(&tabla->hphrocp_work, hphrocp_off_report);
-	rc =  tabla_codec_enable_hs_detect(codec, 1);
+
+	if (!read_fw_bin) {
+		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);
+	} else {
+		pr_err("%s: MBHC firmware read not supported\n", __func__);
+		rc = -EINVAL;
+	}
 
 	if (!IS_ERR_VALUE(rc)) {
 		snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
@@ -3238,12 +3613,14 @@
 	tabla_lock_sleep(priv);
 
 	bias_value = tabla_codec_read_dce_result(codec);
-	pr_debug("%s: button press interrupt, bias value(DCE Read)=%d\n",
-			__func__, bias_value);
+	pr_debug("%s: button press interrupt, DCE: %d,%d\n",
+		 __func__, bias_value,
+		 tabla_codec_sta_dce_v(codec, 1, bias_value));
 
 	bias_value = tabla_codec_read_sta_result(codec);
-	pr_debug("%s: button press interrupt, bias value(STA Read)=%d\n",
-			__func__, bias_value);
+	pr_debug("%s: button press interrupt, STA: %d,%d\n",
+		 __func__, bias_value,
+		 tabla_codec_sta_dce_v(codec, 0, bias_value));
 	/*
 	 * TODO: If button pressed is not button 0,
 	 * report the button press event immediately.
@@ -3265,54 +3642,44 @@
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
-	int ret, mic_voltage;
+	int ret, mb_v;
 
 	pr_debug("%s\n", __func__);
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
 	tabla_lock_sleep(priv);
 
-	mic_voltage = tabla_codec_read_dce_result(codec);
-	pr_debug("%s: Microphone Voltage on release(DCE Read) = %d\n",
-		__func__, mic_voltage);
-
 	if (priv->buttons_pressed & SND_JACK_BTN_0) {
 		ret = cancel_delayed_work(&priv->btn0_dwork);
 
 		if (ret == 0) {
-
 			pr_debug("%s: Reporting long button release event\n",
 					__func__);
-			if (priv->button_jack) {
+			if (priv->button_jack)
 				tabla_snd_soc_jack_report(priv,
 							  priv->button_jack, 0,
 							  SND_JACK_BTN_0);
-			}
-
 		} else {
 			/* if scheduled btn0_dwork is canceled from here,
 			 * we have to unlock from here instead btn0_work */
 			tabla_unlock_sleep(priv);
-			mic_voltage =
-				tabla_codec_measure_micbias_voltage(codec, 0);
-			pr_debug("%s: Mic Voltage on release(new STA) = %d\n",
-						__func__, mic_voltage);
+			mb_v = tabla_codec_sta_dce(codec, 0);
+			pr_debug("%s: Mic Voltage on release STA: %d,%d\n",
+				 __func__, mb_v,
+				 tabla_codec_sta_dce_v(codec, 0, mb_v));
 
-			if (mic_voltage < -2000 || mic_voltage > -670) {
+			if (mb_v < -2000 || mb_v > -670)
 				pr_debug("%s: Fake buttton press interrupt\n",
 						__func__);
-			} else {
-
-				if (priv->button_jack) {
-					pr_debug("%s:reporting short button press and release\n",
-							__func__);
-
-					tabla_snd_soc_jack_report(priv,
-							      priv->button_jack,
-						SND_JACK_BTN_0, SND_JACK_BTN_0);
-					tabla_snd_soc_jack_report(priv,
-							     priv->button_jack,
-							     0, SND_JACK_BTN_0);
-				}
+			else if (priv->button_jack) {
+				pr_debug("%s:reporting short button "
+					 "press and release\n", __func__);
+				tabla_snd_soc_jack_report(priv,
+							  priv->button_jack,
+							  SND_JACK_BTN_0,
+							  SND_JACK_BTN_0);
+				tabla_snd_soc_jack_report(priv,
+							  priv->button_jack,
+							  0, SND_JACK_BTN_0);
 			}
 		}
 
@@ -3327,7 +3694,8 @@
 static void tabla_codec_shutdown_hs_removal_detect(struct snd_soc_codec *codec)
 {
 	struct tabla_priv *tabla = snd_soc_codec_get_drvdata(codec);
-	struct tabla_mbhc_calibration *calibration = tabla->calibration;
+	const struct tabla_mbhc_general_cfg *generic =
+	    TABLA_MBHC_CAL_GENERAL_PTR(tabla->calibration);
 
 	if (!tabla->mclk_enabled && !tabla->mbhc_polling_active)
 		tabla_codec_enable_config_mode(codec, 1);
@@ -3335,10 +3703,10 @@
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
 
-	snd_soc_update_bits(codec,
-		tabla->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
-	usleep_range(calibration->shutdown_plug_removal,
-		calibration->shutdown_plug_removal);
+	snd_soc_update_bits(codec, tabla->mbhc_bias_regs.mbhc_reg, 0x80, 0x00);
+
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem);
 
 	snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
 	if (!tabla->mclk_enabled && !tabla->mbhc_polling_active)
@@ -3461,13 +3829,14 @@
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	const struct tabla_mbhc_plug_detect_cfg *plug_det =
+	    TABLA_MBHC_CAL_PLUG_DET_PTR(priv->calibration);
 	int ldo_h_on, micb_cfilt_on;
-	short mic_voltage;
-	short threshold_no_mic = 0xF7F6;
-	short threshold_fake_insert = 0xFD30;
+	short mb_v;
 	u8 is_removal;
+	int mic_mv;
 
-	pr_debug("%s\n", __func__);
+	pr_debug("%s: enter\n", __func__);
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
 	tabla_lock_sleep(priv);
 
@@ -3475,38 +3844,40 @@
 	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, priv->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
 	snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x13, 0x00);
 
-	if (priv->fake_insert_context) {
+	if (priv->mbhc_fake_ins_start &&
+	    time_after(jiffies, priv->mbhc_fake_ins_start +
+		       msecs_to_jiffies(TABLA_FAKE_INS_THRESHOLD_MS))) {
 		pr_debug("%s: fake context interrupt, reset insertion\n",
-			__func__);
-		priv->fake_insert_context = false;
+			 __func__);
+		priv->mbhc_fake_ins_start = 0;
 		tabla_codec_shutdown_hs_polling(codec);
 		tabla_codec_enable_hs_detect(codec, 1);
 		return IRQ_HANDLED;
 	}
 
-
 	ldo_h_on = snd_soc_read(codec, TABLA_A_LDO_H_MODE_1) & 0x80;
-	micb_cfilt_on = snd_soc_read(codec,
-					priv->mbhc_bias_regs.cfilt_ctl) & 0x80;
+	micb_cfilt_on = snd_soc_read(codec, priv->mbhc_bias_regs.cfilt_ctl)
+			    & 0x80;
 
 	if (!ldo_h_on)
 		snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x80, 0x80);
 	if (!micb_cfilt_on)
 		snd_soc_update_bits(codec, priv->mbhc_bias_regs.cfilt_ctl,
-							0x80, 0x80);
-
-	usleep_range(priv->calibration->setup_plug_removal_delay,
-		priv->calibration->setup_plug_removal_delay);
+				    0x80, 0x80);
+	if (plug_det->t_ins_complete > 20)
+		msleep(plug_det->t_ins_complete);
+	else
+		usleep_range(plug_det->t_ins_complete * 1000,
+			     plug_det->t_ins_complete * 1000);
 
 	if (!ldo_h_on)
 		snd_soc_update_bits(codec, TABLA_A_LDO_H_MODE_1, 0x80, 0x0);
 	if (!micb_cfilt_on)
 		snd_soc_update_bits(codec, priv->mbhc_bias_regs.cfilt_ctl,
-							0x80, 0x0);
+				    0x80, 0x0);
 
 	if (is_removal) {
 		/*
@@ -3543,37 +3914,56 @@
 		return IRQ_HANDLED;
 	}
 
-	mic_voltage = tabla_codec_setup_hs_polling(codec);
+	mb_v = tabla_codec_setup_hs_polling(codec);
+	mic_mv = tabla_codec_sta_dce_v(codec, 0, mb_v);
 
-	if (mic_voltage > threshold_fake_insert) {
-		pr_debug("%s: Fake insertion interrupt, mic_voltage = %x\n",
-			__func__, mic_voltage);
-
-		/* Disable HPH trigger and enable MIC line trigger */
-		snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x12, 0x00);
-
-		snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg, 0x60,
-			priv->calibration->mic_current << 5);
-		snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg,
-			0x80, 0x80);
-		usleep_range(priv->calibration->mic_pid,
-			priv->calibration->mic_pid);
-		snd_soc_update_bits(codec, priv->mbhc_bias_regs.mbhc_reg,
-			0x10, 0x10);
-
+	if (mb_v > (short) priv->mbhc_data.v_ins_hu) {
+		pr_debug("%s: Fake insertion interrupt since %dmsec ago, "
+			 "STA : %d,%d\n", __func__,
+			 (priv->mbhc_fake_ins_start ?
+			     jiffies_to_msecs(jiffies -
+					      priv->mbhc_fake_ins_start) :
+			     0),
+			 mb_v, mic_mv);
+		if (time_after(jiffies,
+			       priv->mbhc_fake_ins_start +
+			       msecs_to_jiffies(TABLA_FAKE_INS_THRESHOLD_MS))) {
+			/* Disable HPH trigger and enable MIC line trigger */
+			snd_soc_update_bits(codec, TABLA_A_MBHC_HPH, 0x12,
+					    0x00);
+			snd_soc_update_bits(codec,
+					    priv->mbhc_bias_regs.mbhc_reg, 0x60,
+					    plug_det->mic_current << 5);
+			snd_soc_update_bits(codec,
+					    priv->mbhc_bias_regs.mbhc_reg,
+					    0x80, 0x80);
+			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid);
+			snd_soc_update_bits(codec,
+					    priv->mbhc_bias_regs.mbhc_reg,
+					    0x10, 0x10);
+		} else {
+			if (priv->mbhc_fake_ins_start == 0)
+				priv->mbhc_fake_ins_start = jiffies;
+			/* Setup normal insert detection
+			 * Enable HPH Schmitt Trigger
+			 */
+			snd_soc_update_bits(codec, TABLA_A_MBHC_HPH,
+					    0x13 | 0x0C,
+					    0x13 | plug_det->hph_current << 2);
+		}
 		/* Setup for insertion detection */
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x2, 0);
-		priv->fake_insert_context = true;
 		tabla_enable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
 		snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
 
-	} else if (mic_voltage < threshold_no_mic) {
-		pr_debug("%s: Headphone Detected, mic_voltage = %x\n",
-			__func__, mic_voltage);
+	} else if (mb_v < (short) priv->mbhc_data.v_no_mic) {
+		pr_debug("%s: Headphone Detected, mb_v: %d,%d\n",
+			 __func__, mb_v, mic_mv);
+		priv->mbhc_fake_ins_start = 0;
 		priv->hph_status |= SND_JACK_HEADPHONE;
 		if (priv->headset_jack) {
 			pr_debug("%s: Reporting insertion %d\n", __func__,
-				SND_JACK_HEADPHONE);
+				 SND_JACK_HEADPHONE);
 			tabla_snd_soc_jack_report(priv, priv->headset_jack,
 						  priv->hph_status,
 						  TABLA_JACK_MASK);
@@ -3582,16 +3972,19 @@
 		tabla_codec_enable_hs_detect(codec, 0);
 		tabla_sync_hph_state(priv);
 	} else {
-		pr_debug("%s: Headset detected, mic_voltage = %x\n",
-			__func__, mic_voltage);
+		pr_debug("%s: Headset detected, mb_v: %d,%d\n",
+			__func__, mb_v, mic_mv);
+		priv->mbhc_fake_ins_start = 0;
 		priv->hph_status |= SND_JACK_HEADSET;
 		if (priv->headset_jack) {
 			pr_debug("%s: Reporting insertion %d\n", __func__,
-				SND_JACK_HEADSET);
+				 SND_JACK_HEADSET);
 			tabla_snd_soc_jack_report(priv, priv->headset_jack,
 						  priv->hph_status,
 						  TABLA_JACK_MASK);
 		}
+		/* avoid false button press detect */
+		msleep(50);
 		tabla_codec_start_hs_polling(codec);
 		tabla_sync_hph_state(priv);
 	}
@@ -3604,6 +3997,8 @@
 {
 	struct tabla_priv *priv = data;
 	struct snd_soc_codec *codec = priv->codec;
+	const struct tabla_mbhc_general_cfg *generic =
+	    TABLA_MBHC_CAL_GENERAL_PTR(priv->calibration);
 	short bias_value;
 
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
@@ -3611,13 +4006,14 @@
 	tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
 	tabla_lock_sleep(priv);
 
-	usleep_range(priv->calibration->shutdown_plug_removal,
-		priv->calibration->shutdown_plug_removal);
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem);
 
-	bias_value = tabla_codec_measure_micbias_voltage(codec, 1);
-	pr_debug("removal interrupt, bias value is %d\n", bias_value);
+	bias_value = tabla_codec_sta_dce(codec, 1);
+	pr_debug("removal interrupt, DCE: %d,%d\n",
+		 bias_value, tabla_codec_sta_dce_v(codec, 1, bias_value));
 
-	if (bias_value < -90) {
+	if (bias_value < (short) priv->mbhc_data.v_ins_h) {
 		pr_debug("False alarm, headset not actually removed\n");
 		tabla_codec_start_hs_polling(codec);
 	} else {
@@ -3940,6 +4336,11 @@
 	tabla->cfilt_k_value = 0;
 	tabla->mbhc_micbias_switched = false;
 
+	/* Make sure mbhc intenal calibration data is zeroed out */
+	memset(&tabla->mbhc_data, 0,
+		sizeof(struct mbhc_internal_cal_data));
+	tabla->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+	tabla->mbhc_data.t_sta = DEFAULT_STA_WAIT;
 	snd_soc_codec_set_drvdata(codec, tabla);
 
 	tabla->mclk_enabled = false;
@@ -3947,7 +4348,7 @@
 	tabla->clock_active = false;
 	tabla->config_mode_active = false;
 	tabla->mbhc_polling_active = false;
-	tabla->fake_insert_context = false;
+	tabla->mbhc_fake_ins_start = 0;
 	tabla->no_mic_headset_override = false;
 	tabla->codec = codec;
 	tabla->pdata = dev_get_platdata(codec->dev->parent);
@@ -3981,7 +4382,8 @@
 	snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 
 	tabla_version = snd_soc_read(codec, TABLA_A_CHIP_VERSION);
-	pr_info("%s : Tabla version reg 0x%2x\n", __func__, (u32)tabla_version);
+	pr_info("%s : Tabla version reg 0x%2x\n", __func__,
+			(u32)tabla_version);
 
 	tabla_version &=  0x1F;
 	pr_info("%s : Tabla version %u\n", __func__, (u32)tabla_version);
diff --git a/sound/soc/codecs/wcd9310.h b/sound/soc/codecs/wcd9310.h
index 32fc48f..66c3e39 100644
--- a/sound/soc/codecs/wcd9310.h
+++ b/sound/soc/codecs/wcd9310.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,7 +9,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
 #include <sound/soc.h>
 
 #define TABLA_VERSION_1_0	0
@@ -22,6 +21,13 @@
 
 #define TABLA_REG_VAL(reg, val)		{reg, 0, val}
 
+
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define STA 0
+#define DCE 1
+
 extern const u8 tabla_reg_readable[TABLA_CACHE_SIZE];
 extern const u8 tabla_reg_defaults[TABLA_CACHE_SIZE];
 
@@ -39,26 +45,116 @@
 	TABLA_PID_MIC_20_UA,
 };
 
-struct tabla_mbhc_calibration {
-	enum tabla_micbias_num bias;
-	int tldoh;
-	int bg_fast_settle;
-	enum tabla_pid_current mic_current;
-	int mic_pid;
-	enum tabla_pid_current hph_current;
-	int setup_plug_removal_delay;
-	int shutdown_plug_removal;
-};
-
 struct tabla_reg_mask_val {
 	u16	reg;
 	u8	mask;
 	u8	val;
 };
 
+enum tabla_mbhc_clk_freq {
+	TABLA_MCLK_12P2MHZ = 0,
+	TABLA_MCLK_9P6MHZ,
+	TABLA_NUM_CLK_FREQS,
+};
+
+enum tabla_mbhc_analog_pwr_cfg {
+	TABLA_ANALOG_PWR_COLLAPSED = 0,
+	TABLA_ANALOG_PWR_ON,
+	TABLA_NUM_ANALOG_PWR_CONFIGS,
+};
+
+enum tabla_mbhc_btn_det_mem {
+	TABLA_BTN_DET_V_BTN_LOW,
+	TABLA_BTN_DET_V_BTN_HIGH,
+	TABLA_BTN_DET_V_N_READY,
+	TABLA_BTN_DET_N_CIC,
+	TABLA_BTN_DET_GAIN
+};
+
+struct tabla_mbhc_general_cfg {
+	u8 t_ldoh;
+	u8 t_bg_fast_settle;
+	u8 t_shutdown_plug_rem;
+	u8 mbhc_nsa;
+	u8 mbhc_navg;
+	u8 v_micbias_l;
+	u8 v_micbias;
+	u8 mbhc_reserved;
+	u16 settle_wait;
+	u16 t_micbias_rampup;
+	u16 t_micbias_rampdown;
+	u16 t_supply_bringup;
+} __packed;
+
+struct tabla_mbhc_plug_detect_cfg {
+	u32 mic_current;
+	u32 hph_current;
+	u16 t_mic_pid;
+	u16 t_ins_complete;
+	u16 t_ins_retry;
+	u16 v_removal_delta;
+	u8 micbias_slow_ramp;
+	u8 reserved0;
+	u8 reserved1;
+	u8 reserved2;
+} __packed;
+
+struct tabla_mbhc_plug_type_cfg {
+	u8 av_detect;
+	u8 mono_detect;
+	u8 num_ins_tries;
+	u8 reserved0;
+	s16 v_no_mic;
+	s16 v_av_min;
+	s16 v_av_max;
+	s16 v_hs_min;
+	s16 v_hs_max;
+	u16 reserved1;
+} __packed;
+
+
+struct tabla_mbhc_btn_detect_cfg {
+	s8 c[8];
+	u8 nc;
+	u8 n_meas;
+	u8 mbhc_nsc;
+	u8 n_btn_meas;
+	u8 n_btn_con;
+	u8 num_btn;
+	u8 reserved0;
+	u8 reserved1;
+	u16 t_poll;
+	u16 t_bounce_wait;
+	u16 t_rel_timeout;
+	s16 v_btn_press_delta_sta;
+	s16 v_btn_press_delta_cic;
+	u16 t_btn0_timeout;
+	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+	u8 _n_ready[TABLA_NUM_CLK_FREQS];
+	u8 _n_cic[TABLA_NUM_CLK_FREQS];
+	u8 _gain[TABLA_NUM_CLK_FREQS];
+} __packed;
+
+struct tabla_mbhc_imped_detect_cfg {
+	u8 _hs_imped_detect;
+	u8 _n_rload;
+	u8 _hph_keep_on;
+	u8 _repeat_rload_calc;
+	u16 _t_dac_ramp_time;
+	u16 _rhph_high;
+	u16 _rhph_low;
+	u16 _rload[0]; /* rload[n_rload] */
+	u16 _alpha[0]; /* alpha[n_rload] */
+	u16 _beta[3];
+} __packed;
+
 extern int tabla_hs_detect(struct snd_soc_codec *codec,
-	struct snd_soc_jack *headset_jack, struct snd_soc_jack *button_jack,
-	struct tabla_mbhc_calibration *calibration);
+			   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);
 
 struct anc_header {
 	u32 reserved[3];
@@ -66,3 +162,38 @@
 };
 
 extern int tabla_mclk_enable(struct snd_soc_codec *codec, int mclk_enable);
+
+extern void *tabla_mbhc_cal_btn_det_mp(const struct tabla_mbhc_btn_detect_cfg
+				       *btn_det,
+				       const enum tabla_mbhc_btn_det_mem mem);
+
+#define TABLA_MBHC_CAL_SIZE(buttons, rload) ( \
+	sizeof(enum tabla_micbias_num) + \
+	sizeof(struct tabla_mbhc_general_cfg) + \
+	sizeof(struct tabla_mbhc_plug_detect_cfg) + \
+	    ((sizeof(s16) + sizeof(s16)) * buttons) + \
+	sizeof(struct tabla_mbhc_plug_type_cfg) + \
+	sizeof(struct tabla_mbhc_btn_detect_cfg) + \
+	sizeof(struct tabla_mbhc_imped_detect_cfg) + \
+	    ((sizeof(u16) + sizeof(u16)) * rload) \
+	)
+
+#define TABLA_MBHC_CAL_GENERAL_PTR(cali) ( \
+	    (struct tabla_mbhc_general_cfg *) cali)
+#define TABLA_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+	    (struct tabla_mbhc_plug_detect_cfg *) \
+	    &(TABLA_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define TABLA_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+	    (struct tabla_mbhc_plug_type_cfg *) \
+	    &(TABLA_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define TABLA_MBHC_CAL_BTN_DET_PTR(cali) ( \
+	    (struct tabla_mbhc_btn_detect_cfg *) \
+	    &(TABLA_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define TABLA_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+	    (struct tabla_mbhc_imped_detect_cfg *) \
+	    (((void *)&TABLA_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+	     (TABLA_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+	      (sizeof(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+	       sizeof(TABLA_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+	)
+
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index 870bd20..bd41e0b 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
@@ -52,6 +53,11 @@
 #define GPIO_AUX_PCM_SYNC 45
 #define GPIO_AUX_PCM_CLK 46
 
+#define TABLA_EXT_CLK_RATE 12288000
+
+#define TABLA_MBHC_DEF_BUTTONS 3
+#define TABLA_MBHC_DEF_RLOADS 5
+
 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 msm_spk_control;
@@ -63,17 +69,6 @@
 static int msm_btsco_rate = BTSCO_RATE_8KHZ;
 static int msm_btsco_ch = 1;
 
-struct tabla_mbhc_calibration tabla_calib = {
-	.bias = TABLA_MICBIAS2,
-	.tldoh = 100,
-	.bg_fast_settle = 100,
-	.mic_current = TABLA_PID_MIC_5_UA,
-	.mic_pid = 100,
-	.hph_current = TABLA_PID_MIC_5_UA,
-	.setup_plug_removal_delay = 1000000,
-	.shutdown_plug_removal = 100000,
-};
-
 static struct clk *codec_clk;
 static int clk_users;
 
@@ -82,6 +77,8 @@
 static struct snd_soc_jack hs_jack;
 static struct snd_soc_jack button_jack;
 
+static void *tabla_mbhc_cal;
+
 static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
 {
 	int ret = 0;
@@ -303,6 +300,42 @@
 	return 0;
 }
 
+int msm_enable_codec_ext_clk(
+		struct snd_soc_codec *codec, int enable)
+{
+	pr_debug("%s: enable = %d\n", __func__, enable);
+	if (enable) {
+		clk_users++;
+		pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+		if (clk_users != 1)
+			return 0;
+
+		codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+		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;
+		}
+	} 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",
+					 __func__, clk_users);
+			clk_disable(codec_clk);
+			clk_put(codec_clk);
+			tabla_mclk_enable(codec, 0);
+		}
+	}
+	return 0;
+}
+
 static int msm_mclk_event(struct snd_soc_dapm_widget *w,
 		struct snd_kcontrol *kcontrol, int event)
 {
@@ -570,6 +603,74 @@
 	return 0;
 }
 
+static void *def_tabla_mbhc_cal(void)
+{
+	void *tabla_cal;
+	struct tabla_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_low, *btn_high;
+	u8 *n_cic, *gain;
+
+	tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
+						TABLA_MBHC_DEF_RLOADS),
+			    GFP_KERNEL);
+	if (!tabla_cal) {
+		pr_err("%s: out of memory\n", __func__);
+		return NULL;
+	}
+
+#define S(X, Y) ((TABLA_MBHC_CAL_GENERAL_PTR(tabla_cal)->X) = (Y))
+	S(t_ldoh, 100);
+	S(t_bg_fast_settle, 100);
+	S(t_shutdown_plug_rem, 255);
+	S(mbhc_nsa, 4);
+	S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_DET_PTR(tabla_cal)->X) = (Y))
+	S(mic_current, TABLA_PID_MIC_5_UA);
+	S(hph_current, TABLA_PID_MIC_5_UA);
+	S(t_mic_pid, 100);
+	S(t_ins_complete, 1000);
+	S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla_cal)->X) = (Y))
+	S(v_no_mic, 30);
+	S(v_hs_max, 1450);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
+	S(c[0], 6);
+	S(c[1], 10);
+	S(c[2], 10);
+	S(c[3], 14);
+	S(c[4], 14);
+	S(c[5], 16);
+	S(c[6], 0);
+	S(c[7], 0);
+	S(nc, 5);
+	S(n_meas, 11);
+	S(mbhc_nsc, 11);
+	S(num_btn, TABLA_MBHC_DEF_BUTTONS);
+	S(v_btn_press_delta_sta, 100);
+	S(v_btn_press_delta_cic, 50);
+#undef S
+	btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal);
+	btn_low = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_LOW);
+	btn_high = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_HIGH);
+	btn_low[0] = 0;
+	btn_high[0] = 40;
+	btn_low[1] = 60;
+	btn_high[1] = 140;
+	btn_low[2] = 160;
+	btn_high[2] = 240;
+	n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
+	n_cic[0] = 120;
+	n_cic[1] = 94;
+	gain = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_GAIN);
+	gain[0] = 11;
+	gain[1] = 9;
+
+	return tabla_cal;
+}
+
 static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
 {
 	int err;
@@ -618,7 +719,9 @@
 		return err;
 	}
 
-	tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_calib);
+	tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
+			TABLA_MICBIAS2, msm_enable_codec_ext_clk, 0,
+			TABLA_EXT_CLK_RATE);
 
 	return 0;
 }
@@ -1122,9 +1225,16 @@
 		return -ENODEV;
 	}
 
+	tabla_mbhc_cal = def_tabla_mbhc_cal();
+	if (!tabla_mbhc_cal) {
+		pr_err("Calibration data allocation failed\n");
+		return -ENOMEM;
+	}
+
 	msm_snd_device = platform_device_alloc("soc-audio", 0);
 	if (!msm_snd_device) {
 		pr_err("Platform device allocation failed\n");
+		kfree(tabla_mbhc_cal);
 		return -ENOMEM;
 	}
 
@@ -1132,6 +1242,7 @@
 	ret = platform_device_add(msm_snd_device);
 	if (ret) {
 		platform_device_put(msm_snd_device);
+		kfree(tabla_mbhc_cal);
 		return ret;
 	}
 
diff --git a/sound/soc/msm/msm-pcm-voice.c b/sound/soc/msm/msm-pcm-voice.c
index 5b50509..eafe0f9 100644
--- a/sound/soc/msm/msm-pcm-voice.c
+++ b/sound/soc/msm/msm-pcm-voice.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -270,8 +270,8 @@
 
 	pr_debug("%s: st enable=%d\n", __func__, st_enable);
 
-	voc_set_slowtalk_enable(voc_get_session_id(VOICE_SESSION_NAME),
-				st_enable);
+	voc_set_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
+			MODULE_ID_VOICE_MODULE_ST, st_enable);
 
 	return 0;
 }
@@ -280,7 +280,30 @@
 			struct snd_ctl_elem_value *ucontrol)
 {
 	ucontrol->value.integer.value[0] =
-		voc_get_slowtalk_enable(voc_get_session_id(VOICE_SESSION_NAME));
+		voc_get_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
+				MODULE_ID_VOICE_MODULE_ST);
+	return 0;
+}
+
+static int msm_voice_fens_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	int fens_enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: fens enable=%d\n", __func__, fens_enable);
+
+	voc_set_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
+			MODULE_ID_VOICE_MODULE_FENS, fens_enable);
+
+	return 0;
+}
+
+static int msm_voice_fens_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] =
+		voc_get_pp_enable(voc_get_session_id(VOICE_SESSION_NAME),
+				MODULE_ID_VOICE_MODULE_FENS);
 	return 0;
 }
 
@@ -295,6 +318,8 @@
 			msm_voice_widevoice_get, msm_voice_widevoice_put),
 	SOC_SINGLE_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, 1, 0,
 				msm_voice_slowtalk_get, msm_voice_slowtalk_put),
+	SOC_SINGLE_EXT("FENS Enable", SND_SOC_NOPM, 0, 1, 0,
+				msm_voice_fens_get, msm_voice_fens_put),
 };
 
 static struct snd_pcm_ops msm_pcm_ops = {
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 2e98627..f26edc5 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -17,6 +17,7 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
@@ -52,6 +53,11 @@
 #define GPIO_AUX_PCM_SYNC 65
 #define GPIO_AUX_PCM_CLK 66
 
+#define TABLA_EXT_CLK_RATE 12288000
+
+#define TABLA_MBHC_DEF_BUTTONS 3
+#define TABLA_MBHC_DEF_RLOADS 5
+
 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;
@@ -63,17 +69,6 @@
 static int msm8960_btsco_rate = BTSCO_RATE_8KHZ;
 static int msm8960_btsco_ch = 1;
 
-struct tabla_mbhc_calibration tabla_cal = {
-	.bias = TABLA_MICBIAS2,
-	.tldoh = 100,
-	.bg_fast_settle = 100,
-	.mic_current = TABLA_PID_MIC_5_UA,
-	.mic_pid = 100,
-	.hph_current = TABLA_PID_MIC_5_UA,
-	.setup_plug_removal_delay = 1000000,
-	.shutdown_plug_removal = 100000,
-};
-
 static struct clk *codec_clk;
 static int clk_users;
 
@@ -82,6 +77,8 @@
 static struct snd_soc_jack hs_jack;
 static struct snd_soc_jack button_jack;
 
+static void *tabla_mbhc_cal;
+
 static void msm8960_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
 {
 	int ret = 0;
@@ -302,6 +299,41 @@
 	}
 	return 0;
 }
+int msm8960_enable_codec_ext_clk(
+		struct snd_soc_codec *codec, int enable)
+{
+	pr_debug("%s: enable = %d\n", __func__, enable);
+	if (enable) {
+		clk_users++;
+		pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+		if (clk_users != 1)
+			return 0;
+
+		codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+		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;
+		}
+	} 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",
+					 __func__, clk_users);
+			clk_disable(codec_clk);
+			clk_put(codec_clk);
+			tabla_mclk_enable(codec, 0);
+		}
+	}
+	return 0;
+}
 
 static int msm8960_mclk_event(struct snd_soc_dapm_widget *w,
 		struct snd_kcontrol *kcontrol, int event)
@@ -310,43 +342,9 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
-
-		clk_users++;
-		pr_debug("%s: clk_users = %d\n", __func__, clk_users);
-
-		if (clk_users != 1)
-			return 0;
-
-		codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
-		if (codec_clk) {
-			clk_set_rate(codec_clk, 12288000);
-			clk_enable(codec_clk);
-			tabla_mclk_enable(w->codec, 1);
-
-		} else {
-			pr_err("%s: Error setting Tabla MCLK\n", __func__);
-			clk_users--;
-			return -EINVAL;
-		}
-		break;
+		return msm8960_enable_codec_ext_clk(w->codec, 1);
 	case SND_SOC_DAPM_POST_PMD:
-
-		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",
-					__func__, clk_users);
-
-			clk_disable(codec_clk);
-			clk_put(codec_clk);
-			tabla_mclk_enable(w->codec, 0);
-		}
-		break;
+		return msm8960_enable_codec_ext_clk(w->codec, 0);
 	}
 	return 0;
 }
@@ -481,7 +479,7 @@
 	struct snd_ctl_elem_value *ucontrol)
 {
 	pr_debug("%s: msm8960_slim_0_rx_ch  = %d\n", __func__,
-			msm8960_slim_0_rx_ch);
+		 msm8960_slim_0_rx_ch);
 	ucontrol->value.integer.value[0] = msm8960_slim_0_rx_ch - 1;
 	return 0;
 }
@@ -492,7 +490,7 @@
 	msm8960_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
 
 	pr_debug("%s: msm8960_slim_0_rx_ch = %d\n", __func__,
-			msm8960_slim_0_rx_ch);
+		 msm8960_slim_0_rx_ch);
 	return 1;
 }
 
@@ -500,7 +498,7 @@
 	struct snd_ctl_elem_value *ucontrol)
 {
 	pr_debug("%s: msm8960_slim_0_tx_ch  = %d\n", __func__,
-			msm8960_slim_0_tx_ch);
+		 msm8960_slim_0_tx_ch);
 	ucontrol->value.integer.value[0] = msm8960_slim_0_tx_ch - 1;
 	return 0;
 }
@@ -511,15 +509,14 @@
 	msm8960_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
 
 	pr_debug("%s: msm8960_slim_0_tx_ch = %d\n", __func__,
-			msm8960_slim_0_tx_ch);
+		 msm8960_slim_0_tx_ch);
 	return 1;
 }
 
 static int msm8960_btsco_rate_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
-	pr_debug("%s: msm8960_btsco_rate  = %d", __func__,
-					msm8960_btsco_rate);
+	pr_debug("%s: msm8960_btsco_rate  = %d", __func__, msm8960_btsco_rate);
 	ucontrol->value.integer.value[0] = msm8960_btsco_rate;
 	return 0;
 }
@@ -538,8 +535,7 @@
 		msm8960_btsco_rate = BTSCO_RATE_8KHZ;
 		break;
 	}
-	pr_debug("%s: msm8960_btsco_rate = %d\n", __func__,
-					msm8960_btsco_rate);
+	pr_debug("%s: msm8960_btsco_rate = %d\n", __func__, msm8960_btsco_rate);
 	return 0;
 }
 
@@ -570,6 +566,74 @@
 	return 0;
 }
 
+static void *def_tabla_mbhc_cal(void)
+{
+	void *tabla_cal;
+	struct tabla_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_low, *btn_high;
+	u8 *n_cic, *gain;
+
+	tabla_cal = kzalloc(TABLA_MBHC_CAL_SIZE(TABLA_MBHC_DEF_BUTTONS,
+						TABLA_MBHC_DEF_RLOADS),
+			    GFP_KERNEL);
+	if (!tabla_cal) {
+		pr_err("%s: out of memory\n", __func__);
+		return NULL;
+	}
+
+#define S(X, Y) ((TABLA_MBHC_CAL_GENERAL_PTR(tabla_cal)->X) = (Y))
+	S(t_ldoh, 100);
+	S(t_bg_fast_settle, 100);
+	S(t_shutdown_plug_rem, 255);
+	S(mbhc_nsa, 4);
+	S(mbhc_navg, 4);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_DET_PTR(tabla_cal)->X) = (Y))
+	S(mic_current, TABLA_PID_MIC_5_UA);
+	S(hph_current, TABLA_PID_MIC_5_UA);
+	S(t_mic_pid, 100);
+	S(t_ins_complete, 250);
+	S(t_ins_retry, 200);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_PLUG_TYPE_PTR(tabla_cal)->X) = (Y))
+	S(v_no_mic, 30);
+	S(v_hs_max, 1450);
+#undef S
+#define S(X, Y) ((TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal)->X) = (Y))
+	S(c[0], 6);
+	S(c[1], 10);
+	S(c[2], 10);
+	S(c[3], 14);
+	S(c[4], 14);
+	S(c[5], 16);
+	S(c[6], 0);
+	S(c[7], 0);
+	S(nc, 5);
+	S(n_meas, 11);
+	S(mbhc_nsc, 11);
+	S(num_btn, TABLA_MBHC_DEF_BUTTONS);
+	S(v_btn_press_delta_sta, 100);
+	S(v_btn_press_delta_cic, 50);
+#undef S
+	btn_cfg = TABLA_MBHC_CAL_BTN_DET_PTR(tabla_cal);
+	btn_low = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_LOW);
+	btn_high = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_V_BTN_HIGH);
+	btn_low[0] = 0;
+	btn_high[0] = 40;
+	btn_low[1] = 60;
+	btn_high[1] = 140;
+	btn_low[2] = 160;
+	btn_high[2] = 240;
+	n_cic = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_N_CIC);
+	n_cic[0] = 120;
+	n_cic[1] = 94;
+	gain = tabla_mbhc_cal_btn_det_mp(btn_cfg, TABLA_BTN_DET_GAIN);
+	gain[0] = 11;
+	gain[1] = 9;
+
+	return tabla_cal;
+}
+
 static int msm8960_audrx_init(struct snd_soc_pcm_runtime *rtd)
 {
 	int err;
@@ -618,7 +682,9 @@
 		return err;
 	}
 
-	tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_cal);
+	tabla_hs_detect(codec, &hs_jack, &button_jack, tabla_mbhc_cal,
+			TABLA_MICBIAS2, msm8960_enable_codec_ext_clk, 0,
+			TABLA_EXT_CLK_RATE);
 
 	return 0;
 }
@@ -1169,9 +1235,17 @@
 		pr_err("%s: Not the right machine type\n", __func__);
 		return -ENODEV ;
 	}
+
+	tabla_mbhc_cal = def_tabla_mbhc_cal();
+	if (!tabla_mbhc_cal) {
+		pr_err("Calibration data allocation failed\n");
+		return -ENOMEM;
+	}
+
 	msm8960_snd_device = platform_device_alloc("soc-audio", 0);
 	if (!msm8960_snd_device) {
 		pr_err("Platform device allocation failed\n");
+		kfree(tabla_mbhc_cal);
 		return -ENOMEM;
 	}
 
@@ -1179,6 +1253,7 @@
 	ret = platform_device_add(msm8960_snd_device);
 	if (ret) {
 		platform_device_put(msm8960_snd_device);
+		kfree(tabla_mbhc_cal);
 		return ret;
 	}
 
@@ -1201,6 +1276,7 @@
 	}
 	msm8960_free_headset_mic_gpios();
 	platform_device_unregister(msm8960_snd_device);
+	kfree(tabla_mbhc_cal);
 }
 module_exit(msm8960_audio_exit);
 
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index de63fa0..2acf59e 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -1,4 +1,4 @@
-/*  Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/*  Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -52,8 +52,8 @@
 static int voice_send_cvp_register_vol_cal_table_cmd(struct voice_data *v);
 static int voice_send_cvp_deregister_vol_cal_table_cmd(struct voice_data *v);
 static int voice_send_set_widevoice_enable_cmd(struct voice_data *v);
-static int voice_send_set_slowtalk_enable_cmd(struct voice_data *v);
-
+static int voice_send_set_pp_enable_cmd(struct voice_data *v,
+					uint32_t module_id, int enable);
 static int voice_cvs_stop_playback(struct voice_data *v);
 static int voice_cvs_start_playback(struct voice_data *v);
 static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode);
@@ -1764,9 +1764,10 @@
 	return -EINVAL;
 }
 
-static int voice_send_set_slowtalk_enable_cmd(struct voice_data *v)
+static int voice_send_set_pp_enable_cmd(struct voice_data *v,
+					uint32_t module_id, int enable)
 {
-	struct cvs_set_slowtalk_enable_cmd cvs_set_st_cmd;
+	struct cvs_set_pp_enable_cmd cvs_set_pp_cmd;
 	int ret = 0;
 	void *apr_cvs;
 	u16 cvs_handle;
@@ -1784,24 +1785,26 @@
 	cvs_handle = voice_get_cvs_handle(v);
 
 	/* fill in the header */
-	cvs_set_st_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+	cvs_set_pp_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
-	cvs_set_st_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
-				sizeof(cvs_set_st_cmd) - APR_HDR_SIZE);
-	cvs_set_st_cmd.hdr.src_port = v->session_id;
-	cvs_set_st_cmd.hdr.dest_port = cvs_handle;
-	cvs_set_st_cmd.hdr.token = 0;
-	cvs_set_st_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_UI_PROPERTY;
+	cvs_set_pp_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_set_pp_cmd) - APR_HDR_SIZE);
+	cvs_set_pp_cmd.hdr.src_port = v->session_id;
+	cvs_set_pp_cmd.hdr.dest_port = cvs_handle;
+	cvs_set_pp_cmd.hdr.token = 0;
+	cvs_set_pp_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_UI_PROPERTY;
 
-	cvs_set_st_cmd.vss_set_st.module_id = MODULE_ID_VOICE_MODULE_ST;
-	cvs_set_st_cmd.vss_set_st.param_id = VOICE_PARAM_MOD_ENABLE;
-	cvs_set_st_cmd.vss_set_st.param_size = MOD_ENABLE_PARAM_LEN;
-	cvs_set_st_cmd.vss_set_st.reserved = 0;
-	cvs_set_st_cmd.vss_set_st.enable = v->st_enable;
-	cvs_set_st_cmd.vss_set_st.reserved_field = 0;
+	cvs_set_pp_cmd.vss_set_pp.module_id = module_id;
+	cvs_set_pp_cmd.vss_set_pp.param_id = VOICE_PARAM_MOD_ENABLE;
+	cvs_set_pp_cmd.vss_set_pp.param_size = MOD_ENABLE_PARAM_LEN;
+	cvs_set_pp_cmd.vss_set_pp.reserved = 0;
+	cvs_set_pp_cmd.vss_set_pp.enable = enable;
+	cvs_set_pp_cmd.vss_set_pp.reserved_field = 0;
+	pr_debug("voice_send_set_pp_enable_cmd, module_id=%d, enable=%d\n",
+		module_id, enable);
 
 	v->cvs_state = CMD_STATUS_FAIL;
-	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_st_cmd);
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_pp_cmd);
 	if (ret < 0) {
 		pr_err("Fail: sending cvs set slowtalk enable,\n");
 		goto fail;
@@ -1918,7 +1921,11 @@
 
 	/* enable slowtalk if st_enable is set */
 	if (v->st_enable)
-		voice_send_set_slowtalk_enable_cmd(v);
+		voice_send_set_pp_enable_cmd(v, MODULE_ID_VOICE_MODULE_ST,
+					v->st_enable);
+	if (v->fens_enable)
+		voice_send_set_pp_enable_cmd(v, MODULE_ID_VOICE_MODULE_FENS,
+					v->fens_enable);
 
 	if (is_voip_session(v->session_id))
 		voice_send_netid_timing_cmd(v);
@@ -2836,7 +2843,14 @@
 
 		/* enable slowtalk */
 		if (v->st_enable)
-			voice_send_set_slowtalk_enable_cmd(v);
+			voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_ST,
+						v->st_enable);
+		/* enable FENS */
+		if (v->fens_enable)
+			voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_FENS,
+						v->fens_enable);
 
 		get_sidetone_cal(&sidetone_cal_data);
 		if (v->dev_tx.port_id != RT_PROXY_PORT_001_TX &&
@@ -2973,7 +2987,7 @@
 	return ret;
 }
 
-int voc_set_slowtalk_enable(uint16_t session_id, uint32_t st_enable)
+int voc_set_pp_enable(uint16_t session_id, uint32_t module_id, uint32_t enable)
 {
 	struct voice_data *v = voice_get_session(session_id);
 	int ret = 0;
@@ -2985,18 +2999,27 @@
 	}
 
 	mutex_lock(&v->lock);
+	if (module_id == MODULE_ID_VOICE_MODULE_ST)
+		v->st_enable = enable;
+	else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
+		v->fens_enable = enable;
 
-	v->st_enable = st_enable;
-
-	if (v->voc_state == VOC_RUN)
-		ret = voice_send_set_slowtalk_enable_cmd(v);
-
+	if (v->voc_state == VOC_RUN) {
+		if (module_id == MODULE_ID_VOICE_MODULE_ST)
+			ret = voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_ST,
+						enable);
+		else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
+			ret = voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_FENS,
+						enable);
+	}
 	mutex_unlock(&v->lock);
 
 	return ret;
 }
 
-uint32_t voc_get_slowtalk_enable(uint16_t session_id)
+int voc_get_pp_enable(uint16_t session_id, uint32_t module_id)
 {
 	struct voice_data *v = voice_get_session(session_id);
 	int ret = 0;
@@ -3008,8 +3031,10 @@
 	}
 
 	mutex_lock(&v->lock);
-
-	ret = v->st_enable;
+	if (module_id == MODULE_ID_VOICE_MODULE_ST)
+		ret = v->st_enable;
+	else if (module_id == MODULE_ID_VOICE_MODULE_FENS)
+		ret = v->fens_enable;
 
 	mutex_unlock(&v->lock);
 
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
index d330ada..2dc08d6 100644
--- a/sound/soc/msm/qdsp6/q6voice.h
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -323,6 +323,7 @@
 #define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE		0x0001101D
 /* Set encoder DTX mode. */
 
+#define MODULE_ID_VOICE_MODULE_FENS			0x00010EEB
 #define MODULE_ID_VOICE_MODULE_ST			0x00010EE3
 #define VOICE_PARAM_MOD_ENABLE				0x00010E00
 #define MOD_ENABLE_PARAM_LEN				4
@@ -501,7 +502,7 @@
 	/* Size of the calibration data in bytes. */
 };
 
-struct vss_icommon_cmd_set_ui_property_st_enable_t {
+struct vss_icommon_cmd_set_ui_property_enable_t {
 	uint32_t module_id;
 	/* Unique ID of the module. */
 	uint32_t param_id;
@@ -573,9 +574,9 @@
 	struct apr_hdr hdr;
 } __packed;
 
-struct cvs_set_slowtalk_enable_cmd {
+struct cvs_set_pp_enable_cmd {
 	struct apr_hdr hdr;
-	struct vss_icommon_cmd_set_ui_property_st_enable_t vss_set_st;
+	struct vss_icommon_cmd_set_ui_property_enable_t vss_set_pp;
 } __packed;
 struct cvs_start_record_cmd {
 	struct apr_hdr hdr;
@@ -832,6 +833,8 @@
 	uint8_t wv_enable;
 	/* slowtalk enable value */
 	uint32_t st_enable;
+	/* FENC enable value */
+	uint32_t fens_enable;
 
 	struct voice_dev_route_state voc_route_state;
 
@@ -886,8 +889,8 @@
 };
 
 /* called  by alsa driver */
-int voc_set_slowtalk_enable(uint16_t session_id, uint32_t st_enable);
-uint32_t voc_get_slowtalk_enable(uint16_t session_id);
+int voc_set_pp_enable(uint16_t session_id, uint32_t module_id, uint32_t enable);
+int voc_get_pp_enable(uint16_t session_id, uint32_t module_id);
 int voc_set_widevoice_enable(uint16_t session_id, uint32_t wv_enable);
 uint32_t voc_get_widevoice_enable(uint16_t session_id);
 uint8_t voc_get_tty_mode(uint16_t session_id);