Merge "msm: kgsl: Disable shader memory dump during snapshot"
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 276a6dc..399cea9 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -1115,8 +1115,8 @@
 	bool found_dead_app = false;
 
 	if (!memcmp(data->client.app_name, "keymaste", strlen("keymaste"))) {
-		pr_warn("Do not unload keymaster app from tz\n");
-		return 0;
+		pr_debug("Do not unload keymaster app from tz\n");
+		goto unload_exit;
 	}
 
 	if (data->client.app_id > 0) {
@@ -1211,6 +1211,7 @@
 		spin_unlock_irqrestore(&qseecom.registered_app_list_lock,
 								flags1);
 	}
+unload_exit:
 	qseecom_unmap_ion_allocated_memory(data);
 	data->released = true;
 	return ret;
diff --git a/drivers/nfc/nfc-nci.c b/drivers/nfc/nfc-nci.c
index 9ba0a87..e51da7f 100644
--- a/drivers/nfc/nfc-nci.c
+++ b/drivers/nfc/nfc-nci.c
@@ -762,6 +762,60 @@
 }
 
 /*
+ * Inside nfc_ioctl_nfcc_efuse
+ *
+ * @brief   nfc_ioctl_nfcc_efuse
+ *
+ */
+int nfc_ioctl_nfcc_efuse(struct file *filp, unsigned int cmd,
+				unsigned long arg)
+{
+	int r = 0;
+	unsigned short slave_addr = 0xE;
+	unsigned short curr_addr;
+	unsigned char efuse_addr  = EFUSE_REG;
+	unsigned char efuse_value = 0xFF;
+
+	struct qca199x_dev *qca199x_dev = filp->private_data;
+
+	curr_addr = qca199x_dev->client->addr;
+	qca199x_dev->client->addr = slave_addr;
+
+	r = nfc_i2c_write(qca199x_dev->client,
+				&efuse_addr, 1);
+	if (r < 0) {
+		/* Restore original NFCC slave I2C address */
+		qca199x_dev->client->addr = curr_addr;
+		dev_err(&qca199x_dev->client->dev,
+		"ERROR_WRITE_FAIL : i2c write fail\n");
+		return -EIO;
+	}
+
+	/*
+	 * NFCC chip needs to be at least
+	 * 10usec high before make it low
+	 */
+	usleep_range(10, 15);
+
+	r = i2c_master_recv(qca199x_dev->client, &efuse_value,
+					sizeof(efuse_value));
+	if (r < 0) {
+		/* Restore original NFCC slave I2C address */
+		qca199x_dev->client->addr = curr_addr;
+		dev_err(&qca199x_dev->client->dev,
+		"ERROR_I2C_RCV_FAIL : i2c recv fail\n");
+		return -EIO;
+	}
+
+	dev_dbg(&qca199x_dev->client->dev, "%s : EFUSE_VALUE %02x\n",
+	__func__, efuse_value);
+
+	/* Restore original NFCC slave I2C address */
+	qca199x_dev->client->addr = curr_addr;
+	return efuse_value;
+}
+
+/*
  * Inside nfc_ioctl_nfcc_version
  *
  * @brief   nfc_ioctl_nfcc_version
@@ -934,7 +988,7 @@
 static long nfc_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
 {
 	int r = 0;
-
+	struct qca199x_dev *qca199x_dev = pfile->private_data;
 	switch (cmd) {
 
 	case NFC_SET_PWR:
@@ -956,6 +1010,14 @@
 	case NFCC_INITIAL_CORE_RESET_NTF:
 		r = nfc_ioctl_core_reset_ntf(pfile, cmd, arg);
 		break;
+	case NFC_GET_EFUSE:
+		r = nfc_ioctl_nfcc_efuse(pfile, cmd, arg);
+		if (r < 0) {
+			r = 0xFF;
+			dev_err(&qca199x_dev->client->dev,
+			"nfc_ioctl : FAILED TO READ EFUSE TYPE\n");
+		}
+		break;
 	default:
 		r = -ENOIOCTLCMD;
 	}
diff --git a/drivers/nfc/nfc-nci.h b/drivers/nfc/nfc-nci.h
index 8186861..398fa3f 100644
--- a/drivers/nfc/nfc-nci.h
+++ b/drivers/nfc/nfc-nci.h
@@ -60,6 +60,7 @@
 #define SET_RX_BLOCK	_IOW(0xE9, 0x04, unsigned int)
 #define SET_EMULATOR_TEST_POINT		_IOW(0xE9, 0x05, unsigned int)
 #define NFCC_VERSION				_IOW(0xE9, 0x08, unsigned int)
+#define NFC_GET_EFUSE				_IOW(0xE9, 0x09, unsigned int)
 #define NFCC_INITIAL_CORE_RESET_NTF		_IOW(0xE9, 0x10, unsigned int)
 
 #define NFC_MAX_I2C_TRANSFER	(0x0400)
diff --git a/include/sound/Kbuild b/include/sound/Kbuild
index aeccfed..bb8b17b 100644
--- a/include/sound/Kbuild
+++ b/include/sound/Kbuild
@@ -14,3 +14,4 @@
 header-y += lsm_params.h
 header-y += voice_params.h
 header-y += voice_svc.h
+header-y += msmcal-hwdep.h
diff --git a/include/sound/asound.h b/include/sound/asound.h
index 7bf01b6..244bb30 100644
--- a/include/sound/asound.h
+++ b/include/sound/asound.h
@@ -95,9 +95,10 @@
 	SNDRV_HWDEP_IFACE_SB_RC,	/* SB Extigy/Audigy2NX remote control */
 	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
 	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
+        SNDRV_HWDEP_IFACE_AUDIO_CODEC,  /* codec Audio Control */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_USB_STREAM
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_AUDIO_CODEC
 };
 
 struct snd_hwdep_info {
diff --git a/include/sound/msmcal-hwdep.h b/include/sound/msmcal-hwdep.h
new file mode 100644
index 0000000..324b497
--- /dev/null
+++ b/include/sound/msmcal-hwdep.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CALIB_HWDEP_H
+#define _CALIB_HWDEP_H
+
+#define WCD9XXX_CODEC_HWDEP_NODE    1000
+enum wcd_cal_type {
+	WCD9XXX_MIN_CAL,
+	WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL,
+	WCD9XXX_MAD_CAL,
+	WCD9XXX_MBHC_CAL,
+	WCD9XXX_MAX_CAL,
+};
+
+struct wcdcal_ioctl_buffer {
+	__u32 size;
+	__u8 __user *buffer;
+	enum wcd_cal_type cal_type;
+};
+
+#define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \
+	_IOW('U', 0x1, struct wcdcal_ioctl_buffer)
+
+#endif /*_CALIB_HWDEP_H*/
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index a3ed23d..b42599e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -51,8 +51,8 @@
 snd-soc-wcd9304-objs := wcd9304.o wcd9304-tables.o
 snd-soc-wcd9310-objs := wcd9310.o wcd9310-tables.o
 snd-soc-cs8427-objs := cs8427.o
-snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o wcd9xxx-common.o
-snd-soc-wcd9306-objs := wcd9306.o wcd9306-tables.o wcd9xxx-common.o
+snd-soc-wcd9320-objs := wcd9xxx-resmgr.o wcd9320.o wcd9320-tables.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o
+snd-soc-wcd9306-objs := wcd9306.o wcd9306-tables.o wcd9xxx-common.o wcdcal-hwdep.o
 snd-soc-msm8x10-wcd-objs := msm8x10-wcd.o msm8x10-wcd-tables.o wcd9xxx-common.o
 snd-soc-wl1273-objs := wl1273.o
 snd-soc-wm1250-ev1-objs := wm1250-ev1.o
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index ce42a94..1dfd72b 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -19,7 +19,6 @@
 #include <linux/ratelimit.h>
 #include <linux/debugfs.h>
 #include <linux/wait.h>
-#include <linux/bitops.h>
 #include <linux/mfd/wcd9xxx/core.h>
 #include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
 #include <linux/mfd/wcd9xxx/wcd9320_registers.h>
@@ -41,6 +40,8 @@
 #include "wcd9320.h"
 #include "wcd9xxx-resmgr.h"
 #include "wcd9xxx-common.h"
+#include "wcdcal-hwdep.h"
+
 
 #define TAIKO_MAD_SLIMBUS_TX_PORT 12
 #define TAIKO_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin"
@@ -453,6 +454,8 @@
 	 */
 	struct list_head reg_save_restore;
 	struct pm_qos_request pm_qos_req;
+	/* cal info for codec */
+	struct fw_info *fw_data;
 };
 
 static const u32 comp_shift[] = {
@@ -2680,8 +2683,11 @@
 	int ret;
 	const struct firmware *fw;
 	struct mad_audio_cal *mad_cal;
+	struct firmware_cal *hwdep_cal = NULL;
+	const void *data;
 	const char *filename = TAIKO_MAD_AUDIO_FIRMWARE_PATH;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+	size_t cal_size;
 
 	pr_debug("%s: enter\n", __func__);
 	/* wakeup for codec calibration access */
@@ -2690,24 +2696,46 @@
 			   PM_QOS_DEFAULT_VALUE);
 	pm_qos_update_request(&taiko->pm_qos_req,
 			      msm_cpuidle_get_deep_idle_latency());
-	ret = request_firmware(&fw, filename, codec->dev);
-	if (ret != 0) {
-		pr_err("Failed to acquire MAD firwmare data %s: %d\n", filename,
-		       ret);
+	if (!taiko->fw_data) {
+		dev_err(codec->dev, "%s: invalid cal data\n",
+				 __func__);
 		return -ENODEV;
 	}
-
-	if (fw->size < sizeof(struct mad_audio_cal)) {
-		pr_err("%s: incorrect firmware size %u\n", __func__, fw->size);
-		release_firmware(fw);
-		return -ENOMEM;
+	hwdep_cal = wcdcal_get_fw_cal(taiko->fw_data, WCD9XXX_MAD_CAL);
+	if (hwdep_cal) {
+		data = hwdep_cal->data;
+		cal_size = hwdep_cal->size;
+		dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+				__func__);
+	} else {
+		ret = request_firmware(&fw, filename, codec->dev);
+		if (ret != 0) {
+			pr_err("Failed to acquire MAD firwmare data %s: %d\n",
+				filename, ret);
+			return -ENODEV;
+		}
+		if (!fw) {
+			dev_err(codec->dev, "failed to get mad fw");
+			return -ENODEV;
+		}
+		data = fw->data;
+		cal_size = fw->size;
+		dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+				__func__);
+	}
+	if (cal_size < sizeof(struct mad_audio_cal)) {
+		pr_err("%s: incorrect hwdep cal size %zu\n",
+			__func__, cal_size);
+		ret = -ENOMEM;
+		goto err;
 	}
 
-	mad_cal = (struct mad_audio_cal *)(fw->data);
+	mad_cal = (struct mad_audio_cal *)(data);
 	if (!mad_cal) {
-		pr_err("%s: Invalid calibration data\n", __func__);
-		release_firmware(fw);
-		return -EINVAL;
+		dev_err(codec->dev, "%s: Invalid calibration data\n",
+				__func__);
+		ret =  -EINVAL;
+		goto err;
 	}
 
 	snd_soc_write(codec, TAIKO_A_CDC_MAD_MAIN_CTL_2,
@@ -2757,11 +2785,13 @@
 	snd_soc_write(codec, TAIKO_A_CDC_MAD_ULTR_CTL_6,
 		      mad_cal->ultrasound_info.rms_threshold_msb);
 
-	release_firmware(fw);
 	pr_debug("%s: leave ret %d\n", __func__, ret);
 	pm_qos_update_request(&taiko->pm_qos_req,
 			      PM_QOS_DEFAULT_VALUE);
 	pm_qos_remove_request(&taiko->pm_qos_req);
+err:
+	if (!hwdep_cal)
+		release_firmware(fw);
 	return ret;
 }
 
@@ -3318,15 +3348,18 @@
 	const char *filename;
 	const struct firmware *fw;
 	int i;
-	int ret;
+	int ret =0;
 	int num_anc_slots;
 	struct wcd9xxx_anc_header *anc_head;
 	struct taiko_priv *taiko = snd_soc_codec_get_drvdata(codec);
+	struct firmware_cal *hwdep_cal = NULL;
 	u32 anc_writes_size = 0;
 	int anc_size_remaining;
 	u32 *anc_ptr;
 	u16 reg;
 	u8 mask, val, old_val;
+	size_t cal_size;
+	const void *data;
 
 
 	if (taiko->anc_func == 0)
@@ -3335,38 +3368,53 @@
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
 		filename = "wcd9320/wcd9320_anc.bin";
+		hwdep_cal = wcdcal_get_fw_cal(taiko->fw_data, WCD9XXX_ANC_CAL);
+		if (hwdep_cal) {
+			data = hwdep_cal->data;
+			cal_size = hwdep_cal->size;
+			dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+				__func__);
+		} else {
+			ret = request_firmware(&fw, filename, codec->dev);
+			if (ret != 0) {
+				dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
+					ret);
+				return -ENODEV;
+			}
+			if (!fw) {
+				dev_err(codec->dev, "failed to get anc fw");
+				return -ENODEV;
+			}
+			data = fw->data;
+			cal_size = fw->size;
+			dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+					 __func__);
 
-		ret = request_firmware(&fw, filename, codec->dev);
-		if (ret != 0) {
-			dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
-				ret);
-			return -ENODEV;
 		}
 
-		if (fw->size < sizeof(struct wcd9xxx_anc_header)) {
+		if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
 			dev_err(codec->dev, "Not enough data\n");
-			release_firmware(fw);
-			return -ENOMEM;
+			goto err;
 		}
 
 		/* First number is the number of register writes */
-		anc_head = (struct wcd9xxx_anc_header *)(fw->data);
-		anc_ptr = (u32 *)((u32)fw->data +
+		anc_head = (struct wcd9xxx_anc_header *)(data);
+		anc_ptr = (u32 *)(data +
 				  sizeof(struct wcd9xxx_anc_header));
-		anc_size_remaining = fw->size -
+		anc_size_remaining = cal_size -
 				     sizeof(struct wcd9xxx_anc_header);
 		num_anc_slots = anc_head->num_anc_slots;
 
 		if (taiko->anc_slot >= num_anc_slots) {
 			dev_err(codec->dev, "Invalid ANC slot selected\n");
-			release_firmware(fw);
-			return -EINVAL;
+			ret = -EINVAL;
+			goto err;
 		}
 		for (i = 0; i < num_anc_slots; i++) {
 			if (anc_size_remaining < TAIKO_PACKED_REG_SIZE) {
 				dev_err(codec->dev, "Invalid register format\n");
-				release_firmware(fw);
-				return -EINVAL;
+				ret = -EINVAL;
+				goto err;
 			}
 			anc_writes_size = (u32)(*anc_ptr);
 			anc_size_remaining -= sizeof(u32);
@@ -3375,8 +3423,8 @@
 			if (anc_writes_size * TAIKO_PACKED_REG_SIZE
 				> anc_size_remaining) {
 				dev_err(codec->dev, "Invalid register format\n");
-				release_firmware(fw);
-				return -ENOMEM;
+				ret = -EINVAL;
+				goto err;
 			}
 
 			if (taiko->anc_slot == i)
@@ -3388,8 +3436,8 @@
 		}
 		if (i == num_anc_slots) {
 			dev_err(codec->dev, "Selected ANC slot not present\n");
-			release_firmware(fw);
-			return -ENOMEM;
+			ret = -EINVAL;
+			goto err;
 		}
 		for (i = 0; i < anc_writes_size; i++) {
 			TAIKO_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
@@ -3399,6 +3447,8 @@
 				(val & mask));
 		}
 		release_firmware(fw);
+		if (!hwdep_cal)
+			release_firmware(fw);
 		break;
 	case SND_SOC_DAPM_PRE_PMD:
 		msleep(40);
@@ -3411,6 +3461,11 @@
 		break;
 	}
 	return 0;
+err:
+	if (!hwdep_cal)
+		release_firmware(fw);
+	return ret;
+
 }
 
 static int taiko_hph_pa_event(struct snd_soc_dapm_widget *w,
@@ -6586,6 +6641,27 @@
 
 	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, taiko);
 }
+static
+struct firmware_cal *taiko_get_hwdep_fw_cal(struct snd_soc_codec *codec,
+			enum wcd_cal_type type)
+{
+	struct taiko_priv *taiko;
+	struct firmware_cal *hwdep_cal;
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer\n", __func__);
+		return NULL;
+	}
+	taiko = snd_soc_codec_get_drvdata(codec);
+	hwdep_cal = wcdcal_get_fw_cal(taiko->fw_data, type);
+	if (!hwdep_cal) {
+		dev_err(codec->dev, "%s: cal not sent by %d\n",
+				 __func__, type);
+		return NULL;
+	}
+
+	return hwdep_cal;
+}
 
 int taiko_hs_detect(struct snd_soc_codec *codec,
 		    struct wcd9xxx_mbhc_config *mbhc_cfg)
@@ -6840,6 +6916,7 @@
 	.get_cdc_type = taiko_get_cdc_type,
 	.setup_zdet = taiko_setup_zdet,
 	.compute_impedance = taiko_compute_impedance,
+	.get_hwdep_fw_cal = taiko_get_hwdep_fw_cal,
 };
 
 static const struct wcd9xxx_mbhc_intr cdc_intr_ids = {
@@ -7093,7 +7170,7 @@
 				  WCD9XXX_CDC_TYPE_TAIKO);
 	if (ret) {
 		pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
-		goto err_init;
+		goto err_nomem_slimch;
 	}
 
 	taiko->clsh_d.buck_mv = taiko_codec_get_buck_mv(codec);
@@ -7105,7 +7182,20 @@
 		rco_clk_rate = TAIKO_MCLK_CLK_12P288MHZ;
 	else
 		rco_clk_rate = TAIKO_MCLK_CLK_9P6MHZ;
-
+	taiko->fw_data = kzalloc(sizeof(*(taiko->fw_data)), GFP_KERNEL);
+	if (!taiko->fw_data) {
+		dev_err(codec->dev, "Failed to allocate fw_data\n");
+		goto err_nomem_slimch;
+	}
+	set_bit(WCD9XXX_ANC_CAL, taiko->fw_data->cal_bit);
+	set_bit(WCD9XXX_MAD_CAL, taiko->fw_data->cal_bit);
+	set_bit(WCD9XXX_MBHC_CAL, taiko->fw_data->cal_bit);
+	ret = wcd_cal_create_hwdep(taiko->fw_data,
+					WCD9XXX_CODEC_HWDEP_NODE, codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+		goto err_hwdep;
+	}
 	/* init and start mbhc */
 	ret = wcd9xxx_mbhc_init(&taiko->mbhc, &taiko->resmgr, codec,
 				taiko_enable_mbhc_micbias,
@@ -7113,7 +7203,7 @@
 				rco_clk_rate, true);
 	if (ret) {
 		pr_err("%s: mbhc init failed %d\n", __func__, ret);
-		goto err_init;
+		goto err_hwdep;
 	}
 
 	taiko->codec = codec;
@@ -7137,7 +7227,7 @@
 	ret = taiko_handle_pdata(taiko);
 	if (IS_ERR_VALUE(ret)) {
 		pr_err("%s: bad pdata\n", __func__);
-		goto err_pdata;
+		goto err_hwdep;
 	}
 
 	taiko->spkdrv_reg = taiko_codec_find_regulator(codec,
@@ -7155,7 +7245,7 @@
 	if (!ptr) {
 		pr_err("%s: no mem for slim chan ctl data\n", __func__);
 		ret = -ENOMEM;
-		goto err_nomem_slimch;
+		goto err_hwdep;
 	}
 
 	if (taiko->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
@@ -7233,11 +7323,11 @@
 
 err_irq:
 	taiko_cleanup_irqs(taiko);
-err_pdata:
-	kfree(ptr);
+        kfree(ptr);
+err_hwdep:
+	kfree(taiko->fw_data);
 err_nomem_slimch:
 	kfree(taiko);
-err_init:
 	return ret;
 }
 static int taiko_codec_remove(struct snd_soc_codec *codec)
@@ -7261,6 +7351,7 @@
 
 	taiko->spkdrv_reg = NULL;
 
+	kfree(taiko->fw_data);
 	kfree(taiko);
 	return 0;
 }
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
index 09721e1..1475df2 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.c
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -35,6 +35,7 @@
 #include <linux/kernel.h>
 #include <linux/gpio.h>
 #include <linux/input.h>
+#include "wcdcal-hwdep.h"
 #include "wcd9320.h"
 #include "wcd9306.h"
 #include "wcd9xxx-mbhc.h"
@@ -74,7 +75,7 @@
 #define OCP_ATTEMPT 1
 
 #define FW_READ_ATTEMPTS 15
-#define FW_READ_TIMEOUT 2000000
+#define FW_READ_TIMEOUT 4000000
 
 #define BUTTON_POLLING_SUPPORTED true
 
@@ -2806,35 +2807,39 @@
 	wcd9xxx_unlock_sleep(core_res);
 }
 
-static bool wcd9xxx_mbhc_fw_validate(const struct firmware *fw)
+static bool wcd9xxx_mbhc_fw_validate(const void *data, size_t size)
 {
 	u32 cfg_offset;
 	struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
 	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+	struct firmware_cal fw;
 
-	if (fw->size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+	fw.data = (void *)data;
+	fw.size = size;
+
+	if (fw.size < WCD9XXX_MBHC_CAL_MIN_SIZE)
 		return false;
 
 	/*
 	 * Previous check guarantees that there is enough fw data up
 	 * to num_btn
 	 */
-	btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw->data);
-	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw->data);
-	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+        btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw.data);
+        cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
+        if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
 		return false;
 
 	/*
 	 * Previous check guarantees that there is enough fw data up
 	 * to start of impedance detection configuration
 	 */
-	imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw->data);
-	cfg_offset = (u32) ((void *) imped_cfg - (void *) fw->data);
+        imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw.data);
+        cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data);
 
-	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+	if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
 		return false;
 
-	if (fw->size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+	if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
 		return false;
 
 	return true;
@@ -4171,7 +4176,9 @@
 	struct wcd9xxx_mbhc *mbhc;
 	struct snd_soc_codec *codec;
 	const struct firmware *fw;
+        struct firmware_cal *fw_data = NULL;
 	int ret = -1, retry = 0;
+	bool use_default_cal = false;
 
 	dwork = to_delayed_work(work);
 	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
@@ -4179,29 +4186,62 @@
 
 	while (retry < FW_READ_ATTEMPTS) {
 		retry++;
-		pr_info("%s:Attempt %d to request MBHC firmware\n",
-			__func__, retry);
-		ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
-				       codec->dev);
-
-		if (ret != 0) {
+		pr_debug("%s:Attempt %d to request MBHC firmware\n",
+                               __func__, retry);
+		if (mbhc->mbhc_cb->get_hwdep_fw_cal)
+			fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec,
+					WCD9XXX_MBHC_CAL);
+		if (!fw_data)
+			ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+                                       codec->dev);
+		/*
+		* if request_firmware and hwdep cal both fail then
+		* retry for few times before bailing out
+		*/
+		if ((ret != 0) && !fw_data) {
 			usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT);
 		} else {
 			pr_info("%s: MBHC Firmware read succesful\n", __func__);
 			break;
 		}
 	}
-
-	if (ret != 0) {
+	if (!fw_data)
+		pr_debug("%s: using request_firmware\n", __func__);
+	else
+		pr_debug("%s: using hwdep cal\n", __func__);
+	if (ret != 0 && !fw_data) {
 		pr_err("%s: Cannot load MBHC firmware use default cal\n",
-		       __func__);
-	} else if (wcd9xxx_mbhc_fw_validate(fw) == false) {
-		pr_err("%s: Invalid MBHC cal data size use default cal\n",
-		       __func__);
-		release_firmware(fw);
-	} else {
-		mbhc->mbhc_cfg->calibration = (void *)fw->data;
-		mbhc->mbhc_fw = fw;
+			__func__);
+		use_default_cal = true;
+	}
+	if (!use_default_cal) {
+		const void *data;
+		size_t size;
+
+		if (fw_data) {
+			data = fw_data->data;
+			size = fw_data->size;
+		} else {
+			data = fw->data;
+			size = fw->size;
+		}
+		if (wcd9xxx_mbhc_fw_validate(data, size) == false) {
+			pr_err("%s: Invalid MBHC cal data size use default cal\n",
+				__func__);
+			if (!fw_data)
+				release_firmware(fw);
+		} else {
+			if (fw_data) {
+				mbhc->mbhc_cfg->calibration =
+						(void *)fw_data->data;
+				mbhc->mbhc_cal = fw_data;
+			} else {
+				mbhc->mbhc_cfg->calibration =
+						(void *)fw->data;
+				mbhc->mbhc_fw = fw;
+			}
+		}
+
 	}
 
 	(void) wcd9xxx_init_and_calibrate(mbhc);
@@ -4391,15 +4431,16 @@
 		mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
 
 	if (!mbhc->mbhc_cfg->read_fw_bin ||
-	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw)) {
+		(mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
+		(mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
 		rc = wcd9xxx_init_and_calibrate(mbhc);
 	} else {
-		if (!mbhc->mbhc_fw)
+		if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
 			schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
 					     usecs_to_jiffies(FW_READ_TIMEOUT));
 		else
-			pr_debug("%s: Skipping to read mbhc fw, 0x%p\n",
-				 __func__, mbhc->mbhc_fw);
+			pr_debug("%s: Skipping to read mbhc fw, 0x%p 0x%p\n",
+				 __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
 	}
 
 	pr_debug("%s: leave %d\n", __func__, rc);
@@ -4409,10 +4450,12 @@
 
 void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
 {
-	if (mbhc->mbhc_fw) {
+	if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
 		cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
-		release_firmware(mbhc->mbhc_fw);
+		if (!mbhc->mbhc_cal)
+			release_firmware(mbhc->mbhc_fw);
 		mbhc->mbhc_fw = NULL;
+		mbhc->mbhc_cal = NULL;
 	}
 }
 EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
index 91edaca..7eba649 100644
--- a/sound/soc/codecs/wcd9xxx-mbhc.h
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -13,6 +13,7 @@
 #define __WCD9XXX_MBHC_H__
 
 #include "wcd9xxx-resmgr.h"
+#include "wcdcal-hwdep.h"
 
 #define WCD9XXX_CFILT_FAST_MODE 0x00
 #define WCD9XXX_CFILT_SLOW_MODE 0x40
@@ -285,6 +286,9 @@
 	int (*enable_mb_source) (struct snd_soc_codec *, bool, bool);
 	void (*setup_int_rbias) (struct snd_soc_codec *, bool);
 	void (*pull_mb_to_vddio) (struct snd_soc_codec *, bool);
+	struct firmware_cal * (*get_hwdep_fw_cal) (struct snd_soc_codec *,
+				enum wcd_cal_type);
+
 };
 
 struct wcd9xxx_mbhc {
@@ -312,6 +316,7 @@
 	const struct firmware *mbhc_fw;
 
 	struct delayed_work mbhc_insert_dwork;
+	struct firmware_cal *mbhc_cal;
 
 	u8 current_plug;
 	struct work_struct correct_plug_swch;
diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c
new file mode 100644
index 0000000..1132a3c
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/ioctl.h>
+#include <linux/bitops.h>
+#include <sound/hwdep.h>
+#include <sound/msmcal-hwdep.h>
+#include <sound/soc.h>
+#include "wcdcal-hwdep.h"
+
+const int cal_size_info[WCD9XXX_MAX_CAL] = {
+	[WCD9XXX_ANC_CAL] = 4096,
+	[WCD9XXX_MBHC_CAL] = 4096,
+	[WCD9XXX_MAD_CAL] = 4096,
+};
+
+const char *cal_name_info[WCD9XXX_MAX_CAL] = {
+	[WCD9XXX_ANC_CAL] = "anc",
+	[WCD9XXX_MBHC_CAL] = "mbhc",
+	[WCD9XXX_MAD_CAL] = "mad",
+};
+
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+					enum wcd_cal_type type)
+{
+	if (!fw_data) {
+		pr_err("%s: fw_data is NULL\n", __func__);
+		return NULL;
+	}
+	if (type >= WCD9XXX_MAX_CAL ||
+		type < WCD9XXX_MIN_CAL) {
+		pr_err("%s: wrong cal type sent %d\n", __func__, type);
+		return NULL;
+	}
+	mutex_lock(&fw_data->lock);
+	if (!test_bit(WCDCAL_RECIEVED,
+		&fw_data->wcdcal_state[type])) {
+		pr_err("%s: cal not sent by userspace %d\n",
+			__func__, type);
+		mutex_unlock(&fw_data->lock);
+		return NULL;
+	}
+	mutex_unlock(&fw_data->lock);
+	return fw_data->fw[type];
+}
+EXPORT_SYMBOL(wcdcal_get_fw_cal);
+
+static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
+			struct wcdcal_ioctl_buffer fw_user)
+{
+	struct fw_info *fw_data = hw->private_data;
+	struct firmware_cal **fw = fw_data->fw;
+	void *data;
+
+	if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
+		pr_err("%s: codec didn't set this %d!!\n",
+				__func__, fw_user.cal_type);
+		return -EFAULT;
+	}
+	if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
+		fw_user.cal_type < WCD9XXX_MIN_CAL) {
+		pr_err("%s: wrong cal type sent %d\n",
+				__func__, fw_user.cal_type);
+		return -EFAULT;
+	}
+	if (fw_user.size > cal_size_info[fw_user.cal_type] ||
+		fw_user.size <= 0) {
+		pr_err("%s: incorrect firmware size %d for %s\n",
+			__func__, fw_user.size,
+			cal_name_info[fw_user.cal_type]);
+		return -EFAULT;
+	}
+	data = fw[fw_user.cal_type]->data;
+	memcpy(data, fw_user.buffer, fw_user.size);
+	fw[fw_user.cal_type]->size = fw_user.size;
+	mutex_lock(&fw_data->lock);
+	set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
+	mutex_unlock(&fw_data->lock);
+	return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct wcdcal_ioctl_buffer32 {
+	u32 size;
+	compat_uptr_t buffer;
+	enum wcd_cal_type cal_type;
+};
+
+enum {
+	SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
+		_IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
+};
+
+static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+	struct wcdcal_ioctl_buffer32 fw_user32;
+	struct wcdcal_ioctl_buffer fw_user_compat;
+
+	if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
+		pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
+		pr_err("%s: failed to copy\n", __func__);
+		return -EFAULT;
+	}
+	fw_user_compat.size = fw_user32.size;
+	fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
+	fw_user_compat.cal_type = fw_user32.cal_type;
+	return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
+}
+#else
+#define wcdcal_hwdep_ioctl_compat NULL
+#endif
+
+static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+	struct wcdcal_ioctl_buffer fw_user;
+
+	if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
+		pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
+		pr_err("%s: failed to copy\n", __func__);
+		return -EFAULT;
+	}
+	return wcdcal_hwdep_ioctl_shared(hw, fw_user);
+}
+
+static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct fw_info *fw_data = hw->private_data;
+	mutex_lock(&fw_data->lock);
+	/* clear all the calibrations */
+	memset(fw_data->wcdcal_state, 0,
+		sizeof(fw_data->wcdcal_state));
+	mutex_unlock(&fw_data->lock);
+	return 0;
+}
+
+int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
+{
+	char hwname[40];
+	struct snd_hwdep *hwdep;
+	struct firmware_cal **fw;
+	struct fw_info *fw_data = data;
+	int err, cal_bit;
+
+	if (!fw_data || !codec) {
+		pr_err("%s: wrong arguments passed\n", __func__);
+		return -EINVAL;
+	}
+
+	fw = fw_data->fw;
+	snprintf(hwname, strlen("Codec %s"), "Codec %s", codec->name);
+	err = snd_hwdep_new(codec->card->snd_card, hwname, node, &hwdep);
+	if (err < 0) {
+		dev_err(codec->dev, "%s: new hwdep failed %d\n",
+				__func__, err);
+		return err;
+	}
+	snprintf(hwdep->name, strlen("Codec %s"), "Codec %s", codec->name);
+	hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
+	hwdep->private_data = fw_data;
+	hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
+	hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
+	hwdep->ops.release = wcdcal_hwdep_release;
+	mutex_init(&fw_data->lock);
+
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		set_bit(WCDCAL_UNINITIALISED,
+				&fw_data->wcdcal_state[cal_bit]);
+		fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
+		if (!fw[cal_bit]) {
+			dev_err(codec->dev, "%s: no memory for %s cal\n",
+				__func__, cal_name_info[cal_bit]);
+			goto end;
+		}
+	}
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
+						GFP_KERNEL);
+		if (!fw[cal_bit]->data) {
+			dev_err(codec->dev, "%s: no memory for %s cal data\n",
+				__func__, cal_name_info[cal_bit]);
+			goto exit;
+		}
+		set_bit(WCDCAL_INITIALISED,
+			&fw_data->wcdcal_state[cal_bit]);
+	}
+	return 0;
+exit:
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		kfree(fw[cal_bit]->data);
+		fw[cal_bit]->data = NULL;
+	}
+end:
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		kfree(fw[cal_bit]);
+		fw[cal_bit] = NULL;
+	}
+	return -ENOMEM;
+}
+EXPORT_SYMBOL(wcd_cal_create_hwdep);
diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/sound/soc/codecs/wcdcal-hwdep.h
new file mode 100644
index 0000000..632e2f1
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. 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
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_HWDEP_H__
+#define __WCD9XXX_HWDEP_H__
+#include <sound/msmcal-hwdep.h>
+
+enum wcd_cal_states {
+	WCDCAL_UNINITIALISED,
+	WCDCAL_INITIALISED,
+	WCDCAL_RECIEVED
+};
+
+struct fw_info {
+	struct firmware_cal *fw[WCD9XXX_MAX_CAL];
+	DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL);
+	/* for calibration tracking */
+	unsigned long wcdcal_state[WCD9XXX_MAX_CAL];
+	struct mutex lock;
+};
+
+struct firmware_cal {
+	u8 *data;
+	size_t size;
+};
+
+struct snd_soc_codec;
+int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec);
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+					enum wcd_cal_type type);
+#endif /* __WCD9XXX_HWDEP_H__ */
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
index 86dd840..fbcd953 100644
--- a/sound/soc/msm/Kconfig
+++ b/sound/soc/msm/Kconfig
@@ -183,6 +183,7 @@
 	select SND_DYNAMIC_MINORS
 	select AUDIO_OCMEM
 	select DOLBY_DAP
+	select SND_HWDEP
 	help
 	 To add support for SoC audio on MSM8974.
 	 This will enable sound soc drivers which