asoc: codecs: Add support for rouleur codec

Add support for rouleur codec driver and pm2250
spmi driver.

Change-Id: Ie56f7af012c0c7b6e68077463f43050e051a3ef9
Signed-off-by: Aditya Bavanari <abavanar@codeaurora.org>
diff --git a/asoc/codecs/rouleur/Android.mk b/asoc/codecs/rouleur/Android.mk
new file mode 100644
index 0000000..4fb5793
--- /dev/null
+++ b/asoc/codecs/rouleur/Android.mk
@@ -0,0 +1,65 @@
+# Android makefile for audio kernel modules
+
+# Assume no targets will be supported
+
+# Check if this driver needs be built for current target
+ifeq ($(call is-board-platform,bengal),true)
+AUDIO_SELECT  := CONFIG_SND_SOC_BENGAL=m
+endif
+
+AUDIO_CHIPSET := audio
+# Build/Package only in case of supported target
+ifeq ($(call is-board-platform-in-list,$(MSMSTEPPE) $(TRINKET) bengal),true)
+
+LOCAL_PATH := $(call my-dir)
+
+# This makefile is only for DLKM
+ifneq ($(findstring vendor,$(LOCAL_PATH)),)
+
+ifneq ($(findstring opensource,$(LOCAL_PATH)),)
+	AUDIO_BLD_DIR := $(shell pwd)/vendor/qcom/opensource/audio-kernel
+endif # opensource
+
+DLKM_DIR := $(TOP)/device/qcom/common/dlkm
+
+# Build audio.ko as $(AUDIO_CHIPSET)_audio.ko
+###########################################################
+# This is set once per LOCAL_PATH, not per (kernel) module
+KBUILD_OPTIONS := AUDIO_ROOT=$(AUDIO_BLD_DIR)
+
+# We are actually building audio.ko here, as per the
+# requirement we are specifying <chipset>_audio.ko as LOCAL_MODULE.
+# This means we need to rename the module to <chipset>_audio.ko
+# after audio.ko is built.
+KBUILD_OPTIONS += MODNAME=rouleur_dlkm
+KBUILD_OPTIONS += BOARD_PLATFORM=$(TARGET_BOARD_PLATFORM)
+KBUILD_OPTIONS += $(AUDIO_SELECT)
+
+###########################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE              := $(AUDIO_CHIPSET)_rouleur.ko
+LOCAL_MODULE_KBUILD_NAME  := rouleur_dlkm.ko
+LOCAL_MODULE_TAGS         := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+include $(DLKM_DIR)/AndroidKernelModule.mk
+###########################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE              := $(AUDIO_CHIPSET)_rouleur_slave.ko
+LOCAL_MODULE_KBUILD_NAME  := rouleur_slave_dlkm.ko
+LOCAL_MODULE_TAGS         := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+include $(DLKM_DIR)/AndroidKernelModule.mk
+###########################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE              := $(AUDIO_CHIPSET)_pm2250_spmi.ko
+LOCAL_MODULE_KBUILD_NAME  := pm2250_spmi_dlkm.ko
+LOCAL_MODULE_TAGS         := optional
+LOCAL_MODULE_DEBUG_ENABLE := true
+LOCAL_MODULE_PATH         := $(KERNEL_MODULES_OUT)
+include $(DLKM_DIR)/AndroidKernelModule.mk
+###########################################################
+
+endif # DLKM check
+endif # supported target check
diff --git a/asoc/codecs/rouleur/Kbuild b/asoc/codecs/rouleur/Kbuild
new file mode 100644
index 0000000..b59bcb1
--- /dev/null
+++ b/asoc/codecs/rouleur/Kbuild
@@ -0,0 +1,120 @@
+# We can build either as part of a standalone Kernel build or as
+# an external module.  Determine which mechanism is being used
+ifeq ($(MODNAME),)
+	KERNEL_BUILD := 1
+else
+	KERNEL_BUILD := 0
+endif
+
+
+
+ifeq ($(KERNEL_BUILD), 1)
+	# These are configurable via Kconfig for kernel-based builds
+	# Need to explicitly configure for Android-based builds
+	AUDIO_BLD_DIR := $(ANDROID_BUILD_TOP)/kernel/msm-4.19
+	AUDIO_ROOT := $(AUDIO_BLD_DIR)/techpack/audio
+endif
+
+ifeq ($(KERNEL_BUILD), 0)
+	ifeq ($(CONFIG_ARCH_BENGAL), y)
+		include $(AUDIO_ROOT)/config/bengalauto.conf
+		export
+		INCS    +=  -include $(AUDIO_ROOT)/config/bengalautoconf.h
+	endif
+endif
+
+# As per target team, build is done as follows:
+# Defconfig : build with default flags
+# Slub      : defconfig  + CONFIG_SLUB_DEBUG := y +
+#	      CONFIG_SLUB_DEBUG_ON := y + CONFIG_PAGE_POISONING := y
+# Perf      : Using appropriate msmXXXX-perf_defconfig
+#
+# Shipment builds (user variants) should not have any debug feature
+# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds
+# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since
+# there is no other way to identify defconfig builds, QTI internal
+# representation of perf builds (identified using the string 'perf'),
+# is used to identify if the build is a slub or defconfig one. This
+# way no critical debug feature will be enabled for perf and shipment
+# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT
+# config.
+
+############ UAPI ############
+UAPI_DIR :=	uapi
+UAPI_INC :=	-I$(AUDIO_ROOT)/include/$(UAPI_DIR)
+
+############ COMMON ############
+COMMON_DIR :=	include
+COMMON_INC :=	-I$(AUDIO_ROOT)/$(COMMON_DIR)
+
+############ ROULEUR ############
+
+# for ROULEUR Codec
+ifdef CONFIG_SND_SOC_ROULEUR
+	ROULEUR_OBJS += rouleur.o
+	ROULEUR_OBJS += rouleur-regmap.o
+	ROULEUR_OBJS += rouleur-tables.o
+	ROULEUR_OBJS += rouleur-mbhc.o
+endif
+
+ifdef CONFIG_PM2250_SPMI
+	PM2250_SPMI_OBJS += pm2250_spmi.o
+endif
+
+ifdef CONFIG_SND_SOC_ROULEUR_SLAVE
+	ROULEUR_SLAVE_OBJS += rouleur_slave.o
+endif
+
+LINUX_INC +=	-Iinclude/linux
+
+INCS +=		$(COMMON_INC) \
+		$(UAPI_INC)
+
+EXTRA_CFLAGS += $(INCS)
+
+
+CDEFINES +=	-DANI_LITTLE_BYTE_ENDIAN \
+		-DANI_LITTLE_BIT_ENDIAN \
+		-DDOT11F_LITTLE_ENDIAN_HOST \
+		-DANI_COMPILER_TYPE_GCC \
+		-DANI_OS_TYPE_ANDROID=6 \
+		-DPTT_SOCK_SVC_ENABLE \
+		-Wall\
+		-Werror\
+		-D__linux__
+
+KBUILD_CPPFLAGS += $(CDEFINES)
+
+# Currently, for versions of gcc which support it, the kernel Makefile
+# is disabling the maybe-uninitialized warning.  Re-enable it for the
+# AUDIO driver.  Note that we must use EXTRA_CFLAGS here so that it
+# will override the kernel settings.
+ifeq ($(call cc-option-yn, -Wmaybe-uninitialized),y)
+EXTRA_CFLAGS += -Wmaybe-uninitialized
+endif
+#EXTRA_CFLAGS += -Wmissing-prototypes
+
+ifeq ($(call cc-option-yn, -Wheader-guard),y)
+EXTRA_CFLAGS += -Wheader-guard
+endif
+
+ifeq ($(KERNEL_BUILD), 0)
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/ipc/Module.symvers
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/dsp/Module.symvers
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/Module.symvers
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/asoc/codecs/Module.symvers
+KBUILD_EXTRA_SYMBOLS +=$(OUT)/obj/vendor/qcom/opensource/audio-kernel/soc/Module.symvers
+endif
+
+# Module information used by KBuild framework
+obj-$(CONFIG_SND_SOC_ROULEUR) += rouleur_dlkm.o
+rouleur_dlkm-y := $(ROULEUR_OBJS)
+
+obj-$(CONFIG_SND_SOC_ROULEUR_SLAVE) += rouleur_slave_dlkm.o
+rouleur_slave_dlkm-y := $(ROULEUR_SLAVE_OBJS)
+
+obj-$(CONFIG_PM2250_SPMI) += pm2250_spmi_dlkm.o
+pm2250_spmi_dlkm-y := $(PM2250_SPMI_OBJS)
+
+# inject some build related information
+DEFINES += -DBUILD_TIMESTAMP=\"$(shell date -u +'%Y-%m-%dT%H:%M:%SZ')\"
diff --git a/asoc/codecs/rouleur/internal.h b/asoc/codecs/rouleur/internal.h
new file mode 100644
index 0000000..7104685
--- /dev/null
+++ b/asoc/codecs/rouleur/internal.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ROULEUR_INTERNAL_H
+#define _ROULEUR_INTERNAL_H
+
+#include <asoc/wcd-clsh.h>
+#include <asoc/wcd-mbhc-v2.h>
+#include <asoc/wcd-irq.h>
+#include "rouleur-mbhc.h"
+
+#define ROULEUR_MAX_MICBIAS 3
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define  WCD_VOUT_CTL_TO_MICB(v)  (1600 + v * 50)
+#define MAX_PORT 8
+#define MAX_CH_PER_PORT 8
+
+extern struct regmap_config rouleur_regmap_config;
+
+struct codec_port_info {
+	u32 slave_port_type;
+	u32 master_port_type;
+	u32 ch_mask;
+	u32 num_ch;
+	u32 ch_rate;
+};
+
+struct rouleur_priv {
+	struct device *dev;
+
+	int variant;
+	struct snd_soc_component *component;
+	struct device_node *spmi_np;
+	struct regmap *regmap;
+
+	struct swr_device *rx_swr_dev;
+	struct swr_device *tx_swr_dev;
+
+	s32 micb_ref[ROULEUR_MAX_MICBIAS];
+	s32 pullup_ref[ROULEUR_MAX_MICBIAS];
+
+	struct fw_info *fw_data;
+
+	struct mutex micb_lock;
+	s32 dmic_0_1_clk_cnt;
+	/* mbhc module */
+	struct rouleur_mbhc *mbhc;
+
+	bool comp1_enable;
+	bool comp2_enable;
+
+	struct irq_domain *virq;
+	struct wcd_irq_info irq_info;
+	u32 rx_clk_cnt;
+	int num_irq_regs;
+	/* to track the status */
+	unsigned long status_mask;
+
+	u8 num_tx_ports;
+	u8 num_rx_ports;
+	struct codec_port_info
+			tx_port_mapping[MAX_PORT][MAX_CH_PER_PORT];
+	struct codec_port_info
+			rx_port_mapping[MAX_PORT][MAX_CH_PER_PORT];
+	struct regulator_bulk_data *supplies;
+	struct notifier_block nblock;
+	/* wcd callback to bolero */
+	void *handle;
+	int (*update_wcd_event)(void *handle, u16 event, u32 data);
+	int (*register_notifier)(void *handle,
+				struct notifier_block *nblock,
+				bool enable);
+	int (*wakeup)(void *handle, bool enable);
+	u32 version;
+	/* Entry for version info */
+	struct snd_info_entry *entry;
+	struct snd_info_entry *version_entry;
+	struct device *spmi_dev;
+	int reset_reg;
+	int mbias_cnt;
+	struct mutex rx_clk_lock;
+	struct mutex main_bias_lock;
+};
+
+struct rouleur_micbias_setting {
+	u32 micb1_mv;
+	u32 micb2_mv;
+	u32 micb3_mv;
+};
+
+struct rouleur_pdata {
+	struct device_node *spmi_np;
+	struct device_node *rx_slave;
+	struct device_node *tx_slave;
+	struct rouleur_micbias_setting micbias;
+
+	struct cdc_regulator *regulator;
+	int num_supplies;
+	int reset_reg;
+};
+
+struct wcd_ctrl_platform_data {
+	void *handle;
+	int (*update_wcd_event)(void *handle, u16 event, u32 data);
+	int (*register_notifier)(void *handle,
+				 struct notifier_block *nblock,
+				 bool enable);
+};
+
+enum {
+	WCD_RX1,
+	WCD_RX2,
+	WCD_RX3
+};
+
+enum {
+	BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR = 1,
+	BOLERO_WCD_EVT_PA_OFF_PRE_SSR,
+	BOLERO_WCD_EVT_SSR_DOWN,
+	BOLERO_WCD_EVT_SSR_UP,
+};
+
+enum {
+	WCD_BOLERO_EVT_RX_MUTE = 1,	/* for RX mute/unmute */
+	WCD_BOLERO_EVT_IMPED_TRUE,	/* for imped true */
+	WCD_BOLERO_EVT_IMPED_FALSE,	/* for imped false */
+	WCD_BOLERO_EVT_RX_COMPANDER_SOFT_RST,
+	WCD_BOLERO_EVT_BCS_CLK_OFF,
+};
+
+enum {
+	/* INTR_CTRL_INT_MASK_0 */
+	ROULEUR_IRQ_MBHC_BUTTON_PRESS_DET = 0,
+	ROULEUR_IRQ_MBHC_BUTTON_RELEASE_DET,
+	ROULEUR_IRQ_MBHC_ELECT_INS_REM_DET,
+	ROULEUR_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	ROULEUR_IRQ_MBHC_SW_DET,
+	ROULEUR_IRQ_HPHR_OCP_INT,
+	ROULEUR_IRQ_HPHR_CNP_INT,
+	ROULEUR_IRQ_HPHL_OCP_INT,
+
+	/* INTR_CTRL_INT_MASK_1 */
+	ROULEUR_IRQ_HPHL_CNP_INT,
+	ROULEUR_IRQ_EAR_CNP_INT,
+	ROULEUR_IRQ_EAR_OCP_INT,
+	ROULEUR_IRQ_LO_CNP_INT,
+	ROULEUR_IRQ_LO_OCP_INT,
+	ROULEUR_IRQ_HPHL_PDM_WD_INT,
+	ROULEUR_IRQ_HPHR_PDM_WD_INT,
+	ROULEUR_IRQ_RESERVED_0,
+
+	/* INTR_CTRL_INT_MASK_2 */
+	ROULEUR_IRQ_RESERVED_1,
+	ROULEUR_IRQ_RESERVED_2,
+	ROULEUR_IRQ_HPHL_SURGE_DET_INT,
+	ROULEUR_IRQ_HPHR_SURGE_DET_INT,
+	ROULEUR_NUM_IRQS,
+};
+
+extern void rouleur_disable_bcs_before_slow_insert(
+				struct snd_soc_component *component,
+				bool bcs_disable);
+extern struct rouleur_mbhc *rouleur_soc_get_mbhc(
+				struct snd_soc_component *component);
+extern int rouleur_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+					int volt, int micb_num);
+extern int rouleur_get_micb_vout_ctl_val(u32 micb_mv);
+extern int rouleur_micbias_control(struct snd_soc_component *component,
+			int micb_num, int req, bool is_dapm);
+#endif
diff --git a/asoc/codecs/rouleur/pm2250-spmi.h b/asoc/codecs/rouleur/pm2250-spmi.h
new file mode 100644
index 0000000..87c913d
--- /dev/null
+++ b/asoc/codecs/rouleur/pm2250-spmi.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _PM2250_SPMI_H
+#define _PM2250_SPMI_H
+
+#ifdef CONFIG_PM2250_SPMI
+int pm2250_spmi_write(struct device *dev, int reg, int value);
+#else
+int pm2250_spmi_write(struct device *dev, int reg, int value)
+{
+	return 0;
+}
+#endif	/* CONFIG_PM2250_SPMI */
+
+#endif
diff --git a/asoc/codecs/rouleur/pm2250_spmi.c b/asoc/codecs/rouleur/pm2250_spmi.c
new file mode 100644
index 0000000..1e5f70d
--- /dev/null
+++ b/asoc/codecs/rouleur/pm2250_spmi.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/**
+ * @regmap: regmap used to access PMIC registers
+ */
+struct pm2250_spmi {
+	struct regmap *regmap;
+};
+
+static const struct of_device_id pm2250_id_table[] = {
+	{ .compatible = "qcom,pm2250-spmi" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pm2250_id_table);
+
+int pm2250_spmi_write(struct device *dev, int reg, int value)
+{
+	int rc;
+	struct pm2250_spmi *spmi_dd;
+
+	if (!of_device_is_compatible(dev->of_node, "qcom,pm2250-spmi")) {
+		pr_err("%s: Device node is invalid\n", __func__);
+		return -EINVAL;
+	}
+
+	spmi_dd = dev_get_drvdata(dev);
+	if (!spmi_dd)
+		return -EINVAL;
+
+	rc = regmap_write(spmi_dd->regmap, reg, value);
+	if (rc)
+		dev_err(dev, "%s: Write to PMIC register failed\n", __func__);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm2250_spmi_write);
+
+static int pm2250_spmi_probe(struct platform_device *pdev)
+{
+	struct pm2250_spmi *spmi_dd;
+	const struct of_device_id *match;
+
+	match = of_match_node(pm2250_id_table, pdev->dev.of_node);
+	if (!match)
+		return -ENXIO;
+
+	spmi_dd = devm_kzalloc(&pdev->dev, sizeof(*spmi_dd), GFP_KERNEL);
+	if (spmi_dd == NULL)
+		return -ENOMEM;
+
+	spmi_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!spmi_dd->regmap) {
+		dev_err(&pdev->dev, "Parent regmap unavailable.\n");
+		return -ENXIO;
+	}
+
+	platform_set_drvdata(pdev, spmi_dd);
+
+	dev_dbg(&pdev->dev, "Probe success !!\n");
+
+	return 0;
+}
+
+static int pm2250_spmi_remove(struct platform_device *pdev)
+{
+	of_platform_depopulate(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver pm2250_spmi_driver = {
+	.probe		= pm2250_spmi_probe,
+	.remove		= pm2250_spmi_remove,
+	.driver	= {
+		.name		= "pm2250-spmi",
+		.of_match_table	= pm2250_id_table,
+	},
+};
+module_platform_driver(pm2250_spmi_driver);
+
+MODULE_ALIAS("platform:pm2250-spmi");
+MODULE_DESCRIPTION("PMIC SPMI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/asoc/codecs/rouleur/rouleur-mbhc.c b/asoc/codecs/rouleur/rouleur-mbhc.c
new file mode 100644
index 0000000..3e3ef51
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur-mbhc.c
@@ -0,0 +1,1155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "rouleur-registers.h"
+#include <asoc/wcdcal-hwdep.h>
+#include <asoc/wcd-mbhc-v2-api.h>
+#include "internal.h"
+
+#define ROULEUR_ZDET_SUPPORTED          true
+/* Z value defined in milliohm */
+#define ROULEUR_ZDET_VAL_32             32000
+#define ROULEUR_ZDET_VAL_400            400000
+#define ROULEUR_ZDET_VAL_1200           1200000
+#define ROULEUR_ZDET_VAL_100K           100000000
+/* Z floating defined in ohms */
+#define ROULEUR_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define ROULEUR_ZDET_NUM_MEASUREMENTS   900
+#define ROULEUR_MBHC_GET_C1(c)          ((c & 0xC000) >> 14)
+#define ROULEUR_MBHC_GET_X1(x)          (x & 0x3FFF)
+/* Z value compared in milliOhm */
+#define ROULEUR_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define ROULEUR_MBHC_ZDET_CONST         (86 * 16384)
+#define ROULEUR_MBHC_MOISTURE_RREF      R_24_KOHM
+
+static struct wcd_mbhc_register
+	wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = {
+	WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN",
+			  ROULEUR_ANA_MBHC_MECH, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+			  ROULEUR_ANA_MBHC_MECH, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+			  ROULEUR_ANA_MBHC_MECH, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+			  ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+			  ROULEUR_ANA_MBHC_ELECT, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+			  ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+			  ROULEUR_ANA_MBHC_MECH, 0x04, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+			  ROULEUR_ANA_MBHC_MECH, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+			  ROULEUR_ANA_MBHC_MECH, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+			  ROULEUR_ANA_MBHC_MECH, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+			  ROULEUR_ANA_MBHC_ELECT, 0x06, 1, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+			  ROULEUR_ANA_MBHC_ELECT, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+			  ROULEUR_ANA_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+			  ROULEUR_ANA_MBHC_CTL_1, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+			  ROULEUR_ANA_MBHC_CTL_2, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+			  SND_SOC_NOPM, 0x00, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x07, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+			  ROULEUR_ANA_MBHC_ELECT, 0x70, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+			  ROULEUR_ANA_MICBIAS_MICB_1_2_EN, 0x04, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+			  SND_SOC_NOPM, 0x00, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+			  ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+			  ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+			  ROULEUR_ANA_HPHPA_CNP_CTL_2, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+			  ROULEUR_ANA_MBHC_RESULT_3, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
+			  0, 0, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN",
+			  SND_SOC_NOPM, 0x00, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+			  ROULEUR_ANA_MBHC_FSM_STATUS, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+			  ROULEUR_ANA_MBHC_CTL_2, 0x70, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS",
+			  ROULEUR_ANA_MBHC_FSM_STATUS, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND",
+			  SND_SOC_NOPM, 0x00, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND",
+			  SND_SOC_NOPM, 0x00, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN",
+			  ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x02, 1, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN",
+			  ROULEUR_ANA_HPHPA_CNP_CTL_2, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS",
+			  ROULEUR_DIG_SWR_INTR_STATUS_0, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS",
+			  ROULEUR_DIG_SWR_INTR_STATUS_0, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ADC_EN",
+			  ROULEUR_ANA_MBHC_CTL_1, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ADC_COMPLETE", ROULEUR_ANA_MBHC_FSM_STATUS,
+			  0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ADC_TIMEOUT", ROULEUR_ANA_MBHC_FSM_STATUS,
+			  0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ADC_RESULT", ROULEUR_ANA_MBHC_ADC_RESULT,
+			  0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MICB2_VOUT",
+			  ROULEUR_ANA_MICBIAS_LDO_1_SETTING, 0xF8, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ADC_MODE",
+			  ROULEUR_ANA_MBHC_CTL_1, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_DETECTION_DONE",
+			  ROULEUR_ANA_MBHC_CTL_1, 0x04, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_ISRC_EN",
+			  ROULEUR_ANA_MBHC_ZDET, 0x02, 1, 0),
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+	.mbhc_sw_intr =  ROULEUR_IRQ_MBHC_SW_DET,
+	.mbhc_btn_press_intr = ROULEUR_IRQ_MBHC_BUTTON_PRESS_DET,
+	.mbhc_btn_release_intr = ROULEUR_IRQ_MBHC_BUTTON_RELEASE_DET,
+	.mbhc_hs_ins_intr = ROULEUR_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	.mbhc_hs_rem_intr = ROULEUR_IRQ_MBHC_ELECT_INS_REM_DET,
+	.hph_left_ocp = ROULEUR_IRQ_HPHL_OCP_INT,
+	.hph_right_ocp = ROULEUR_IRQ_HPHR_OCP_INT,
+};
+
+struct rouleur_mbhc_zdet_param {
+	u16 ldo_ctl;
+	u16 noff;
+	u16 nshift;
+	u16 btn5;
+	u16 btn6;
+	u16 btn7;
+};
+
+static int rouleur_mbhc_request_irq(struct snd_soc_component *component,
+				  int irq, irq_handler_t handler,
+				  const char *name, void *data)
+{
+	struct rouleur_priv *rouleur = dev_get_drvdata(component->dev);
+
+	return wcd_request_irq(&rouleur->irq_info, irq, name, handler, data);
+}
+
+static void rouleur_mbhc_irq_control(struct snd_soc_component *component,
+				   int irq, bool enable)
+{
+	struct rouleur_priv *rouleur = dev_get_drvdata(component->dev);
+
+	if (enable)
+		wcd_enable_irq(&rouleur->irq_info, irq);
+	else
+		wcd_disable_irq(&rouleur->irq_info, irq);
+}
+
+static int rouleur_mbhc_free_irq(struct snd_soc_component *component,
+			       int irq, void *data)
+{
+	struct rouleur_priv *rouleur = dev_get_drvdata(component->dev);
+
+	wcd_free_irq(&rouleur->irq_info, irq, data);
+
+	return 0;
+}
+
+static void rouleur_mbhc_clk_setup(struct snd_soc_component *component,
+				 bool enable)
+{
+	if (enable)
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_1,
+				    0x80, 0x80);
+	else
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_1,
+				    0x80, 0x00);
+}
+
+static int rouleur_mbhc_btn_to_num(struct snd_soc_component *component)
+{
+	return snd_soc_component_read32(component, ROULEUR_ANA_MBHC_RESULT_3) &
+				0x7;
+}
+
+static void rouleur_mbhc_mbhc_bias_control(struct snd_soc_component *component,
+					 bool enable)
+{
+	if (enable)
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ELECT,
+				    0x01, 0x01);
+	else
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ELECT,
+				    0x01, 0x00);
+}
+
+static void rouleur_mbhc_program_btn_thr(struct snd_soc_component *component,
+				       s16 *btn_low, s16 *btn_high,
+				       int num_btn, bool is_micbias)
+{
+	int i;
+	int vth;
+
+	if (num_btn > WCD_MBHC_DEF_BUTTONS) {
+		dev_err(component->dev, "%s: invalid number of buttons: %d\n",
+			__func__, num_btn);
+		return;
+	}
+
+	for (i = 0; i < num_btn; i++) {
+		vth = ((btn_high[i] * 2) / 25) & 0x3F;
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1 + i,
+				0xFC, vth << 2);
+		dev_dbg(component->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+			__func__, i, btn_high[i], vth);
+	}
+}
+
+static bool rouleur_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+	struct snd_soc_component *component = mbhc->component;
+	struct rouleur_priv *rouleur = dev_get_drvdata(component->dev);
+
+	rouleur->wakeup((void *)rouleur, lock);
+	return true;
+}
+
+static int rouleur_mbhc_register_notifier(struct wcd_mbhc *mbhc,
+					struct notifier_block *nblock,
+					bool enable)
+{
+	struct rouleur_mbhc *rouleur_mbhc;
+
+	rouleur_mbhc = container_of(mbhc, struct rouleur_mbhc, wcd_mbhc);
+
+	if (enable)
+		return blocking_notifier_chain_register(&rouleur_mbhc->notifier,
+							nblock);
+	else
+		return blocking_notifier_chain_unregister(
+				&rouleur_mbhc->notifier, nblock);
+}
+
+static bool rouleur_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+	u8 val = 0;
+
+	if (micb_num == MIC_BIAS_2) {
+		val = ((snd_soc_component_read32(mbhc->component,
+				ROULEUR_ANA_MICBIAS_MICB_1_2_EN) & 0x04)
+				>> 2);
+		if (val == 0x01)
+			return true;
+	}
+	return false;
+}
+
+static bool rouleur_mbhc_hph_pa_on_status(struct snd_soc_component *component)
+{
+	return (snd_soc_component_read32(component, ROULEUR_ANA_HPHPA_PA_STATUS)
+					& 0xFF) ? true : false;
+}
+
+static void rouleur_mbhc_hph_l_pull_up_control(
+				struct snd_soc_component *component,
+				int pull_up_cur)
+{
+	/* Default pull up current to 2uA */
+	if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA ||
+	    pull_up_cur == I_DEFAULT)
+		pull_up_cur = I_2P0_UA;
+
+	dev_dbg(component->dev, "%s: HS pull up current:%d\n",
+		__func__, pull_up_cur);
+
+	snd_soc_component_update_bits(component,
+				ROULEUR_ANA_MBHC_PLUG_DETECT_CTL,
+				0xC0, pull_up_cur);
+}
+
+static int rouleur_mbhc_request_micbias(struct snd_soc_component *component,
+					int micb_num, int req)
+{
+	int ret = 0;
+
+	ret = rouleur_micbias_control(component, micb_num, req, false);
+
+	return ret;
+}
+
+static void rouleur_mbhc_micb_ramp_control(struct snd_soc_component *component,
+					   bool enable)
+{
+	if (enable) {
+		snd_soc_component_update_bits(component,
+					ROULEUR_ANA_MBHC_MICB2_RAMP,
+					0x1C, 0x0C);
+		snd_soc_component_update_bits(component,
+					ROULEUR_ANA_MBHC_MICB2_RAMP,
+					0x80, 0x80);
+	} else {
+		snd_soc_component_update_bits(component,
+					ROULEUR_ANA_MBHC_MICB2_RAMP,
+					0x80, 0x00);
+		snd_soc_component_update_bits(component,
+					ROULEUR_ANA_MBHC_MICB2_RAMP,
+					0x1C, 0x00);
+	}
+}
+
+static struct firmware_cal *rouleur_get_hwdep_fw_cal(struct wcd_mbhc *mbhc,
+						   enum wcd_cal_type type)
+{
+	struct rouleur_mbhc *rouleur_mbhc;
+	struct firmware_cal *hwdep_cal;
+	struct snd_soc_component *component = mbhc->component;
+
+	rouleur_mbhc = container_of(mbhc, struct rouleur_mbhc, wcd_mbhc);
+
+	if (!component) {
+		pr_err("%s: NULL component pointer\n", __func__);
+		return NULL;
+	}
+	hwdep_cal = wcdcal_get_fw_cal(rouleur_mbhc->fw_data, type);
+	if (!hwdep_cal)
+		dev_err(component->dev, "%s: cal not sent by %d\n",
+			__func__, type);
+
+	return hwdep_cal;
+}
+
+static int rouleur_mbhc_micb_ctrl_threshold_mic(
+					struct snd_soc_component *component,
+					int micb_num, bool req_en)
+{
+	struct rouleur_pdata *pdata = dev_get_platdata(component->dev);
+	int rc, micb_mv;
+
+	if (micb_num != MIC_BIAS_2)
+		return -EINVAL;
+	/*
+	 * If device tree micbias level is already above the minimum
+	 * voltage needed to detect threshold microphone, then do
+	 * not change the micbias, just return.
+	 */
+	if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV)
+		return 0;
+
+	micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv;
+
+	rc = rouleur_mbhc_micb_adjust_voltage(component, micb_mv, MIC_BIAS_2);
+
+	return rc;
+}
+
+static inline void rouleur_mbhc_get_result_params(struct rouleur_priv *rouleur,
+						s16 *d1_a, u16 noff,
+						int32_t *zdet)
+{
+	int i;
+	int val = 0, val1 = 0;
+	s16 c1 = 0;
+	s32 x1 = 0, d1 = 0;
+	int32_t denom;
+	int minCode_param[] = {
+			3277, 1639, 820, 410, 205, 103, 52, 26
+	};
+
+	regmap_update_bits(rouleur->regmap, ROULEUR_ANA_MBHC_ZDET, 0x20, 0x20);
+	for (i = 0; i < ROULEUR_ZDET_NUM_MEASUREMENTS; i++) {
+		regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_2, &val);
+		if (val & 0x80)
+			break;
+	}
+	val = val << 0x8;
+	regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_1, &val1);
+	val |= val1;
+	regmap_update_bits(rouleur->regmap, ROULEUR_ANA_MBHC_ZDET, 0x20, 0x00);
+	x1 = ROULEUR_MBHC_GET_X1(val);
+	c1 = ROULEUR_MBHC_GET_C1(val);
+	/* If ramp is not complete, give additional 5ms */
+	if ((c1 < 2) && x1)
+		usleep_range(5000, 5050);
+
+	if (!c1 || !x1) {
+		dev_dbg(rouleur->dev,
+			"%s: Impedance detect ramp error, c1=%d, x1=0x%x\n",
+			__func__, c1, x1);
+		goto ramp_down;
+	}
+	d1 = d1_a[c1];
+	denom = (x1 * d1) - (1 << (14 - noff));
+	if (denom > 0)
+		*zdet = (ROULEUR_MBHC_ZDET_CONST * 1000) / denom;
+	else if (x1 < minCode_param[noff])
+		*zdet = ROULEUR_ZDET_FLOATING_IMPEDANCE;
+
+	dev_dbg(rouleur->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n",
+		__func__, d1, c1, x1, *zdet);
+ramp_down:
+	i = 0;
+	while (x1) {
+		regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_1, &val);
+		regmap_read(rouleur->regmap, ROULEUR_ANA_MBHC_RESULT_2, &val1);
+		val = val << 0x8;
+		val |= val1;
+		x1 = ROULEUR_MBHC_GET_X1(val);
+		i++;
+		if (i == ROULEUR_ZDET_NUM_MEASUREMENTS)
+			break;
+	}
+}
+
+#if 0
+static void rouleur_mbhc_zdet_ramp(struct snd_soc_component *component,
+				 struct rouleur_mbhc_zdet_param *zdet_param,
+				 int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+	struct rouleur_priv *rouleur = dev_get_drvdata(component->dev);
+	int32_t zdet = 0;
+
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ZDET_ANA_CTL,
+				0x70, zdet_param->ldo_ctl << 4);
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_BTN5, 0xFC,
+				zdet_param->btn5);
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_BTN6, 0xFC,
+				zdet_param->btn6);
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_BTN7, 0xFC,
+				zdet_param->btn7);
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ZDET_ANA_CTL,
+				0x0F, zdet_param->noff);
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_ZDET_RAMP_CTL,
+				0x0F, zdet_param->nshift);
+
+	if (!zl)
+		goto z_right;
+	/* Start impedance measurement for HPH_L */
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_ANA_MBHC_ZDET, 0x80, 0x80);
+	dev_dbg(rouleur->dev, "%s: ramp for HPH_L, noff = %d\n",
+		__func__, zdet_param->noff);
+	rouleur_mbhc_get_result_params(rouleur, d1_a, zdet_param->noff, &zdet);
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_ANA_MBHC_ZDET, 0x80, 0x00);
+
+	*zl = zdet;
+
+z_right:
+	if (!zr)
+		return;
+	/* Start impedance measurement for HPH_R */
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_ANA_MBHC_ZDET, 0x40, 0x40);
+	dev_dbg(rouleur->dev, "%s: ramp for HPH_R, noff = %d\n",
+		__func__, zdet_param->noff);
+	rouleur_mbhc_get_result_params(rouleur, d1_a, zdet_param->noff, &zdet);
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_ANA_MBHC_ZDET, 0x40, 0x00);
+
+	*zr = zdet;
+}
+
+static inline void rouleur_wcd_mbhc_qfuse_cal(
+					struct snd_soc_component *component,
+					int32_t *z_val, int flag_l_r)
+{
+	s16 q1;
+	int q1_cal;
+
+	if (*z_val < (ROULEUR_ZDET_VAL_400/1000))
+		q1 = snd_soc_component_read32(component,
+			ROULEUR_DIGITAL_EFUSE_REG_23 + (2 * flag_l_r));
+	else
+		q1 = snd_soc_component_read32(component,
+			ROULEUR_DIGITAL_EFUSE_REG_24 + (2 * flag_l_r));
+	if (q1 & 0x80)
+		q1_cal = (10000 - ((q1 & 0x7F) * 25));
+	else
+		q1_cal = (10000 + (q1 * 25));
+	if (q1_cal > 0)
+		*z_val = ((*z_val) * 10000) / q1_cal;
+}
+
+static void rouleur_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+					  uint32_t *zr)
+{
+	struct snd_soc_component *component = mbhc->component;
+	struct rouleur_priv *rouleur = dev_get_drvdata(component->dev);
+	s16 reg0, reg1, reg2, reg3, reg4;
+	int32_t z1L, z1R, z1Ls;
+	int zMono, z_diff1, z_diff2;
+	bool is_fsm_disable = false;
+	struct rouleur_mbhc_zdet_param zdet_param[] = {
+		{4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */
+		{2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */
+		{1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */
+		{1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */
+	};
+	struct rouleur_mbhc_zdet_param *zdet_param_ptr = NULL;
+	s16 d1_a[][4] = {
+		{0, 30, 90, 30},
+		{0, 30, 30, 5},
+		{0, 30, 30, 5},
+		{0, 30, 30, 5},
+	};
+	s16 *d1 = NULL;
+
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+	reg0 = snd_soc_component_read32(component, ROULEUR_ANA_MBHC_BTN5);
+	reg1 = snd_soc_component_read32(component, ROULEUR_ANA_MBHC_BTN6);
+	reg2 = snd_soc_component_read32(component, ROULEUR_ANA_MBHC_BTN7);
+	reg3 = snd_soc_component_read32(component, ROULEUR_MBHC_CTL_CLK);
+	reg4 = snd_soc_component_read32(component,
+			ROULEUR_ANA_MBHC_ZDET_ANA_CTL);
+
+	if (snd_soc_component_read32(component, ROULEUR_ANA_MBHC_ELECT) &
+			0x80) {
+		is_fsm_disable = true;
+		regmap_update_bits(rouleur->regmap,
+				   ROULEUR_ANA_MBHC_ELECT, 0x80, 0x00);
+	}
+
+	/* For NO-jack, disable L_DET_EN before Z-det measurements */
+	if (mbhc->hphl_swh)
+		regmap_update_bits(rouleur->regmap,
+				   ROULEUR_ANA_MBHC_MECH, 0x80, 0x00);
+
+	/* Turn off 100k pull down on HPHL */
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_ANA_MBHC_MECH, 0x01, 0x00);
+
+	/* Disable surge protection before impedance detection.
+	 * This is done to give correct value for high impedance.
+	 */
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0x00);
+	/* 1ms delay needed after disable surge protection */
+	usleep_range(1000, 1010);
+
+	/* First get impedance on Left */
+	d1 = d1_a[1];
+	zdet_param_ptr = &zdet_param[1];
+	rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+	if (!ROULEUR_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+		goto left_ch_impedance;
+
+	/* Second ramp for left ch */
+	if (z1L < ROULEUR_ZDET_VAL_32) {
+		zdet_param_ptr = &zdet_param[0];
+		d1 = d1_a[0];
+	} else if ((z1L > ROULEUR_ZDET_VAL_400) &&
+		  (z1L <= ROULEUR_ZDET_VAL_1200)) {
+		zdet_param_ptr = &zdet_param[2];
+		d1 = d1_a[2];
+	} else if (z1L > ROULEUR_ZDET_VAL_1200) {
+		zdet_param_ptr = &zdet_param[3];
+		d1 = d1_a[3];
+	}
+	rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+	if ((z1L == ROULEUR_ZDET_FLOATING_IMPEDANCE) ||
+		(z1L > ROULEUR_ZDET_VAL_100K)) {
+		*zl = ROULEUR_ZDET_FLOATING_IMPEDANCE;
+		zdet_param_ptr = &zdet_param[1];
+		d1 = d1_a[1];
+	} else {
+		*zl = z1L/1000;
+		rouleur_wcd_mbhc_qfuse_cal(component, zl, 0);
+	}
+	dev_dbg(component->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+		__func__, *zl);
+
+	/* Start of right impedance ramp and calculation */
+	rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, NULL, &z1R, d1);
+	if (ROULEUR_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+		if (((z1R > ROULEUR_ZDET_VAL_1200) &&
+			(zdet_param_ptr->noff == 0x6)) ||
+			((*zl) != ROULEUR_ZDET_FLOATING_IMPEDANCE))
+			goto right_ch_impedance;
+		/* Second ramp for right ch */
+		if (z1R < ROULEUR_ZDET_VAL_32) {
+			zdet_param_ptr = &zdet_param[0];
+			d1 = d1_a[0];
+		} else if ((z1R > ROULEUR_ZDET_VAL_400) &&
+			(z1R <= ROULEUR_ZDET_VAL_1200)) {
+			zdet_param_ptr = &zdet_param[2];
+			d1 = d1_a[2];
+		} else if (z1R > ROULEUR_ZDET_VAL_1200) {
+			zdet_param_ptr = &zdet_param[3];
+			d1 = d1_a[3];
+		}
+		rouleur_mbhc_zdet_ramp(component, zdet_param_ptr, NULL,
+				&z1R, d1);
+	}
+right_ch_impedance:
+	if ((z1R == ROULEUR_ZDET_FLOATING_IMPEDANCE) ||
+		(z1R > ROULEUR_ZDET_VAL_100K)) {
+		*zr = ROULEUR_ZDET_FLOATING_IMPEDANCE;
+	} else {
+		*zr = z1R/1000;
+		rouleur_wcd_mbhc_qfuse_cal(component, zr, 1);
+	}
+	dev_dbg(component->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+		__func__, *zr);
+
+	/* Mono/stereo detection */
+	if ((*zl == ROULEUR_ZDET_FLOATING_IMPEDANCE) &&
+		(*zr == ROULEUR_ZDET_FLOATING_IMPEDANCE)) {
+		dev_dbg(component->dev,
+			"%s: plug type is invalid or extension cable\n",
+			__func__);
+		goto zdet_complete;
+	}
+	if ((*zl == ROULEUR_ZDET_FLOATING_IMPEDANCE) ||
+	    (*zr == ROULEUR_ZDET_FLOATING_IMPEDANCE) ||
+	    ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) ||
+	    ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) {
+		dev_dbg(component->dev,
+			"%s: Mono plug type with one ch floating or shorted to GND\n",
+			__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_MONO;
+		goto zdet_complete;
+	}
+	snd_soc_component_update_bits(component, ROULEUR_HPH_R_ATEST,
+				0x02, 0x02);
+	snd_soc_component_update_bits(component, ROULEUR_HPH_PA_CTL2,
+				0x40, 0x01);
+	if (*zl < (ROULEUR_ZDET_VAL_32/1000))
+		rouleur_mbhc_zdet_ramp(component, &zdet_param[0], &z1Ls,
+				NULL, d1);
+	else
+		rouleur_mbhc_zdet_ramp(component, &zdet_param[1], &z1Ls,
+				NULL, d1);
+	snd_soc_component_update_bits(component, ROULEUR_HPH_PA_CTL2,
+				0x40, 0x00);
+	snd_soc_component_update_bits(component, ROULEUR_HPH_R_ATEST,
+				0x02, 0x00);
+	z1Ls /= 1000;
+	rouleur_wcd_mbhc_qfuse_cal(component, &z1Ls, 0);
+	/* Parallel of left Z and 9 ohm pull down resistor */
+	zMono = ((*zl) * 9) / ((*zl) + 9);
+	z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls);
+	z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl));
+	if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) {
+		dev_dbg(component->dev, "%s: stereo plug type detected\n",
+			__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+	} else {
+		dev_dbg(component->dev, "%s: MONO plug type detected\n",
+			__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_MONO;
+	}
+
+	/* Enable surge protection again after impedance detection */
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_HPH_SURGE_HPHLR_SURGE_EN, 0xC0, 0xC0);
+zdet_complete:
+	snd_soc_component_write(component, ROULEUR_ANA_MBHC_BTN5, reg0);
+	snd_soc_component_write(component, ROULEUR_ANA_MBHC_BTN6, reg1);
+	snd_soc_component_write(component, ROULEUR_ANA_MBHC_BTN7, reg2);
+	/* Turn on 100k pull down on HPHL */
+	regmap_update_bits(rouleur->regmap,
+			   ROULEUR_ANA_MBHC_MECH, 0x01, 0x01);
+
+	/* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+	if (mbhc->hphl_swh)
+		regmap_update_bits(rouleur->regmap,
+				   ROULEUR_ANA_MBHC_MECH, 0x80, 0x80);
+
+	snd_soc_component_write(component, ROULEUR_ANA_MBHC_ZDET_ANA_CTL, reg4);
+	snd_soc_component_write(component, ROULEUR_MBHC_CTL_CLK, reg3);
+	if (is_fsm_disable)
+		regmap_update_bits(rouleur->regmap,
+				   ROULEUR_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+#endif
+
+static void rouleur_mbhc_gnd_det_ctrl(struct snd_soc_component *component,
+			bool enable)
+{
+	if (enable) {
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH,
+				    0x02, 0x02);
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH,
+				    0x40, 0x40);
+	} else {
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH,
+				    0x40, 0x00);
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH,
+				    0x02, 0x00);
+	}
+}
+
+static void rouleur_mbhc_hph_pull_down_ctrl(struct snd_soc_component *component,
+					  bool enable)
+{
+	if (enable) {
+		snd_soc_component_update_bits(component,
+				    ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				    0x30, 0x10);
+		snd_soc_component_update_bits(component,
+				    ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				    0x0C, 0x04);
+	} else {
+		snd_soc_component_update_bits(component,
+				    ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				    0x30, 0x00);
+		snd_soc_component_update_bits(component,
+				    ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				    0x0C, 0x00);
+	}
+}
+
+static void rouleur_mbhc_moisture_config(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_component *component = mbhc->component;
+
+	if ((mbhc->moist_rref == R_OFF) ||
+	    (mbhc->mbhc_cfg->enable_usbc_analog)) {
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+				    0x0C, R_OFF << 2);
+		return;
+	}
+
+	/* Do not enable moisture detection if jack type is NC */
+	if (!mbhc->hphl_swh) {
+		dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+			__func__);
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+				    0x0C, R_OFF << 2);
+		return;
+	}
+
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+			    0x0C, mbhc->moist_rref << 2);
+}
+
+static void rouleur_mbhc_moisture_detect_en(struct wcd_mbhc *mbhc, bool enable)
+{
+	struct snd_soc_component *component = mbhc->component;
+
+	if (enable)
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+					0x0C, mbhc->moist_rref << 2);
+	else
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+				    0x0C, R_OFF << 2);
+}
+
+static bool rouleur_mbhc_get_moisture_status(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_component *component = mbhc->component;
+	bool ret = false;
+
+	if ((mbhc->moist_rref == R_OFF) ||
+	    (mbhc->mbhc_cfg->enable_usbc_analog)) {
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+				    0x0C, R_OFF << 2);
+		goto done;
+	}
+
+	/* Do not enable moisture detection if jack type is NC */
+	if (!mbhc->hphl_swh) {
+		dev_dbg(component->dev, "%s: disable moisture detection for NC\n",
+			__func__);
+		snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_CTL_2,
+				    0x0C, R_OFF << 2);
+		goto done;
+	}
+
+	/* If moisture_en is already enabled, then skip to plug type
+	 * detection.
+	 */
+	if ((snd_soc_component_read32(component, ROULEUR_ANA_MBHC_CTL_2) &
+			0x0C))
+		goto done;
+
+	rouleur_mbhc_moisture_detect_en(mbhc, true);
+	/* Read moisture comparator status */
+	ret = ((snd_soc_component_read32(component, ROULEUR_ANA_MBHC_FSM_STATUS)
+				& 0x20) ? 0 : 1);
+
+done:
+	return ret;
+
+}
+
+static void rouleur_mbhc_bcs_enable(struct wcd_mbhc *mbhc,
+						  bool bcs_enable)
+{
+	if (bcs_enable)
+		rouleur_disable_bcs_before_slow_insert(mbhc->component, false);
+	else
+		rouleur_disable_bcs_before_slow_insert(mbhc->component, true);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+	.request_irq = rouleur_mbhc_request_irq,
+	.irq_control = rouleur_mbhc_irq_control,
+	.free_irq = rouleur_mbhc_free_irq,
+	.clk_setup = rouleur_mbhc_clk_setup,
+	.map_btn_code_to_num = rouleur_mbhc_btn_to_num,
+	.mbhc_bias = rouleur_mbhc_mbhc_bias_control,
+	.set_btn_thr = rouleur_mbhc_program_btn_thr,
+	.lock_sleep = rouleur_mbhc_lock_sleep,
+	.register_notifier = rouleur_mbhc_register_notifier,
+	.micbias_enable_status = rouleur_mbhc_micb_en_status,
+	.hph_pa_on_status = rouleur_mbhc_hph_pa_on_status,
+	.hph_pull_up_control = rouleur_mbhc_hph_l_pull_up_control,
+	.mbhc_micbias_control = rouleur_mbhc_request_micbias,
+	.mbhc_micb_ramp_control = rouleur_mbhc_micb_ramp_control,
+	.get_hwdep_fw_cal = rouleur_get_hwdep_fw_cal,
+	.mbhc_micb_ctrl_thr_mic = rouleur_mbhc_micb_ctrl_threshold_mic,
+	//.compute_impedance = rouleur_wcd_mbhc_calc_impedance,
+	.mbhc_gnd_det_ctrl = rouleur_mbhc_gnd_det_ctrl,
+	.hph_pull_down_ctrl = rouleur_mbhc_hph_pull_down_ctrl,
+	.mbhc_moisture_config = rouleur_mbhc_moisture_config,
+	.mbhc_get_moisture_status = rouleur_mbhc_get_moisture_status,
+	.mbhc_moisture_detect_en = rouleur_mbhc_moisture_detect_en,
+	.bcs_enable = rouleur_mbhc_bcs_enable,
+};
+
+static int rouleur_get_hph_type(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct rouleur_mbhc *rouleur_mbhc = rouleur_soc_get_mbhc(component);
+	struct wcd_mbhc *mbhc;
+
+	if (!rouleur_mbhc) {
+		dev_err(component->dev, "%s: mbhc not initialized!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	mbhc = &rouleur_mbhc->wcd_mbhc;
+
+	ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+	dev_dbg(component->dev, "%s: hph_type = %u\n", __func__,
+		mbhc->hph_type);
+
+	return 0;
+}
+
+static int rouleur_hph_impedance_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	uint32_t zl = 0, zr = 0;
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct rouleur_mbhc *rouleur_mbhc = rouleur_soc_get_mbhc(component);
+
+	if (!rouleur_mbhc) {
+		dev_err(component->dev, "%s: mbhc not initialized!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+	wcd_mbhc_get_impedance(&rouleur_mbhc->wcd_mbhc, &zl, &zr);
+	dev_dbg(component->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__,
+		zl, zr);
+	ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+	SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+		       rouleur_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+		       rouleur_hph_impedance_get, NULL),
+	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+		       rouleur_hph_impedance_get, NULL),
+};
+
+/*
+ * rouleur_mbhc_get_impedance: get impedance of headphone
+ * left and right channels
+ * @rouleur_mbhc: handle to struct rouleur_mbhc *
+ * @zl: handle to left-ch impedance
+ * @zr: handle to right-ch impedance
+ * return 0 for success or error code in case of failure
+ */
+int rouleur_mbhc_get_impedance(struct rouleur_mbhc *rouleur_mbhc,
+			     uint32_t *zl, uint32_t *zr)
+{
+	if (!rouleur_mbhc) {
+		pr_err("%s: mbhc not initialized!\n", __func__);
+		return -EINVAL;
+	}
+	if (!zl || !zr) {
+		pr_err("%s: zl or zr null!\n", __func__);
+		return -EINVAL;
+	}
+
+	return wcd_mbhc_get_impedance(&rouleur_mbhc->wcd_mbhc, zl, zr);
+}
+EXPORT_SYMBOL(rouleur_mbhc_get_impedance);
+
+/*
+ * rouleur_mbhc_hs_detect: starts mbhc insertion/removal functionality
+ * @component: handle to snd_soc_component *
+ * @mbhc_cfg: handle to mbhc configuration structure
+ * return 0 if mbhc_start is success or error code in case of failure
+ */
+int rouleur_mbhc_hs_detect(struct snd_soc_component *component,
+			 struct wcd_mbhc_config *mbhc_cfg)
+{
+	struct rouleur_priv *rouleur = NULL;
+	struct rouleur_mbhc *rouleur_mbhc = NULL;
+
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rouleur = snd_soc_component_get_drvdata(component);
+	if (!rouleur) {
+		pr_err("%s: rouleur is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rouleur_mbhc = rouleur->mbhc;
+	if (!rouleur_mbhc) {
+		dev_err(component->dev, "%s: mbhc not initialized!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return wcd_mbhc_start(&rouleur_mbhc->wcd_mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(rouleur_mbhc_hs_detect);
+
+/*
+ * rouleur_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality
+ * @component: handle to snd_soc_component *
+ */
+void rouleur_mbhc_hs_detect_exit(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = NULL;
+	struct rouleur_mbhc *rouleur_mbhc = NULL;
+
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return;
+	}
+
+	rouleur = snd_soc_component_get_drvdata(component);
+	if (!rouleur) {
+		pr_err("%s: rouleur is NULL\n", __func__);
+		return;
+	}
+
+	rouleur_mbhc = rouleur->mbhc;
+	if (!rouleur_mbhc) {
+		dev_err(component->dev, "%s: mbhc not initialized!\n",
+			__func__);
+		return;
+	}
+	wcd_mbhc_stop(&rouleur_mbhc->wcd_mbhc);
+}
+EXPORT_SYMBOL(rouleur_mbhc_hs_detect_exit);
+
+/*
+ * rouleur_mbhc_ssr_down: stop mbhc during
+ * rouleur subsystem restart
+ * @mbhc: pointer to rouleur_mbhc structure
+ * @component: handle to snd_soc_component *
+ */
+void rouleur_mbhc_ssr_down(struct rouleur_mbhc *mbhc,
+			   struct snd_soc_component *component)
+{
+	struct wcd_mbhc *wcd_mbhc = NULL;
+
+	if (!mbhc || !component)
+		return;
+
+	wcd_mbhc = &mbhc->wcd_mbhc;
+	if (wcd_mbhc == NULL) {
+		dev_err(component->dev, "%s: wcd_mbhc is NULL\n", __func__);
+		return;
+	}
+
+	rouleur_mbhc_hs_detect_exit(component);
+	wcd_mbhc_deinit(wcd_mbhc);
+}
+EXPORT_SYMBOL(rouleur_mbhc_ssr_down);
+
+/*
+ * rouleur_mbhc_post_ssr_init: initialize mbhc for
+ * rouleur post subsystem restart
+ * @mbhc: poniter to rouleur_mbhc structure
+ * @component: handle to snd_soc_component *
+ *
+ * return 0 if mbhc_init is success or error code in case of failure
+ */
+int rouleur_mbhc_post_ssr_init(struct rouleur_mbhc *mbhc,
+			     struct snd_soc_component *component)
+{
+	int ret = 0;
+	struct wcd_mbhc *wcd_mbhc = NULL;
+
+	if (!mbhc || !component)
+		return -EINVAL;
+
+	wcd_mbhc = &mbhc->wcd_mbhc;
+	if (wcd_mbhc == NULL) {
+		pr_err("%s: wcd_mbhc is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	snd_soc_component_update_bits(component, ROULEUR_ANA_MBHC_MECH,
+				0x20, 0x20);
+	ret = wcd_mbhc_init(wcd_mbhc, component, &mbhc_cb, &intr_ids,
+			    wcd_mbhc_registers, ROULEUR_ZDET_SUPPORTED);
+	if (ret)
+		dev_err(component->dev, "%s: mbhc initialization failed\n",
+			__func__);
+
+	return ret;
+}
+EXPORT_SYMBOL(rouleur_mbhc_post_ssr_init);
+
+/*
+ * rouleur_mbhc_init: initialize mbhc for rouleur
+ * @mbhc: poniter to rouleur_mbhc struct pointer to store the configs
+ * @component: handle to snd_soc_component *
+ * @fw_data: handle to firmware data
+ *
+ * return 0 if mbhc_init is success or error code in case of failure
+ */
+int rouleur_mbhc_init(struct rouleur_mbhc **mbhc,
+		      struct snd_soc_component *component,
+		      struct fw_info *fw_data)
+{
+	struct rouleur_mbhc *rouleur_mbhc = NULL;
+	struct wcd_mbhc *wcd_mbhc = NULL;
+	struct rouleur_pdata *pdata;
+	int ret = 0;
+
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rouleur_mbhc = devm_kzalloc(component->dev, sizeof(struct rouleur_mbhc),
+				    GFP_KERNEL);
+	if (!rouleur_mbhc)
+		return -ENOMEM;
+
+	rouleur_mbhc->fw_data = fw_data;
+	BLOCKING_INIT_NOTIFIER_HEAD(&rouleur_mbhc->notifier);
+	wcd_mbhc = &rouleur_mbhc->wcd_mbhc;
+	if (wcd_mbhc == NULL) {
+		pr_err("%s: wcd_mbhc is NULL\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+
+	/* Setting default mbhc detection logic to ADC */
+	wcd_mbhc->mbhc_detection_logic = WCD_DETECTION_ADC;
+
+	pdata = dev_get_platdata(component->dev);
+	if (!pdata) {
+		dev_err(component->dev, "%s: pdata pointer is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+	wcd_mbhc->micb_mv = pdata->micbias.micb2_mv;
+
+	ret = wcd_mbhc_init(wcd_mbhc, component, &mbhc_cb,
+				&intr_ids, wcd_mbhc_registers,
+				ROULEUR_ZDET_SUPPORTED);
+	if (ret) {
+		dev_err(component->dev, "%s: mbhc initialization failed\n",
+			__func__);
+		goto err;
+	}
+
+	(*mbhc) = rouleur_mbhc;
+	snd_soc_add_component_controls(component, impedance_detect_controls,
+				   ARRAY_SIZE(impedance_detect_controls));
+	snd_soc_add_component_controls(component, hph_type_detect_controls,
+				   ARRAY_SIZE(hph_type_detect_controls));
+
+	return 0;
+err:
+	devm_kfree(component->dev, rouleur_mbhc);
+	return ret;
+}
+EXPORT_SYMBOL(rouleur_mbhc_init);
+
+/*
+ * rouleur_mbhc_deinit: deinitialize mbhc for rouleur
+ * @component: handle to snd_soc_component *
+ */
+void rouleur_mbhc_deinit(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur;
+	struct rouleur_mbhc *rouleur_mbhc;
+
+	if (!component) {
+		pr_err("%s: component is NULL\n", __func__);
+		return;
+	}
+
+	rouleur = snd_soc_component_get_drvdata(component);
+	if (!rouleur) {
+		pr_err("%s: rouleur is NULL\n", __func__);
+		return;
+	}
+
+	rouleur_mbhc = rouleur->mbhc;
+	if (rouleur_mbhc) {
+		wcd_mbhc_deinit(&rouleur_mbhc->wcd_mbhc);
+		devm_kfree(component->dev, rouleur_mbhc);
+	}
+}
+EXPORT_SYMBOL(rouleur_mbhc_deinit);
diff --git a/asoc/codecs/rouleur/rouleur-mbhc.h b/asoc/codecs/rouleur/rouleur-mbhc.h
new file mode 100644
index 0000000..40bef37
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur-mbhc.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+#ifndef __ROULEUR_MBHC_H__
+#define __ROULEUR_MBHC_H__
+#include <asoc/wcd-mbhc-v2.h>
+
+struct rouleur_mbhc {
+	struct wcd_mbhc wcd_mbhc;
+	struct blocking_notifier_head notifier;
+	struct fw_info *fw_data;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_ROULEUR)
+extern int rouleur_mbhc_init(struct rouleur_mbhc **mbhc,
+			   struct snd_soc_component *component,
+			   struct fw_info *fw_data);
+extern void rouleur_mbhc_hs_detect_exit(struct snd_soc_component *component);
+extern int rouleur_mbhc_hs_detect(struct snd_soc_component *component,
+				struct wcd_mbhc_config *mbhc_cfg);
+extern void rouleur_mbhc_deinit(struct snd_soc_component *component);
+extern int rouleur_mbhc_post_ssr_init(struct rouleur_mbhc *mbhc,
+				    struct snd_soc_component *component);
+extern void rouleur_mbhc_ssr_down(struct rouleur_mbhc *mbhc,
+				    struct snd_soc_component *component);
+extern int rouleur_mbhc_get_impedance(struct rouleur_mbhc *rouleur_mbhc,
+				    uint32_t *zl, uint32_t *zr);
+#else
+static inline int rouleur_mbhc_init(struct rouleur_mbhc **mbhc,
+				  struct snd_soc_component *component,
+				  struct fw_info *fw_data)
+{
+	return 0;
+}
+static inline void rouleur_mbhc_hs_detect_exit(
+			struct snd_soc_component *component)
+{
+}
+static inline int rouleur_mbhc_hs_detect(struct snd_soc_component *component,
+				       struct wcd_mbhc_config *mbhc_cfg)
+{
+		return 0;
+}
+static inline void rouleur_mbhc_deinit(struct snd_soc_component *component)
+{
+}
+static inline int rouleur_mbhc_post_ssr_init(struct rouleur_mbhc *mbhc,
+					   struct snd_soc_component *component)
+{
+	return 0;
+}
+static inline void rouleur_mbhc_ssr_down(struct rouleur_mbhc *mbhc,
+					   struct snd_soc_component *component)
+{
+}
+static inline int rouleur_mbhc_get_impedance(struct rouleur_mbhc *rouleur_mbhc,
+					   uint32_t *zl, uint32_t *zr)
+{
+	if (zl)
+		*zl = 0;
+	if (zr)
+		*zr = 0;
+	return -EINVAL;
+}
+#endif
+
+#endif /* __ROULEUR_MBHC_H__ */
diff --git a/asoc/codecs/rouleur/rouleur-registers.h b/asoc/codecs/rouleur/rouleur-registers.h
new file mode 100644
index 0000000..ff3934b
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur-registers.h
@@ -0,0 +1,121 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ROULEUR_REGISTERS_H
+#define _ROULEUR_REGISTERS_H
+
+#define ROULEUR_ANA_BASE_ADDR 0x3000
+#define ROULEUR_DIG_BASE_ADDR 0x3400
+
+#define ROULEUR_REG(reg)  ((reg > ROULEUR_DIG_BASE_ADDR) ? \
+			   (reg - ROULEUR_DIG_BASE_ADDR) : \
+			   (reg - ROULEUR_ANA_BASE_ADDR))
+
+enum {
+	REG_NO_ACCESS,
+	RD_REG,
+	WR_REG,
+	RD_WR_REG
+};
+
+#define ROULEUR_ANA_MICBIAS_MICB_1_2_EN       (ROULEUR_ANA_BASE_ADDR+0x040)
+#define ROULEUR_ANA_MICBIAS_MICB_3_EN         (ROULEUR_ANA_BASE_ADDR+0x041)
+#define ROULEUR_ANA_MICBIAS_LDO_1_SETTING     (ROULEUR_ANA_BASE_ADDR+0x042)
+#define ROULEUR_ANA_MICBIAS_LDO_1_CTRL        (ROULEUR_ANA_BASE_ADDR+0x043)
+#define ROULEUR_ANA_TX_AMIC1                  (ROULEUR_ANA_BASE_ADDR+0x047)
+#define ROULEUR_ANA_TX_AMIC2                  (ROULEUR_ANA_BASE_ADDR+0x048)
+#define ROULEUR_ANA_MBHC_MECH                 (ROULEUR_ANA_BASE_ADDR+0x05A)
+#define ROULEUR_ANA_MBHC_ELECT                (ROULEUR_ANA_BASE_ADDR+0x05B)
+#define ROULEUR_ANA_MBHC_ZDET                 (ROULEUR_ANA_BASE_ADDR+0x05C)
+#define ROULEUR_ANA_MBHC_RESULT_1             (ROULEUR_ANA_BASE_ADDR+0x05D)
+#define ROULEUR_ANA_MBHC_RESULT_2             (ROULEUR_ANA_BASE_ADDR+0x05E)
+#define ROULEUR_ANA_MBHC_RESULT_3             (ROULEUR_ANA_BASE_ADDR+0x05F)
+#define ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1      (ROULEUR_ANA_BASE_ADDR+0x060)
+#define ROULEUR_ANA_MBHC_BTN1_ZDET_VREF2      (ROULEUR_ANA_BASE_ADDR+0x061)
+#define ROULEUR_ANA_MBHC_BTN2_ZDET_VREF3      (ROULEUR_ANA_BASE_ADDR+0x062)
+#define ROULEUR_ANA_MBHC_BTN3_ZDET_DBG_400    (ROULEUR_ANA_BASE_ADDR+0x063)
+#define ROULEUR_ANA_MBHC_BTN4_ZDET_DBG_1400   (ROULEUR_ANA_BASE_ADDR+0x064)
+#define ROULEUR_ANA_MBHC_MICB2_RAMP           (ROULEUR_ANA_BASE_ADDR+0x065)
+#define ROULEUR_ANA_MBHC_CTL_1                (ROULEUR_ANA_BASE_ADDR+0x066)
+#define ROULEUR_ANA_MBHC_CTL_2                (ROULEUR_ANA_BASE_ADDR+0x067)
+#define ROULEUR_ANA_MBHC_PLUG_DETECT_CTL      (ROULEUR_ANA_BASE_ADDR+0x068)
+#define ROULEUR_ANA_MBHC_ZDET_ANA_CTL         (ROULEUR_ANA_BASE_ADDR+0x069)
+#define ROULEUR_ANA_MBHC_ZDET_RAMP_CTL        (ROULEUR_ANA_BASE_ADDR+0x06A)
+#define ROULEUR_ANA_MBHC_FSM_STATUS           (ROULEUR_ANA_BASE_ADDR+0x06B)
+#define ROULEUR_ANA_MBHC_ADC_RESULT           (ROULEUR_ANA_BASE_ADDR+0x06C)
+#define ROULEUR_ANA_MBHC_MCLK                 (ROULEUR_ANA_BASE_ADDR+0x06D)
+#define ROULEUR_ANA_MBHC_ZDET_CALIB_RESULT    (ROULEUR_ANA_BASE_ADDR+0x072)
+#define ROULEUR_ANA_NCP_EN                    (ROULEUR_ANA_BASE_ADDR+0x077)
+#define ROULEUR_ANA_HPHPA_CNP_CTL_2           (ROULEUR_ANA_BASE_ADDR+0x084)
+#define ROULEUR_ANA_HPHPA_PA_STATUS           (ROULEUR_ANA_BASE_ADDR+0x087)
+#define ROULEUR_ANA_HPHPA_FSM_CLK             (ROULEUR_ANA_BASE_ADDR+0x088)
+#define ROULEUR_ANA_HPHPA_L_GAIN              (ROULEUR_ANA_BASE_ADDR+0x08B)
+#define ROULEUR_ANA_HPHPA_R_GAIN              (ROULEUR_ANA_BASE_ADDR+0x08C)
+#define ROULEUR_ANA_HPHPA_SPARE_CTL           (ROULEUR_ANA_BASE_ADDR+0x08E)
+#define ROULEUR_ANA_SURGE_EN                  (ROULEUR_ANA_BASE_ADDR+0x097)
+#define ROULEUR_ANA_COMBOPA_CTL               (ROULEUR_ANA_BASE_ADDR+0x09B)
+#define ROULEUR_ANA_RXLDO_CTL                 (ROULEUR_ANA_BASE_ADDR+0x0B2)
+#define ROULEUR_ANA_MBIAS_EN                  (ROULEUR_ANA_BASE_ADDR+0x0B4)
+
+#define ROULEUR_DIG_SWR_CHIP_ID0              (ROULEUR_DIG_BASE_ADDR+0x001)
+#define ROULEUR_DIG_SWR_CHIP_ID1              (ROULEUR_DIG_BASE_ADDR+0x002)
+#define ROULEUR_DIG_SWR_CHIP_ID2              (ROULEUR_DIG_BASE_ADDR+0x003)
+#define ROULEUR_DIG_SWR_CHIP_ID3              (ROULEUR_DIG_BASE_ADDR+0x004)
+#define ROULEUR_DIG_SWR_SWR_TX_CLK_RATE       (ROULEUR_DIG_BASE_ADDR+0x040)
+#define ROULEUR_DIG_SWR_CDC_RST_CTL           (ROULEUR_DIG_BASE_ADDR+0x041)
+#define ROULEUR_DIG_SWR_TOP_CLK_CFG           (ROULEUR_DIG_BASE_ADDR+0x042)
+#define ROULEUR_DIG_SWR_CDC_RX_CLK_CTL        (ROULEUR_DIG_BASE_ADDR+0x043)
+#define ROULEUR_DIG_SWR_CDC_TX_CLK_CTL        (ROULEUR_DIG_BASE_ADDR+0x044)
+#define ROULEUR_DIG_SWR_SWR_RST_EN            (ROULEUR_DIG_BASE_ADDR+0x045)
+#define ROULEUR_DIG_SWR_CDC_RX_RST            (ROULEUR_DIG_BASE_ADDR+0x047)
+#define ROULEUR_DIG_SWR_CDC_RX0_CTL           (ROULEUR_DIG_BASE_ADDR+0x048)
+#define ROULEUR_DIG_SWR_CDC_RX1_CTL           (ROULEUR_DIG_BASE_ADDR+0x049)
+#define ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1   (ROULEUR_DIG_BASE_ADDR+0x04B)
+#define ROULEUR_DIG_SWR_CDC_COMP_CTL_0        (ROULEUR_DIG_BASE_ADDR+0x04F)
+#define ROULEUR_DIG_SWR_CDC_RX_DELAY_CTL      (ROULEUR_DIG_BASE_ADDR+0x052)
+#define ROULEUR_DIG_SWR_CDC_RX_GAIN_0         (ROULEUR_DIG_BASE_ADDR+0x053)
+#define ROULEUR_DIG_SWR_CDC_RX_GAIN_1         (ROULEUR_DIG_BASE_ADDR+0x054)
+#define ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL       (ROULEUR_DIG_BASE_ADDR+0x057)
+#define ROULEUR_DIG_SWR_CDC_TX0_CTL           (ROULEUR_DIG_BASE_ADDR+0x060)
+#define ROULEUR_DIG_SWR_CDC_TX1_CTL           (ROULEUR_DIG_BASE_ADDR+0x061)
+#define ROULEUR_DIG_SWR_CDC_TX_RST            (ROULEUR_DIG_BASE_ADDR+0x063)
+#define ROULEUR_DIG_SWR_CDC_REQ0_CTL          (ROULEUR_DIG_BASE_ADDR+0x064)
+#define ROULEUR_DIG_SWR_CDC_REQ1_CTL          (ROULEUR_DIG_BASE_ADDR+0x065)
+#define ROULEUR_DIG_SWR_CDC_RST               (ROULEUR_DIG_BASE_ADDR+0x067)
+#define ROULEUR_DIG_SWR_CDC_AMIC_CTL          (ROULEUR_DIG_BASE_ADDR+0x06A)
+#define ROULEUR_DIG_SWR_CDC_DMIC_CTL          (ROULEUR_DIG_BASE_ADDR+0x06B)
+#define ROULEUR_DIG_SWR_CDC_DMIC1_CTL         (ROULEUR_DIG_BASE_ADDR+0x06C)
+#define ROULEUR_DIG_SWR_CDC_DMIC1_RATE        (ROULEUR_DIG_BASE_ADDR+0x06D)
+#define ROULEUR_DIG_SWR_PDM_WD_CTL0           (ROULEUR_DIG_BASE_ADDR+0x070)
+#define ROULEUR_DIG_SWR_PDM_WD_CTL1           (ROULEUR_DIG_BASE_ADDR+0x071)
+#define ROULEUR_DIG_SWR_INTR_MODE             (ROULEUR_DIG_BASE_ADDR+0x080)
+#define ROULEUR_DIG_SWR_INTR_MASK_0           (ROULEUR_DIG_BASE_ADDR+0x081)
+#define ROULEUR_DIG_SWR_INTR_MASK_1           (ROULEUR_DIG_BASE_ADDR+0x082)
+#define ROULEUR_DIG_SWR_INTR_MASK_2           (ROULEUR_DIG_BASE_ADDR+0x083)
+#define ROULEUR_DIG_SWR_INTR_STATUS_0         (ROULEUR_DIG_BASE_ADDR+0x084)
+#define ROULEUR_DIG_SWR_INTR_STATUS_1         (ROULEUR_DIG_BASE_ADDR+0x085)
+#define ROULEUR_DIG_SWR_INTR_STATUS_2         (ROULEUR_DIG_BASE_ADDR+0x086)
+#define ROULEUR_DIG_SWR_INTR_CLEAR_0          (ROULEUR_DIG_BASE_ADDR+0x087)
+#define ROULEUR_DIG_SWR_INTR_CLEAR_1          (ROULEUR_DIG_BASE_ADDR+0x088)
+#define ROULEUR_DIG_SWR_INTR_CLEAR_2          (ROULEUR_DIG_BASE_ADDR+0x089)
+#define ROULEUR_DIG_SWR_INTR_LEVEL_0          (ROULEUR_DIG_BASE_ADDR+0x08A)
+#define ROULEUR_DIG_SWR_INTR_LEVEL_1          (ROULEUR_DIG_BASE_ADDR+0x08B)
+#define ROULEUR_DIG_SWR_INTR_LEVEL_2          (ROULEUR_DIG_BASE_ADDR+0x08C)
+#define ROULEUR_DIG_SWR_CDC_CONN_RX0_CTL      (ROULEUR_DIG_BASE_ADDR+0x093)
+#define ROULEUR_DIG_SWR_CDC_CONN_RX1_CTL      (ROULEUR_DIG_BASE_ADDR+0x094)
+#define ROULEUR_DIG_SWR_LOOP_BACK_MODE        (ROULEUR_DIG_BASE_ADDR+0x097)
+#define ROULEUR_DIG_SWR_DRIVE_STRENGTH_0      (ROULEUR_DIG_BASE_ADDR+0x0A0)
+#define ROULEUR_DIG_SWR_DIG_DEBUG_CTL         (ROULEUR_DIG_BASE_ADDR+0x0AB)
+#define ROULEUR_DIG_SWR_DIG_DEBUG_EN          (ROULEUR_DIG_BASE_ADDR+0x0AC)
+#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA0      (ROULEUR_DIG_BASE_ADDR+0x0B0)
+#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA1      (ROULEUR_DIG_BASE_ADDR+0x0B1)
+#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA2      (ROULEUR_DIG_BASE_ADDR+0x0B2)
+#define ROULEUR_DIG_SWR_DEM_BYPASS_DATA3      (ROULEUR_DIG_BASE_ADDR+0x0B3)
+
+#define ROULEUR_ANALOG_REGISTERS_MAX_SIZE (ROULEUR_ANA_BASE_ADDR+0x0B5)
+#define ROULEUR_DIGITAL_REGISTERS_MAX_SIZE (ROULEUR_DIG_BASE_ADDR+0x0B4)
+#define ROULEUR_ANALOG_MAX_REGISTER (ROULEUR_ANALOG_REGISTERS_MAX_SIZE - 1)
+#define ROULEUR_DIGITAL_MAX_REGISTER (ROULEUR_DIGITAL_REGISTERS_MAX_SIZE - 1)
+#endif
diff --git a/asoc/codecs/rouleur/rouleur-regmap.c b/asoc/codecs/rouleur/rouleur-regmap.c
new file mode 100644
index 0000000..6250214
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur-regmap.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "rouleur-registers.h"
+
+extern const u8 rouleur_reg_access_analog[
+			ROULEUR_REG(ROULEUR_ANALOG_REGISTERS_MAX_SIZE)];
+extern const u8 rouleur_reg_access_digital[
+			ROULEUR_REG(ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)];
+
+static const struct reg_default rouleur_defaults[] = {
+	{ ROULEUR_ANA_MICBIAS_MICB_1_2_EN,        0x01 },
+	{ ROULEUR_ANA_MICBIAS_MICB_3_EN,          0x00 },
+	{ ROULEUR_ANA_MICBIAS_LDO_1_SETTING,      0x21 },
+	{ ROULEUR_ANA_MICBIAS_LDO_1_CTRL,         0x01 },
+	{ ROULEUR_ANA_TX_AMIC1,                   0x00 },
+	{ ROULEUR_ANA_TX_AMIC2,                   0x00 },
+	{ ROULEUR_ANA_MBHC_MECH,                  0x39 },
+	{ ROULEUR_ANA_MBHC_ELECT,                 0x08 },
+	{ ROULEUR_ANA_MBHC_ZDET,                  0x10 },
+	{ ROULEUR_ANA_MBHC_RESULT_1,              0x00 },
+	{ ROULEUR_ANA_MBHC_RESULT_2,              0x00 },
+	{ ROULEUR_ANA_MBHC_RESULT_3,              0x00 },
+	{ ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1,       0x00 },
+	{ ROULEUR_ANA_MBHC_BTN1_ZDET_VREF2,       0x10 },
+	{ ROULEUR_ANA_MBHC_BTN2_ZDET_VREF3,       0x20 },
+	{ ROULEUR_ANA_MBHC_BTN3_ZDET_DBG_400,     0x30 },
+	{ ROULEUR_ANA_MBHC_BTN4_ZDET_DBG_1400,    0x40 },
+	{ ROULEUR_ANA_MBHC_MICB2_RAMP,            0x00 },
+	{ ROULEUR_ANA_MBHC_CTL_1,                 0x02 },
+	{ ROULEUR_ANA_MBHC_CTL_2,                 0x05 },
+	{ ROULEUR_ANA_MBHC_PLUG_DETECT_CTL,       0xE9 },
+	{ ROULEUR_ANA_MBHC_ZDET_ANA_CTL,          0x0F },
+	{ ROULEUR_ANA_MBHC_ZDET_RAMP_CTL,         0x00 },
+	{ ROULEUR_ANA_MBHC_FSM_STATUS,            0x00 },
+	{ ROULEUR_ANA_MBHC_ADC_RESULT,            0x00 },
+	{ ROULEUR_ANA_MBHC_MCLK,				  0x30 },
+	{ ROULEUR_ANA_MBHC_ZDET_CALIB_RESULT,     0x00 },
+	{ ROULEUR_ANA_NCP_EN,                     0x00 },
+	{ ROULEUR_ANA_HPHPA_CNP_CTL_2,            0x2B },
+	{ ROULEUR_ANA_HPHPA_PA_STATUS,            0x00 },
+	{ ROULEUR_ANA_HPHPA_FSM_CLK,              0x12 },
+	{ ROULEUR_ANA_HPHPA_SPARE_CTL,            0x02 },
+	{ ROULEUR_ANA_SURGE_EN,                   0x38 },
+	{ ROULEUR_ANA_COMBOPA_CTL,                0x35 },
+	{ ROULEUR_ANA_RXLDO_CTL,                  0x86 },
+	{ ROULEUR_ANA_MBIAS_EN,                   0x00 },
+	{ ROULEUR_DIG_SWR_CHIP_ID0,               0x00 },
+	{ ROULEUR_DIG_SWR_CHIP_ID1,               0x00 },
+	{ ROULEUR_DIG_SWR_CHIP_ID2,               0x0C },
+	{ ROULEUR_DIG_SWR_CHIP_ID3,               0x01 },
+	{ ROULEUR_DIG_SWR_SWR_TX_CLK_RATE,        0x00 },
+	{ ROULEUR_DIG_SWR_CDC_RST_CTL,            0x03 },
+	{ ROULEUR_DIG_SWR_TOP_CLK_CFG,            0x00 },
+	{ ROULEUR_DIG_SWR_CDC_RX_CLK_CTL,         0x00 },
+	{ ROULEUR_DIG_SWR_CDC_TX_CLK_CTL,         0x33 },
+	{ ROULEUR_DIG_SWR_SWR_RST_EN,             0x00 },
+	{ ROULEUR_DIG_SWR_CDC_RX_RST,             0x00 },
+	{ ROULEUR_DIG_SWR_CDC_RX0_CTL,            0xFC },
+	{ ROULEUR_DIG_SWR_CDC_RX1_CTL,            0xFC },
+	{ ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1,    0x00 },
+	{ ROULEUR_DIG_SWR_CDC_COMP_CTL_0,         0x00 },
+	{ ROULEUR_DIG_SWR_CDC_RX_DELAY_CTL,       0x66 },
+	{ ROULEUR_DIG_SWR_CDC_RX_GAIN_0,          0x55 },
+	{ ROULEUR_DIG_SWR_CDC_RX_GAIN_1,          0xA9 },
+	{ ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL,        0x00 },
+	{ ROULEUR_DIG_SWR_CDC_TX0_CTL,            0x68 },
+	{ ROULEUR_DIG_SWR_CDC_TX1_CTL,            0x68 },
+	{ ROULEUR_DIG_SWR_CDC_TX_RST,             0x00 },
+	{ ROULEUR_DIG_SWR_CDC_REQ0_CTL,           0x01 },
+	{ ROULEUR_DIG_SWR_CDC_REQ1_CTL,           0x01 },
+	{ ROULEUR_DIG_SWR_CDC_RST,                0x00 },
+	{ ROULEUR_DIG_SWR_CDC_AMIC_CTL,           0x02 },
+	{ ROULEUR_DIG_SWR_CDC_DMIC_CTL,           0x00 },
+	{ ROULEUR_DIG_SWR_CDC_DMIC1_CTL,          0x00 },
+	{ ROULEUR_DIG_SWR_CDC_DMIC1_RATE,         0x01 },
+	{ ROULEUR_DIG_SWR_PDM_WD_CTL0,            0x00 },
+	{ ROULEUR_DIG_SWR_PDM_WD_CTL1,            0x00 },
+	{ ROULEUR_DIG_SWR_INTR_MODE,              0x00 },
+	{ ROULEUR_DIG_SWR_INTR_MASK_0,            0xFF },
+	{ ROULEUR_DIG_SWR_INTR_MASK_1,            0x7F },
+	{ ROULEUR_DIG_SWR_INTR_MASK_2,            0x0C },
+	{ ROULEUR_DIG_SWR_INTR_STATUS_0,          0x00 },
+	{ ROULEUR_DIG_SWR_INTR_STATUS_1,          0x00 },
+	{ ROULEUR_DIG_SWR_INTR_STATUS_2,          0x00 },
+	{ ROULEUR_DIG_SWR_INTR_CLEAR_0,           0x00 },
+	{ ROULEUR_DIG_SWR_INTR_CLEAR_1,           0x00 },
+	{ ROULEUR_DIG_SWR_INTR_CLEAR_2,           0x00 },
+	{ ROULEUR_DIG_SWR_INTR_LEVEL_0,           0x00 },
+	{ ROULEUR_DIG_SWR_INTR_LEVEL_1,           0x2A },
+	{ ROULEUR_DIG_SWR_INTR_LEVEL_2,           0x00 },
+	{ ROULEUR_DIG_SWR_CDC_CONN_RX0_CTL,       0x00 },
+	{ ROULEUR_DIG_SWR_CDC_CONN_RX1_CTL,       0x00 },
+	{ ROULEUR_DIG_SWR_LOOP_BACK_MODE,         0x00 },
+	{ ROULEUR_DIG_SWR_DRIVE_STRENGTH_0,       0x00 },
+	{ ROULEUR_DIG_SWR_DIG_DEBUG_CTL,          0x00 },
+	{ ROULEUR_DIG_SWR_DIG_DEBUG_EN,           0x00 },
+	{ ROULEUR_DIG_SWR_DEM_BYPASS_DATA0,       0x55 },
+	{ ROULEUR_DIG_SWR_DEM_BYPASS_DATA1,       0x55 },
+	{ ROULEUR_DIG_SWR_DEM_BYPASS_DATA2,       0x55 },
+	{ ROULEUR_DIG_SWR_DEM_BYPASS_DATA3,       0x01 },
+};
+
+static bool rouleur_readable_register(struct device *dev, unsigned int reg)
+{
+	if (reg > ROULEUR_ANA_BASE_ADDR && reg <
+				ROULEUR_ANALOG_REGISTERS_MAX_SIZE)
+		return rouleur_reg_access_analog[ROULEUR_REG(reg)] & RD_REG;
+	if (reg > ROULEUR_DIG_BASE_ADDR && reg <
+				ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)
+		return rouleur_reg_access_digital[ROULEUR_REG(reg)] & RD_REG;
+	return 0;
+}
+
+static bool rouleur_writeable_register(struct device *dev, unsigned int reg)
+{
+	if (reg > ROULEUR_ANA_BASE_ADDR && reg <
+					ROULEUR_ANALOG_REGISTERS_MAX_SIZE)
+		return rouleur_reg_access_analog[ROULEUR_REG(reg)] & WR_REG;
+	if (reg > ROULEUR_DIG_BASE_ADDR && reg <
+					ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)
+		return rouleur_reg_access_digital[ROULEUR_REG(reg)] & WR_REG;
+	return 0;
+}
+
+static bool rouleur_volatile_register(struct device *dev, unsigned int reg)
+{
+	if (reg > ROULEUR_ANA_BASE_ADDR && reg <
+					ROULEUR_ANALOG_REGISTERS_MAX_SIZE)
+		if ((rouleur_reg_access_analog[ROULEUR_REG(reg)] & RD_REG)
+		    && !(rouleur_reg_access_analog[ROULEUR_REG(reg)] & WR_REG))
+			return true;
+		return false;
+	if (reg > ROULEUR_DIG_BASE_ADDR && reg <
+					ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)
+		if ((rouleur_reg_access_digital[ROULEUR_REG(reg)] & RD_REG)
+		    && !(rouleur_reg_access_digital[ROULEUR_REG(reg)] & WR_REG))
+			return true;
+		return false;
+	return 0;
+}
+
+struct regmap_config rouleur_regmap_config = {
+	.name = "rouleur_csr",
+	.reg_bits = 16,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = rouleur_defaults,
+	.num_reg_defaults = ARRAY_SIZE(rouleur_defaults),
+	.max_register = ROULEUR_ANALOG_MAX_REGISTER +
+				ROULEUR_DIGITAL_MAX_REGISTER,
+	.readable_reg = rouleur_readable_register,
+	.writeable_reg = rouleur_writeable_register,
+	.volatile_reg = rouleur_volatile_register,
+	.can_multi_write = true,
+};
diff --git a/asoc/codecs/rouleur/rouleur-tables.c b/asoc/codecs/rouleur/rouleur-tables.c
new file mode 100644
index 0000000..ea710eb
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur-tables.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include "rouleur-registers.h"
+
+const u8 rouleur_reg_access_analog[ROULEUR_REG(
+			ROULEUR_ANALOG_REGISTERS_MAX_SIZE)] = {
+	[ROULEUR_REG(ROULEUR_ANA_MICBIAS_MICB_1_2_EN)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MICBIAS_MICB_3_EN)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MICBIAS_LDO_1_SETTING)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MICBIAS_LDO_1_CTRL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_TX_AMIC1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_TX_AMIC2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_MECH)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_ELECT)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_RESULT_1)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_RESULT_2)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_RESULT_3)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_BTN0_ZDET_VREF1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_BTN1_ZDET_VREF2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_BTN2_ZDET_VREF3)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_BTN3_ZDET_DBG_400)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_BTN4_ZDET_DBG_1400)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_MICB2_RAMP)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_CTL_1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_CTL_2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_PLUG_DETECT_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET_ANA_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET_RAMP_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_FSM_STATUS)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_ADC_RESULT)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_MCLK)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBHC_ZDET_CALIB_RESULT)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_NCP_EN)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_HPHPA_CNP_CTL_2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_HPHPA_PA_STATUS)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_ANA_HPHPA_FSM_CLK)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_HPHPA_SPARE_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_SURGE_EN)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_COMBOPA_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_RXLDO_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_ANA_MBIAS_EN)] = RD_WR_REG,
+};
+
+const u8 rouleur_reg_access_digital[ROULEUR_REG(
+			ROULEUR_DIGITAL_REGISTERS_MAX_SIZE)] = {
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID0)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID1)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID2)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CHIP_ID3)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_SWR_TX_CLK_RATE)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RST_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_TOP_CLK_CFG)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_CLK_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX_CLK_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_SWR_RST_EN)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_RST)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX0_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX1_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_COMP_CTL_0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_DELAY_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_GAIN_0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_GAIN_1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX0_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX1_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_TX_RST)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_REQ0_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_REQ1_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_RST)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_AMIC_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_DMIC_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_DMIC1_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_DMIC1_RATE)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_PDM_WD_CTL0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_PDM_WD_CTL1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MODE)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MASK_0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MASK_1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_MASK_2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_STATUS_0)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_STATUS_1)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_STATUS_2)] = RD_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_CLEAR_0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_CLEAR_1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_CLEAR_2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_LEVEL_0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_LEVEL_1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_INTR_LEVEL_2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_CONN_RX0_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_CDC_CONN_RX1_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_LOOP_BACK_MODE)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DRIVE_STRENGTH_0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DIG_DEBUG_CTL)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DIG_DEBUG_EN)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA0)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA1)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA2)] = RD_WR_REG,
+	[ROULEUR_REG(ROULEUR_DIG_SWR_DEM_BYPASS_DATA3)] = RD_WR_REG,
+};
diff --git a/asoc/codecs/rouleur/rouleur.c b/asoc/codecs/rouleur/rouleur.c
new file mode 100644
index 0000000..14dfd52
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur.c
@@ -0,0 +1,2492 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <soc/soundwire.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "internal.h"
+#include "rouleur.h"
+#include <asoc/wcdcal-hwdep.h>
+#include "rouleur-registers.h"
+#include "pm2250-spmi.h"
+#include <asoc/msm-cdc-pinctrl.h>
+#include <dt-bindings/sound/audio-codec-port-types.h>
+#include <asoc/msm-cdc-supply.h>
+
+#define DRV_NAME "rouleur_codec"
+
+#define NUM_SWRS_DT_PARAMS 5
+
+#define ROULEUR_VERSION_1_0 1
+#define ROULEUR_VERSION_ENTRY_SIZE 32
+
+#define NUM_ATTEMPTS 5
+
+enum {
+	CODEC_TX = 0,
+	CODEC_RX,
+};
+
+enum {
+	ALLOW_VPOS_DISABLE,
+	HPH_COMP_DELAY,
+	HPH_PA_DELAY,
+	AMIC2_BCS_ENABLE,
+};
+
+/* TODO: Check on the step values */
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static int rouleur_handle_post_irq(void *data);
+static int rouleur_reset(struct device *dev, int val);
+
+static const struct regmap_irq ROULEUR_IRQs[ROULEUR_NUM_IRQS] = {
+	REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_BUTTON_PRESS_DET, 0, 0x01),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_BUTTON_RELEASE_DET, 0, 0x02),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_ELECT_INS_REM_DET, 0, 0x04),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_ELECT_INS_REM_LEG_DET, 0, 0x08),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_MBHC_SW_DET, 0, 0x10),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_OCP_INT, 0, 0x20),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_CNP_INT, 0, 0x40),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_OCP_INT, 0, 0x80),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_CNP_INT, 1, 0x01),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_EAR_CNP_INT, 1, 0x02),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_EAR_OCP_INT, 1, 0x04),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_LO_CNP_INT, 1, 0x08),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_LO_OCP_INT, 1, 0x10),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_PDM_WD_INT, 1, 0x20),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_PDM_WD_INT, 1, 0x40),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHL_SURGE_DET_INT, 2, 0x04),
+	REGMAP_IRQ_REG(ROULEUR_IRQ_HPHR_SURGE_DET_INT, 2, 0x08),
+};
+
+static struct regmap_irq_chip rouleur_regmap_irq_chip = {
+	.name = "rouleur",
+	.irqs = ROULEUR_IRQs,
+	.num_irqs = ARRAY_SIZE(ROULEUR_IRQs),
+	.num_regs = 3,
+	.status_base = ROULEUR_DIG_SWR_INTR_STATUS_0,
+	.mask_base = ROULEUR_DIG_SWR_INTR_MASK_0,
+	.ack_base = ROULEUR_DIG_SWR_INTR_CLEAR_0,
+	.use_ack = 1,
+	.type_base = ROULEUR_DIG_SWR_INTR_LEVEL_0,
+	.runtime_pm = false,
+	.handle_post_irq = rouleur_handle_post_irq,
+	.irq_drv_data = NULL,
+};
+
+static int rouleur_handle_post_irq(void *data)
+{
+	struct rouleur_priv *rouleur = data;
+	u32 status1 = 0, status2 = 0, status3 = 0;
+
+	regmap_read(rouleur->regmap, ROULEUR_DIG_SWR_INTR_STATUS_0, &status1);
+	regmap_read(rouleur->regmap, ROULEUR_DIG_SWR_INTR_STATUS_1, &status2);
+	regmap_read(rouleur->regmap, ROULEUR_DIG_SWR_INTR_STATUS_2, &status3);
+
+	rouleur->tx_swr_dev->slave_irq_pending =
+			((status1 || status2 || status3) ? true : false);
+
+	return IRQ_HANDLED;
+}
+
+static int rouleur_init_reg(struct snd_soc_component *component)
+{
+	/* Enable surge protection */
+	snd_soc_component_update_bits(component, ROULEUR_ANA_SURGE_EN,
+					0xC0, 0xC0);
+	return 0;
+}
+
+static int rouleur_set_port_params(struct snd_soc_component *component,
+				u8 slv_prt_type, u8 *port_id, u8 *num_ch,
+				u8 *ch_mask, u32 *ch_rate,
+				u8 *port_type, u8 path)
+{
+	int i, j;
+	u8 num_ports = 0;
+	struct codec_port_info (*map)[MAX_PORT][MAX_CH_PER_PORT] = NULL;
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	switch (path) {
+	case CODEC_RX:
+		map = &rouleur->rx_port_mapping;
+		num_ports = rouleur->num_rx_ports;
+		break;
+	case CODEC_TX:
+		map = &rouleur->tx_port_mapping;
+		num_ports = rouleur->num_tx_ports;
+		break;
+	default:
+		dev_err(component->dev, "%s Invalid path: %d\n",
+			__func__, path);
+		return -EINVAL;
+	}
+
+	for (i = 0; i <= num_ports; i++) {
+		for (j = 0; j < MAX_CH_PER_PORT; j++) {
+			if ((*map)[i][j].slave_port_type == slv_prt_type)
+				goto found;
+		}
+	}
+
+	dev_err(component->dev, "%s Failed to find slave port for type %u\n",
+					__func__, slv_prt_type);
+	return -EINVAL;
+found:
+	*port_id = i;
+	*num_ch = (*map)[i][j].num_ch;
+	*ch_mask = (*map)[i][j].ch_mask;
+	*ch_rate = (*map)[i][j].ch_rate;
+	*port_type = (*map)[i][j].master_port_type;
+
+	return 0;
+}
+
+static int rouleur_parse_port_mapping(struct device *dev,
+			char *prop, u8 path)
+{
+	u32 *dt_array, map_size, map_length;
+	u32 port_num = 0, ch_mask, ch_rate, old_port_num = 0;
+	u32 slave_port_type, master_port_type;
+	u32 i, ch_iter = 0;
+	int ret = 0;
+	u8 *num_ports = NULL;
+	struct codec_port_info (*map)[MAX_PORT][MAX_CH_PER_PORT] = NULL;
+	struct rouleur_priv *rouleur = dev_get_drvdata(dev);
+
+	switch (path) {
+	case CODEC_RX:
+		map = &rouleur->rx_port_mapping;
+		num_ports = &rouleur->num_rx_ports;
+		break;
+	case CODEC_TX:
+		map = &rouleur->tx_port_mapping;
+		num_ports = &rouleur->num_tx_ports;
+		break;
+	default:
+		dev_err(dev, "%s Invalid path: %d\n",
+			__func__, path);
+		return -EINVAL;
+	}
+
+	if (!of_find_property(dev->of_node, prop,
+				&map_size)) {
+		dev_err(dev, "missing port mapping prop %s\n", prop);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	map_length = map_size / (NUM_SWRS_DT_PARAMS * sizeof(u32));
+
+	dt_array = kzalloc(map_size, GFP_KERNEL);
+
+	if (!dt_array) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	ret = of_property_read_u32_array(dev->of_node, prop, dt_array,
+				NUM_SWRS_DT_PARAMS * map_length);
+	if (ret) {
+		dev_err(dev, "%s: Failed to read  port mapping from prop %s\n",
+					__func__, prop);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+
+	for (i = 0; i < map_length; i++) {
+		port_num = dt_array[NUM_SWRS_DT_PARAMS * i];
+		slave_port_type = dt_array[NUM_SWRS_DT_PARAMS * i + 1];
+		ch_mask = dt_array[NUM_SWRS_DT_PARAMS * i + 2];
+		ch_rate = dt_array[NUM_SWRS_DT_PARAMS * i + 3];
+		master_port_type = dt_array[NUM_SWRS_DT_PARAMS * i + 4];
+
+		if (port_num != old_port_num)
+			ch_iter = 0;
+
+		(*map)[port_num][ch_iter].slave_port_type = slave_port_type;
+		(*map)[port_num][ch_iter].ch_mask = ch_mask;
+		(*map)[port_num][ch_iter].master_port_type = master_port_type;
+		(*map)[port_num][ch_iter].num_ch = __sw_hweight8(ch_mask);
+		(*map)[port_num][ch_iter++].ch_rate = ch_rate;
+		old_port_num = port_num;
+	}
+	*num_ports = port_num;
+
+err_pdata_fail:
+	kfree(dt_array);
+err:
+	return ret;
+}
+
+static int rouleur_tx_connect_port(struct snd_soc_component *component,
+					u8 slv_port_type, u8 enable)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	u8 port_id;
+	u8 num_ch;
+	u8 ch_mask;
+	u32 ch_rate;
+	u8 port_type;
+	u8 num_port = 1;
+	int ret = 0;
+
+	ret = rouleur_set_port_params(component, slv_port_type, &port_id,
+				&num_ch, &ch_mask, &ch_rate,
+				&port_type, CODEC_TX);
+
+	if (ret) {
+		dev_err(rouleur->dev, "%s:Failed to set port params: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	if (enable)
+		ret = swr_connect_port(rouleur->tx_swr_dev, &port_id,
+					num_port, &ch_mask, &ch_rate,
+					 &num_ch, &port_type);
+	else
+		ret = swr_disconnect_port(rouleur->tx_swr_dev, &port_id,
+					num_port, &ch_mask, &port_type);
+	return ret;
+
+}
+static int rouleur_rx_connect_port(struct snd_soc_component *component,
+					u8 slv_port_type, u8 enable)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	u8 port_id;
+	u8 num_ch;
+	u8 ch_mask;
+	u32 ch_rate;
+	u8 port_type;
+	u8 num_port = 1;
+	int ret = 0;
+
+	ret = rouleur_set_port_params(component, slv_port_type, &port_id,
+				&num_ch, &ch_mask, &ch_rate,
+				&port_type, CODEC_RX);
+
+	if (ret) {
+		dev_err(rouleur->dev, "%s:Failed to set port params: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	if (enable)
+		ret = swr_connect_port(rouleur->rx_swr_dev, &port_id,
+					num_port, &ch_mask, &ch_rate,
+					&num_ch, &port_type);
+	else
+		ret = swr_disconnect_port(rouleur->rx_swr_dev, &port_id,
+					num_port, &ch_mask, &port_type);
+	return ret;
+}
+
+static int rouleur_global_mbias_enable(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	mutex_lock(&rouleur->main_bias_lock);
+	if (rouleur->mbias_cnt == 0) {
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_MBIAS_EN, 0x20, 0x20);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_MBIAS_EN, 0x10, 0x10);
+		usleep_range(1000, 1100);
+	}
+	rouleur->mbias_cnt++;
+	mutex_unlock(&rouleur->main_bias_lock);
+
+	return 0;
+}
+
+static int rouleur_global_mbias_disable(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	mutex_lock(&rouleur->main_bias_lock);
+	if (rouleur->mbias_cnt == 0) {
+		dev_dbg(rouleur->dev, "%s:mbias already disabled\n", __func__);
+		mutex_unlock(&rouleur->main_bias_lock);
+		return 0;
+	}
+	rouleur->mbias_cnt--;
+	if (rouleur->mbias_cnt == 0) {
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_MBIAS_EN, 0x10, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_MBIAS_EN, 0x20, 0x00);
+	}
+	mutex_unlock(&rouleur->main_bias_lock);
+
+	return 0;
+}
+
+static int rouleur_rx_clk_enable(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	mutex_lock(&rouleur->rx_clk_lock);
+	if (rouleur->rx_clk_cnt == 0) {
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x10, 0x10);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x20, 0x20);
+		usleep_range(5000, 5100);
+		rouleur_global_mbias_enable(component);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_FSM_CLK, 0x11, 0x11);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_FSM_CLK, 0x80, 0x80);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_NCP_EN, 0x01, 0x01);
+		usleep_range(500, 510);
+	}
+	rouleur->rx_clk_cnt++;
+	mutex_unlock(&rouleur->rx_clk_lock);
+
+	return 0;
+}
+
+static int rouleur_rx_clk_disable(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	mutex_lock(&rouleur->rx_clk_lock);
+	if (rouleur->rx_clk_cnt == 0) {
+		dev_dbg(rouleur->dev, "%s:clk already disabled\n", __func__);
+		mutex_unlock(&rouleur->rx_clk_lock);
+		return 0;
+	}
+	rouleur->rx_clk_cnt--;
+	if (rouleur->rx_clk_cnt == 0) {
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_FSM_CLK, 0x80, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_FSM_CLK, 0x11, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_NCP_EN, 0x01, 0x00);
+		rouleur_global_mbias_disable(component);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x20, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x10, 0x00);
+
+	}
+	mutex_unlock(&rouleur->rx_clk_lock);
+	return 0;
+}
+
+/*
+ * rouleur_soc_get_mbhc: get rouleur_mbhc handle of corresponding component
+ * @component: handle to snd_soc_component *
+ *
+ * return rouleur_mbhc handle or error code in case of failure
+ */
+struct rouleur_mbhc *rouleur_soc_get_mbhc(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur;
+
+	if (!component) {
+		pr_err("%s: Invalid params, NULL component\n", __func__);
+		return NULL;
+	}
+	rouleur = snd_soc_component_get_drvdata(component);
+
+	if (!rouleur) {
+		pr_err("%s: Invalid params, NULL tavil\n", __func__);
+		return NULL;
+	}
+
+	return rouleur->mbhc;
+}
+EXPORT_SYMBOL(rouleur_soc_get_mbhc);
+
+static int rouleur_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		rouleur_rx_clk_enable(component);
+		set_bit(HPH_COMP_DELAY, &rouleur->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (rouleur->comp1_enable) {
+			snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_COMP_CTL_0,
+				0x02, 0x02);
+
+			if (rouleur->comp2_enable)
+				snd_soc_component_update_bits(component,
+					ROULEUR_DIG_SWR_CDC_COMP_CTL_0,
+					0x01, 0x01);
+			/*
+			 * 5ms sleep is required after COMP is enabled as per
+			 * HW requirement
+			 */
+			if (test_bit(HPH_COMP_DELAY, &rouleur->status_mask)) {
+				usleep_range(5000, 5100);
+				clear_bit(HPH_COMP_DELAY,
+					&rouleur->status_mask);
+			}
+		} else {
+			snd_soc_component_update_bits(component,
+					ROULEUR_DIG_SWR_CDC_COMP_CTL_0,
+					0x02, 0x00);
+		}
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX0_CTL,
+				0x7C, 0x7C);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL,
+				0x04, 0x04);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x01, 0x01);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_PDM_WD_CTL0,
+				0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+			ROULEUR_DIG_SWR_PDM_WD_CTL0,
+			0x03, 0x00);
+		snd_soc_component_update_bits(component,
+			ROULEUR_DIG_SWR_CDC_RX_CLK_CTL,
+			0x01, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int rouleur_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		rouleur_rx_clk_enable(component);
+		set_bit(HPH_COMP_DELAY, &rouleur->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (rouleur->comp2_enable) {
+			snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_COMP_CTL_0,
+				0x01, 0x01);
+
+			if (rouleur->comp1_enable)
+				snd_soc_component_update_bits(component,
+					ROULEUR_DIG_SWR_CDC_COMP_CTL_0,
+					0x02, 0x02);
+			/*
+			 * 5ms sleep is required after COMP is enabled as per
+			 * HW requirement
+			 */
+			if (test_bit(HPH_COMP_DELAY, &rouleur->status_mask)) {
+				usleep_range(5000, 5100);
+				clear_bit(HPH_COMP_DELAY,
+					&rouleur->status_mask);
+			}
+		} else {
+			snd_soc_component_update_bits(component,
+					ROULEUR_DIG_SWR_CDC_COMP_CTL_0,
+					0x01, 0x00);
+		}
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX1_CTL,
+				0x7C, 0x7C);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL,
+				0x08, 0x08);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x02, 0x02);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_PDM_WD_CTL1,
+				0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+			ROULEUR_DIG_SWR_PDM_WD_CTL1,
+			0x03, 0x00);
+		snd_soc_component_update_bits(component,
+			ROULEUR_DIG_SWR_CDC_RX_CLK_CTL, 0x02, 0x00);
+		break;
+
+	}
+
+	return 0;
+}
+
+static int rouleur_codec_ear_lo_dac_event(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol,
+				       int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		rouleur_rx_clk_enable(component);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX0_CTL,
+				0x7C, 0x7C);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL,
+				0x01, 0x01);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_PDM_WD_CTL0,
+				0x03, 0x03);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_GAIN_CTL,
+				0x04, 0x04);
+
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_PDM_WD_CTL0,
+				0x03, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_RX_CLK_CTL,
+				0x01, 0x00);
+
+		break;
+	};
+	return 0;
+
+}
+
+static int rouleur_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev,
+				    rouleur->rx_swr_dev->dev_num,
+				    true);
+
+		snd_soc_component_update_bits(component,
+					ROULEUR_ANA_HPHPA_CNP_CTL_2,
+					0x40, 0x40);
+		set_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		/* TODO: WHY SECOND TIME */
+		ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev,
+					    rouleur->rx_swr_dev->dev_num,
+					    true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * 5ms sleep is required after PA is enabled as per
+		 * HW requirement.
+		 */
+		if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		}
+
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX2 << 0x10));
+		wcd_enable_irq(&rouleur->irq_info,
+				ROULEUR_IRQ_HPHR_PDM_WD_INT);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		wcd_disable_irq(&rouleur->irq_info,
+				ROULEUR_IRQ_HPHR_PDM_WD_INT);
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX2 << 0x10 | 0x1));
+		blocking_notifier_call_chain(&rouleur->mbhc->notifier,
+					     WCD_EVENT_PRE_HPHR_PA_OFF,
+					     &rouleur->mbhc->wcd_mbhc);
+		set_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/*
+		 * 7ms sleep is required after PA is disabled as per
+		 * HW requirement. If compander is disabled, then
+		 * 20ms delay is required.
+		 */
+		if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) {
+
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		}
+
+		blocking_notifier_call_chain(&rouleur->mbhc->notifier,
+					     WCD_EVENT_POST_HPHR_PA_OFF,
+					     &rouleur->mbhc->wcd_mbhc);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				0x40, 0x00);
+		break;
+	};
+	return ret;
+}
+
+static int rouleur_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev,
+				    rouleur->rx_swr_dev->dev_num,
+				    true);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				0x80, 0x80);
+		set_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * 5ms sleep is required after PA is enabled as per
+		 * HW requirement.
+		 */
+		if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		}
+
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX1 << 0x10));
+		wcd_enable_irq(&rouleur->irq_info,
+				ROULEUR_IRQ_HPHL_PDM_WD_INT);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		wcd_disable_irq(&rouleur->irq_info,
+				ROULEUR_IRQ_HPHL_PDM_WD_INT);
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX1 << 0x10 | 0x1));
+		blocking_notifier_call_chain(&rouleur->mbhc->notifier,
+					     WCD_EVENT_PRE_HPHL_PA_OFF,
+					     &rouleur->mbhc->wcd_mbhc);
+		set_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/*
+		 * 5ms sleep is required after PA is disabled as per
+		 * HW requirement.
+		 */
+		if (test_bit(HPH_PA_DELAY, &rouleur->status_mask)) {
+			usleep_range(5000, 5100);
+			clear_bit(HPH_PA_DELAY, &rouleur->status_mask);
+		}
+
+		blocking_notifier_call_chain(&rouleur->mbhc->notifier,
+					     WCD_EVENT_POST_HPHL_PA_OFF,
+					     &rouleur->mbhc->wcd_mbhc);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_HPHPA_CNP_CTL_2,
+				0x80, 0x00);
+
+		break;
+	};
+	return ret;
+}
+
+static int rouleur_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol,
+				       int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev,
+			    rouleur->rx_swr_dev->dev_num,
+			    true);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x80, 0x80);
+		usleep_range(5000, 5100);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX1 << 0x10));
+			wcd_enable_irq(&rouleur->irq_info,
+					ROULEUR_IRQ_HPHL_PDM_WD_INT);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		wcd_disable_irq(&rouleur->irq_info,
+				ROULEUR_IRQ_HPHL_PDM_WD_INT);
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX1 << 0x10 | 0x1));
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x80, 0x00);
+		usleep_range(5000, 5100);
+	};
+	return ret;
+}
+
+static int rouleur_codec_enable_lo_pa(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol,
+				       int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = swr_slvdev_datapath_control(rouleur->rx_swr_dev,
+			    rouleur->rx_swr_dev->dev_num,
+			    true);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x40, 0x40);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x80, 0x80);
+		usleep_range(5000, 5100);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX1 << 0x10));
+			wcd_enable_irq(&rouleur->irq_info,
+					ROULEUR_IRQ_HPHL_PDM_WD_INT);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+			wcd_disable_irq(&rouleur->irq_info,
+					ROULEUR_IRQ_HPHL_PDM_WD_INT);
+		if (rouleur->update_wcd_event)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_RX_MUTE,
+						(WCD_RX1 << 0x10 | 0x1));
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x80, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x40, 0x00);
+		usleep_range(5000, 5100);
+	};
+	return ret;
+}
+
+static int rouleur_enable_rx1(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol,
+			      int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		rouleur_rx_connect_port(component, HPH_L, true);
+		if (rouleur->comp1_enable)
+			rouleur_rx_connect_port(component, COMP_L, true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		rouleur_rx_connect_port(component, HPH_L, false);
+		if (rouleur->comp1_enable)
+			rouleur_rx_connect_port(component, COMP_L, false);
+		rouleur_rx_clk_disable(component);
+		break;
+	};
+	return 0;
+}
+
+static int rouleur_enable_rx2(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		rouleur_rx_connect_port(component, HPH_R, true);
+		if (rouleur->comp2_enable)
+			rouleur_rx_connect_port(component, COMP_R, true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		rouleur_rx_connect_port(component, HPH_R, false);
+		if (rouleur->comp2_enable)
+			rouleur_rx_connect_port(component, COMP_R, false);
+		rouleur_rx_clk_disable(component);
+		break;
+	};
+
+	return 0;
+}
+
+static int rouleur_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	u16 dmic_clk_reg;
+	s32 *dmic_clk_cnt;
+	unsigned int dmic;
+	char *wname;
+	int ret = 0;
+
+	wname = strpbrk(w->name, "01");
+
+	if (!wname) {
+		dev_err(component->dev, "%s: widget not found\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = kstrtouint(wname, 10, &dmic);
+	if (ret < 0) {
+		dev_err(component->dev, "%s: Invalid DMIC line on the codec\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (dmic) {
+	case 0:
+	case 1:
+		dmic_clk_cnt = &(rouleur->dmic_0_1_clk_cnt);
+		dmic_clk_reg = ROULEUR_DIG_SWR_CDC_DMIC1_CTL;
+		break;
+	default:
+		dev_err(component->dev, "%s: Invalid DMIC Selection\n",
+			__func__);
+		return -EINVAL;
+	};
+	dev_dbg(component->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n",
+			__func__, event,  dmic, *dmic_clk_cnt);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_update_bits(component,
+			ROULEUR_DIG_SWR_CDC_AMIC_CTL, 0x02, 0x00);
+		snd_soc_component_update_bits(component,
+			dmic_clk_reg, 0x08, 0x08);
+		rouleur_tx_connect_port(component, DMIC0 + (w->shift), true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		rouleur_tx_connect_port(component, DMIC0 + (w->shift), false);
+		snd_soc_component_update_bits(component,
+			dmic_clk_reg, 0x08, 0x00);
+		snd_soc_component_update_bits(component,
+			ROULEUR_DIG_SWR_CDC_AMIC_CTL, 0x02, 0x02);
+		break;
+
+	};
+	return 0;
+}
+
+static int rouleur_tx_swr_ctrl(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *kcontrol,
+				    int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = swr_slvdev_datapath_control(rouleur->tx_swr_dev,
+		    rouleur->tx_swr_dev->dev_num,
+		    true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = swr_slvdev_datapath_control(rouleur->tx_swr_dev,
+		    rouleur->tx_swr_dev->dev_num,
+		    false);
+		break;
+	};
+
+	return ret;
+}
+
+static int rouleur_codec_enable_adc(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *kcontrol,
+				    int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur =
+			snd_soc_component_get_drvdata(component);
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Enable BCS for Headset mic */
+		if (w->shift == 1 && !(snd_soc_component_read32(component,
+				ROULEUR_ANA_TX_AMIC2) & 0x10)) {
+			rouleur_tx_connect_port(component, MBHC, true);
+			set_bit(AMIC2_BCS_ENABLE, &rouleur->status_mask);
+		}
+		rouleur_tx_connect_port(component, ADC1 + (w->shift), true);
+		rouleur_global_mbias_enable(component);
+		if (w->shift)
+			snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+				0x30, 0x30);
+		else
+			snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+				0x03, 0x03);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		rouleur_tx_connect_port(component, ADC1 + (w->shift), false);
+		if (w->shift == 1 &&
+			test_bit(AMIC2_BCS_ENABLE, &rouleur->status_mask)) {
+			rouleur_tx_connect_port(component, MBHC, false);
+			clear_bit(AMIC2_BCS_ENABLE, &rouleur->status_mask);
+		}
+		if (w->shift)
+			snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+				0x30, 0x00);
+		else
+			snd_soc_component_update_bits(component,
+				ROULEUR_DIG_SWR_CDC_TX_ANA_MODE_0_1,
+				0x03, 0x00);
+		rouleur_global_mbias_disable(component);
+		break;
+	};
+
+	return 0;
+}
+
+/*
+ * rouleur_get_micb_vout_ctl_val: converts micbias from volts to register value
+ * @micb_mv: micbias in mv
+ *
+ * return register value converted
+ */
+int rouleur_get_micb_vout_ctl_val(u32 micb_mv)
+{
+	/* min micbias voltage is 1.6V and maximum is 2.85V */
+	if (micb_mv < 1600 || micb_mv > 2850) {
+		pr_err("%s: unsupported micbias voltage\n", __func__);
+		return -EINVAL;
+	}
+
+	return (micb_mv - 1600) / 50;
+}
+EXPORT_SYMBOL(rouleur_get_micb_vout_ctl_val);
+
+/*
+ * rouleur_mbhc_micb_adjust_voltage: adjust specific micbias voltage
+ * @component: handle to snd_soc_component *
+ * @req_volt: micbias voltage to be set
+ * @micb_num: micbias to be set, e.g. micbias1 or micbias2
+ *
+ * return 0 if adjustment is success or error code in case of failure
+ */
+int rouleur_mbhc_micb_adjust_voltage(struct snd_soc_component *component,
+				   int req_volt, int micb_num)
+{
+	struct rouleur_priv *rouleur =
+			snd_soc_component_get_drvdata(component);
+	int cur_vout_ctl, req_vout_ctl;
+	int micb_reg, micb_val, micb_en;
+	int ret = 0;
+	int pullup_mask;
+
+	micb_reg = ROULEUR_ANA_MICBIAS_MICB_1_2_EN;
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_val = snd_soc_component_read32(component, micb_reg);
+		micb_en = (micb_val & 0x40) >> 6;
+		pullup_mask = 0x20;
+		break;
+	case MIC_BIAS_2:
+		micb_val = snd_soc_component_read32(component, micb_reg);
+		micb_en = (micb_val & 0x04) >> 2;
+		pullup_mask = 0x02;
+		break;
+	case MIC_BIAS_3:
+	default:
+		dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+			__func__, micb_num);
+		return -EINVAL;
+	}
+	mutex_lock(&rouleur->micb_lock);
+
+	/*
+	 * If requested micbias voltage is same as current micbias
+	 * voltage, then just return. Otherwise, adjust voltage as
+	 * per requested value. If micbias is already enabled, then
+	 * to avoid slow micbias ramp-up or down enable pull-up
+	 * momentarily, change the micbias value and then re-enable
+	 * micbias.
+	 */
+	cur_vout_ctl = (snd_soc_component_read32(component,
+				ROULEUR_ANA_MICBIAS_LDO_1_SETTING)) & 0xF8;
+
+	req_vout_ctl = rouleur_get_micb_vout_ctl_val(req_volt);
+	if (req_vout_ctl < 0) {
+		ret = -EINVAL;
+		goto exit;
+	}
+	if (cur_vout_ctl == req_vout_ctl) {
+		ret = 0;
+		goto exit;
+	}
+
+	dev_dbg(component->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n",
+		 __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl),
+		 req_volt, micb_en);
+
+	if (micb_en == 0x1)
+		snd_soc_component_update_bits(component, micb_reg, pullup_mask,
+					      pullup_mask);
+
+	snd_soc_component_update_bits(component,
+		ROULEUR_ANA_MICBIAS_LDO_1_SETTING, 0xF8, req_vout_ctl);
+
+	if (micb_en == 0x1) {
+		snd_soc_component_update_bits(component, micb_reg,
+					      pullup_mask, 0x00);
+		/*
+		 * Add 2ms delay as per HW requirement after enabling
+		 * micbias
+		 */
+		usleep_range(2000, 2100);
+	}
+exit:
+	mutex_unlock(&rouleur->micb_lock);
+	return ret;
+}
+EXPORT_SYMBOL(rouleur_mbhc_micb_adjust_voltage);
+
+int rouleur_micbias_control(struct snd_soc_component *component,
+				int micb_num, int req, bool is_dapm)
+{
+
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int micb_index = micb_num - 1;
+	u16 micb_reg;
+	int pre_off_event = 0, post_off_event = 0;
+	int post_on_event = 0, post_dapm_off = 0;
+	int post_dapm_on = 0;
+	u8 pullup_mask = 0, enable_mask = 0;
+
+	if ((micb_index < 0) || (micb_index > ROULEUR_MAX_MICBIAS - 1)) {
+		dev_err(component->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+			__func__, micb_index);
+		return -EINVAL;
+	}
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_reg = ROULEUR_ANA_MICBIAS_MICB_1_2_EN;
+		pullup_mask = 0x20;
+		enable_mask = 0x40;
+		break;
+	case MIC_BIAS_2:
+		micb_reg = ROULEUR_ANA_MICBIAS_MICB_1_2_EN;
+		pullup_mask = 0x02;
+		enable_mask = 0x04;
+		pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF;
+		post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF;
+		post_on_event = WCD_EVENT_POST_MICBIAS_2_ON;
+		post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON;
+		post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF;
+		break;
+	case MIC_BIAS_3:
+		micb_reg = ROULEUR_ANA_MICBIAS_MICB_3_EN;
+		pullup_mask = 0x02;
+		break;
+	default:
+		dev_err(component->dev, "%s: Invalid micbias number: %d\n",
+			__func__, micb_num);
+		return -EINVAL;
+	};
+	mutex_lock(&rouleur->micb_lock);
+
+	switch (req) {
+	case MICB_PULLUP_ENABLE:
+		rouleur->pullup_ref[micb_index]++;
+		if ((rouleur->pullup_ref[micb_index] == 1) &&
+		    (rouleur->micb_ref[micb_index] == 0))
+			snd_soc_component_update_bits(component, micb_reg,
+				pullup_mask, pullup_mask);
+		break;
+	case MICB_PULLUP_DISABLE:
+		if (rouleur->pullup_ref[micb_index] > 0)
+			rouleur->pullup_ref[micb_index]--;
+		if ((rouleur->pullup_ref[micb_index] == 0) &&
+		    (rouleur->micb_ref[micb_index] == 0))
+			snd_soc_component_update_bits(component, micb_reg,
+				pullup_mask, 0x00);
+		break;
+	case MICB_ENABLE:
+		rouleur->micb_ref[micb_index]++;
+		if (rouleur->micb_ref[micb_index] == 1) {
+			rouleur_global_mbias_enable(component);
+			snd_soc_component_update_bits(component,
+				micb_reg, enable_mask, enable_mask);
+			if (post_on_event)
+				blocking_notifier_call_chain(
+					&rouleur->mbhc->notifier, post_on_event,
+					&rouleur->mbhc->wcd_mbhc);
+		}
+		if (is_dapm && post_dapm_on && rouleur->mbhc)
+			blocking_notifier_call_chain(
+				&rouleur->mbhc->notifier, post_dapm_on,
+				&rouleur->mbhc->wcd_mbhc);
+		break;
+	case MICB_DISABLE:
+		if (rouleur->micb_ref[micb_index] > 0)
+			rouleur->micb_ref[micb_index]--;
+		if ((rouleur->micb_ref[micb_index] == 0) &&
+			 (rouleur->pullup_ref[micb_index] == 0)) {
+			if (pre_off_event && rouleur->mbhc)
+				blocking_notifier_call_chain(
+					&rouleur->mbhc->notifier, pre_off_event,
+					&rouleur->mbhc->wcd_mbhc);
+			snd_soc_component_update_bits(component, micb_reg,
+				enable_mask, 0x00);
+			rouleur_global_mbias_disable(component);
+			if (post_off_event && rouleur->mbhc)
+				blocking_notifier_call_chain(
+					&rouleur->mbhc->notifier,
+					post_off_event,
+					&rouleur->mbhc->wcd_mbhc);
+		}
+		if (is_dapm && post_dapm_off && rouleur->mbhc)
+			blocking_notifier_call_chain(
+				&rouleur->mbhc->notifier, post_dapm_off,
+				&rouleur->mbhc->wcd_mbhc);
+		break;
+	};
+
+	dev_dbg(component->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n",
+		__func__, micb_num, rouleur->micb_ref[micb_index],
+		rouleur->pullup_ref[micb_index]);
+	mutex_unlock(&rouleur->micb_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(rouleur_micbias_control);
+
+void rouleur_disable_bcs_before_slow_insert(struct snd_soc_component *component,
+					    bool bcs_disable)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	if (rouleur->update_wcd_event) {
+		if (bcs_disable)
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_BCS_CLK_OFF, 0);
+		else
+			rouleur->update_wcd_event(rouleur->handle,
+						WCD_BOLERO_EVT_BCS_CLK_OFF, 1);
+	}
+}
+
+static int rouleur_get_logical_addr(struct swr_device *swr_dev)
+{
+	int ret = 0;
+	uint8_t devnum = 0;
+	int num_retry = NUM_ATTEMPTS;
+
+	do {
+		ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum);
+		if (ret) {
+			dev_err(&swr_dev->dev,
+				"%s get devnum %d for dev addr %lx failed\n",
+				__func__, devnum, swr_dev->addr);
+			/* retry after 1ms */
+			usleep_range(1000, 1010);
+		}
+	} while (ret && --num_retry);
+	swr_dev->dev_num = devnum;
+	return 0;
+}
+
+static int rouleur_event_notify(struct notifier_block *block,
+				unsigned long val,
+				void *data)
+{
+	u16 event = (val & 0xffff);
+	int ret = 0;
+	struct rouleur_priv *rouleur = dev_get_drvdata((struct device *)data);
+	struct snd_soc_component *component = rouleur->component;
+	struct wcd_mbhc *mbhc;
+
+	switch (event) {
+	case BOLERO_WCD_EVT_PA_OFF_PRE_SSR:
+		snd_soc_component_update_bits(component,
+					ROULEUR_ANA_HPHPA_CNP_CTL_2,
+					0xC0, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x40, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x80, 0x00);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x40, 0x40);
+		snd_soc_component_update_bits(component,
+				ROULEUR_ANA_COMBOPA_CTL,
+				0x80, 0x00);
+		break;
+	case BOLERO_WCD_EVT_SSR_DOWN:
+		rouleur->mbhc->wcd_mbhc.deinit_in_progress = true;
+		mbhc = &rouleur->mbhc->wcd_mbhc;
+		rouleur_mbhc_ssr_down(rouleur->mbhc, component);
+		rouleur_reset(rouleur->dev, 0x01);
+		break;
+	case BOLERO_WCD_EVT_SSR_UP:
+		rouleur_reset(rouleur->dev, 0x00);
+		/* allow reset to take effect */
+		usleep_range(10000, 10010);
+		rouleur_get_logical_addr(rouleur->tx_swr_dev);
+		rouleur_get_logical_addr(rouleur->rx_swr_dev);
+
+		rouleur_init_reg(component);
+		regcache_mark_dirty(rouleur->regmap);
+		regcache_sync(rouleur->regmap);
+		/* Initialize MBHC module */
+		mbhc = &rouleur->mbhc->wcd_mbhc;
+		ret = rouleur_mbhc_post_ssr_init(rouleur->mbhc, component);
+		if (ret) {
+			dev_err(component->dev, "%s: mbhc initialization failed\n",
+				__func__);
+		} else {
+			rouleur_mbhc_hs_detect(component, mbhc->mbhc_cfg);
+		}
+		rouleur->mbhc->wcd_mbhc.deinit_in_progress = false;
+		break;
+	default:
+		dev_err(component->dev, "%s: invalid event %d\n", __func__,
+			event);
+		break;
+	}
+	return 0;
+}
+
+static int __rouleur_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+					  int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	int micb_num;
+
+	dev_dbg(component->dev, "%s: wname: %s, event: %d\n",
+		__func__, w->name, event);
+
+	if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1")))
+		micb_num = MIC_BIAS_1;
+	else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2")))
+		micb_num = MIC_BIAS_2;
+	else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3")))
+		micb_num = MIC_BIAS_3;
+	else
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Micbias LD0 enable not supported for MicBias 3*/
+		if (micb_num == MIC_BIAS_3)
+			rouleur_micbias_control(component, micb_num,
+				MICB_PULLUP_ENABLE, true);
+		else
+			rouleur_micbias_control(component, micb_num,
+				MICB_ENABLE, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(1000, 1100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (micb_num == MIC_BIAS_3)
+			rouleur_micbias_control(component, micb_num,
+				MICB_PULLUP_DISABLE, true);
+		else
+			rouleur_micbias_control(component, micb_num,
+				MICB_DISABLE, true);
+		break;
+	};
+
+	return 0;
+
+}
+
+static int rouleur_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	return __rouleur_codec_enable_micbias(w, event);
+}
+
+static int __rouleur_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+						 int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	int micb_num;
+
+	dev_dbg(component->dev, "%s: wname: %s, event: %d\n",
+		__func__, w->name, event);
+
+	if (strnstr(w->name, "VA MIC BIAS1", sizeof("VA MIC BIAS1")))
+		micb_num = MIC_BIAS_1;
+	else if (strnstr(w->name, "VA MIC BIAS2", sizeof("VA MIC BIAS2")))
+		micb_num = MIC_BIAS_2;
+	else if (strnstr(w->name, "VA MIC BIAS3", sizeof("VA MIC BIAS3")))
+		micb_num = MIC_BIAS_3;
+	else
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		rouleur_micbias_control(component, micb_num,
+					MICB_PULLUP_ENABLE, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* 1 msec delay as per HW requirement */
+		usleep_range(1000, 1100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		rouleur_micbias_control(component, micb_num,
+					MICB_PULLUP_DISABLE, true);
+		break;
+	};
+
+	return 0;
+
+}
+
+static int rouleur_codec_enable_micbias_pullup(struct snd_soc_dapm_widget *w,
+					       struct snd_kcontrol *kcontrol,
+					       int event)
+{
+	return __rouleur_codec_enable_micbias_pullup(w, event);
+}
+
+static int rouleur_get_compander(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_component *component =
+				snd_soc_kcontrol_component(kcontrol);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+
+	ucontrol->value.integer.value[0] = hphr ? rouleur->comp2_enable :
+						rouleur->comp1_enable;
+	return 0;
+}
+
+static int rouleur_set_compander(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	int value = ucontrol->value.integer.value[0];
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+	if (hphr)
+		rouleur->comp2_enable = value;
+	else
+		rouleur->comp1_enable = value;
+
+	return 0;
+}
+
+static int rouleur_codec_enable_pa_vpos(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol,
+					 int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	struct rouleur_pdata *pdata = NULL;
+	int ret = 0;
+
+	pdata = dev_get_platdata(rouleur->dev);
+
+	if (!pdata) {
+		dev_err(component->dev, "%s: pdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(component->dev, "%s wname: %s event: %d\n", __func__,
+		w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (test_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask)) {
+			dev_dbg(component->dev,
+				"%s: vpos already in enabled state\n",
+				__func__);
+			clear_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask);
+			return 0;
+		}
+		ret = msm_cdc_enable_ondemand_supply(rouleur->dev,
+						rouleur->supplies,
+						pdata->regulator,
+						pdata->num_supplies,
+						"cdc-pa-vpos");
+		if (ret == -EINVAL) {
+			dev_err(component->dev, "%s: pa vpos is not enabled\n",
+				__func__);
+			return ret;
+		}
+		clear_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask);
+		/*
+		 * 200us sleep is required after LDO15 is enabled as per
+		 * HW requirement
+		 */
+		usleep_range(200, 250);
+
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		set_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_kcontrol_new rouleur_snd_controls[] = {
+	SOC_SINGLE_EXT("HPHL_COMP Switch", SND_SOC_NOPM, 0, 1, 0,
+		rouleur_get_compander, rouleur_set_compander),
+	SOC_SINGLE_EXT("HPHR_COMP Switch", SND_SOC_NOPM, 1, 1, 0,
+		rouleur_get_compander, rouleur_set_compander),
+
+	SOC_SINGLE_TLV("HPHL Volume", ROULEUR_ANA_HPHPA_L_GAIN, 0, 20, 1,
+					line_gain),
+	SOC_SINGLE_TLV("HPHR Volume", ROULEUR_ANA_HPHPA_R_GAIN, 0, 20, 1,
+					line_gain),
+	SOC_SINGLE_TLV("ADC1 Volume", ROULEUR_ANA_TX_AMIC1, 0, 8, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC2 Volume", ROULEUR_ANA_TX_AMIC2, 0, 8, 0,
+			analog_gain),
+};
+
+static const struct snd_kcontrol_new adc1_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new adc2_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic1_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new dmic2_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new ear_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new lo_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphr_rdac_switch[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const char * const adc2_mux_text[] = {
+	"INP2", "INP3"
+};
+
+static const struct soc_enum adc2_enum =
+	SOC_ENUM_SINGLE(ROULEUR_ANA_TX_AMIC2, 4,
+		ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+
+static const struct snd_kcontrol_new tx_adc2_mux =
+	SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+
+static const struct snd_soc_dapm_widget rouleur_dapm_widgets[] = {
+
+	/*input widgets*/
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_INPUT("AMIC2"),
+	SND_SOC_DAPM_INPUT("AMIC3"),
+	SND_SOC_DAPM_INPUT("IN1_HPHL"),
+	SND_SOC_DAPM_INPUT("IN2_HPHR"),
+
+	/*tx widgets*/
+	SND_SOC_DAPM_ADC_E("ADC1", NULL, SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_adc,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC2", NULL, SND_SOC_NOPM, 1, 0,
+				rouleur_codec_enable_adc,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0,
+				&tx_adc2_mux),
+
+	/*tx mixers*/
+	SND_SOC_DAPM_MIXER_E("ADC1_MIXER", SND_SOC_NOPM, 0, 0,
+				adc1_switch, ARRAY_SIZE(adc1_switch),
+				rouleur_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("ADC2_MIXER", SND_SOC_NOPM, 0, 0,
+				adc2_switch, ARRAY_SIZE(adc2_switch),
+				rouleur_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+
+	/* micbias widgets*/
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_micbias,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_micbias,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_micbias,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("PA_VPOS", SND_SOC_NOPM, 0, 0,
+			     rouleur_codec_enable_pa_vpos,
+			     SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/*rx widgets*/
+	SND_SOC_DAPM_PGA_E("EAR PGA", ROULEUR_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+				rouleur_codec_enable_ear_pa,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LO PGA", ROULEUR_ANA_COMBOPA_CTL, 7, 0, NULL, 0,
+				rouleur_codec_enable_lo_pa,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HPHL PGA", ROULEUR_ANA_HPHPA_CNP_CTL_2, 7, 0, NULL,
+				0, rouleur_codec_enable_hphl_pa,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HPHR PGA", ROULEUR_ANA_HPHPA_CNP_CTL_2, 6, 0, NULL,
+				0, rouleur_codec_enable_hphr_pa,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_DAC_E("RDAC1", NULL, SND_SOC_NOPM, 0, 0,
+				rouleur_codec_hphl_dac_event,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RDAC2", NULL, SND_SOC_NOPM, 0, 0,
+				rouleur_codec_hphr_dac_event,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RDAC3", NULL, SND_SOC_NOPM, 0, 0,
+				rouleur_codec_ear_lo_dac_event,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER_E("RX1", SND_SOC_NOPM, 0, 0, NULL, 0,
+				rouleur_enable_rx1, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX2", SND_SOC_NOPM, 0, 0, NULL, 0,
+				rouleur_enable_rx2, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+
+	/* rx mixer widgets*/
+
+	SND_SOC_DAPM_MIXER("EAR_RDAC", SND_SOC_NOPM, 0, 0,
+			   ear_rdac_switch, ARRAY_SIZE(ear_rdac_switch)),
+	SND_SOC_DAPM_MIXER("LO_RDAC", SND_SOC_NOPM, 0, 0,
+			   lo_rdac_switch, ARRAY_SIZE(lo_rdac_switch)),
+	SND_SOC_DAPM_MIXER("HPHL_RDAC", SND_SOC_NOPM, 0, 0,
+			   hphl_rdac_switch, ARRAY_SIZE(hphl_rdac_switch)),
+	SND_SOC_DAPM_MIXER("HPHR_RDAC", SND_SOC_NOPM, 0, 0,
+			   hphr_rdac_switch, ARRAY_SIZE(hphr_rdac_switch)),
+
+	/*output widgets tx*/
+
+	SND_SOC_DAPM_OUTPUT("ADC1_OUTPUT"),
+	SND_SOC_DAPM_OUTPUT("ADC2_OUTPUT"),
+
+	/*output widgets rx*/
+	SND_SOC_DAPM_OUTPUT("EAR"),
+	SND_SOC_DAPM_OUTPUT("LO"),
+	SND_SOC_DAPM_OUTPUT("HPHL"),
+	SND_SOC_DAPM_OUTPUT("HPHR"),
+
+	/* micbias pull up widgets*/
+	SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS1", SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_micbias_pullup,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS2", SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_micbias_pullup,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("VA MIC BIAS3", SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_micbias_pullup,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+				rouleur_codec_enable_dmic,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 1, 0,
+				rouleur_codec_enable_dmic,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/*tx mixer widgets*/
+	SND_SOC_DAPM_MIXER_E("DMIC1_MIXER", SND_SOC_NOPM, 0,
+				0, dmic1_switch, ARRAY_SIZE(dmic1_switch),
+				rouleur_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("DMIC2_MIXER", SND_SOC_NOPM, 0,
+				0, dmic2_switch, ARRAY_SIZE(dmic2_switch),
+				rouleur_tx_swr_ctrl, SND_SOC_DAPM_PRE_PMU |
+				SND_SOC_DAPM_POST_PMD),
+
+	/*output widgets*/
+	SND_SOC_DAPM_OUTPUT("DMIC1_OUTPUT"),
+	SND_SOC_DAPM_OUTPUT("DMIC2_OUTPUT"),
+};
+
+static const struct snd_soc_dapm_route rouleur_audio_map[] = {
+	{"ADC1_OUTPUT", NULL, "ADC1_MIXER"},
+	{"ADC1_MIXER", "Switch", "ADC1"},
+	{"ADC1", NULL, "AMIC1"},
+
+	{"ADC2_OUTPUT", NULL, "ADC2_MIXER"},
+	{"ADC2_MIXER", "Switch", "ADC2"},
+	{"ADC2", NULL, "ADC2 MUX"},
+	{"ADC2 MUX", "INP3", "AMIC3"},
+	{"ADC2 MUX", "INP2", "AMIC2"},
+
+	{"IN1_HPHL", NULL, "PA_VPOS"},
+	{"RX1", NULL, "IN1_HPHL"},
+	{"RDAC1", NULL, "RX1"},
+	{"HPHL_RDAC", "Switch", "RDAC1"},
+	{"HPHL PGA", NULL, "HPHL_RDAC"},
+	{"HPHL", NULL, "HPHL PGA"},
+
+	{"IN2_HPHR", NULL, "PA_VPOS"},
+	{"RX2", NULL, "IN2_HPHR"},
+	{"RDAC2", NULL, "RX2"},
+	{"HPHR_RDAC", "Switch", "RDAC2"},
+	{"HPHR PGA", NULL, "HPHR_RDAC"},
+	{"HPHR", NULL, "HPHR PGA"},
+
+	{"RDAC3", NULL, "RX1"},
+	{"EAR_RDAC", "Switch", "RDAC3"},
+	{"EAR PGA", NULL, "EAR_RDAC"},
+	{"EAR", NULL, "EAR PGA"},
+
+	{"RDAC3", NULL, "RX1"},
+	{"LO_RDAC", "Switch", "RDAC3"},
+	{"LO PGA", NULL, "LO_RDAC"},
+	{"LO", NULL, "LO PGA"},
+
+	{"DMIC1_OUTPUT", NULL, "DMIC1_MIXER"},
+	{"DMIC1_MIXER", "Switch", "DMIC1"},
+
+	{"DMIC2_OUTPUT", NULL, "DMIC2_MIXER"},
+	{"DMIC2_MIXER", "Switch", "DMIC2"},
+};
+
+static ssize_t rouleur_version_read(struct snd_info_entry *entry,
+				   void *file_private_data,
+				   struct file *file,
+				   char __user *buf, size_t count,
+				   loff_t pos)
+{
+	struct rouleur_priv *priv;
+	char buffer[ROULEUR_VERSION_ENTRY_SIZE];
+	int len = 0;
+
+	priv = (struct rouleur_priv *) entry->private_data;
+	if (!priv) {
+		pr_err("%s: rouleur priv is null\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (priv->version) {
+	case ROULEUR_VERSION_1_0:
+		len = snprintf(buffer, sizeof(buffer), "rouleur_1_0\n");
+		break;
+	default:
+		len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+	}
+
+	return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops rouleur_info_ops = {
+	.read = rouleur_version_read,
+};
+
+/*
+ * rouleur_info_create_codec_entry - creates rouleur module
+ * @codec_root: The parent directory
+ * @component: component instance
+ *
+ * Creates rouleur module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int rouleur_info_create_codec_entry(struct snd_info_entry *codec_root,
+				   struct snd_soc_component *component)
+{
+	struct snd_info_entry *version_entry;
+	struct rouleur_priv *priv;
+	struct snd_soc_card *card;
+
+	if (!codec_root || !component)
+		return -EINVAL;
+
+	priv = snd_soc_component_get_drvdata(component);
+	if (priv->entry) {
+		dev_dbg(priv->dev,
+			"%s:rouleur module already created\n", __func__);
+		return 0;
+	}
+	card = component->card;
+	priv->entry = snd_info_create_subdir(codec_root->module,
+					     "rouleur", codec_root);
+	if (!priv->entry) {
+		dev_dbg(component->dev, "%s: failed to create rouleur entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+	version_entry = snd_info_create_card_entry(card->snd_card,
+						   "version",
+						   priv->entry);
+	if (!version_entry) {
+		dev_dbg(component->dev, "%s: failed to create rouleur version entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry->private_data = priv;
+	version_entry->size = ROULEUR_VERSION_ENTRY_SIZE;
+	version_entry->content = SNDRV_INFO_CONTENT_DATA;
+	version_entry->c.ops = &rouleur_info_ops;
+
+	if (snd_info_register(version_entry) < 0) {
+		snd_info_free_entry(version_entry);
+		return -ENOMEM;
+	}
+	priv->version_entry = version_entry;
+
+	return 0;
+}
+EXPORT_SYMBOL(rouleur_info_create_codec_entry);
+
+static int rouleur_set_micbias_data(struct rouleur_priv *rouleur,
+			      struct rouleur_pdata *pdata)
+{
+	int vout_ctl = 0;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(rouleur->dev, "%s: NULL pdata\n", __func__);
+		return -ENODEV;
+	}
+
+	/* set micbias voltage */
+	vout_ctl = rouleur_get_micb_vout_ctl_val(pdata->micbias.micb1_mv);
+	if (vout_ctl < 0) {
+		rc = -EINVAL;
+		goto done;
+	}
+	regmap_update_bits(rouleur->regmap, ROULEUR_ANA_MICBIAS_LDO_1_SETTING,
+			   0xF8, vout_ctl);
+done:
+	return rc;
+}
+
+static int rouleur_soc_codec_probe(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_component_get_dapm(component);
+	int ret = -EINVAL;
+
+	dev_info(component->dev, "%s()\n", __func__);
+	rouleur = snd_soc_component_get_drvdata(component);
+
+	if (!rouleur)
+		return -EINVAL;
+
+	rouleur->component = component;
+	snd_soc_component_init_regmap(component, rouleur->regmap);
+
+	rouleur->fw_data = devm_kzalloc(component->dev,
+					sizeof(*(rouleur->fw_data)),
+					GFP_KERNEL);
+	if (!rouleur->fw_data) {
+		dev_err(component->dev, "Failed to allocate fw_data\n");
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	set_bit(WCD9XXX_MBHC_CAL, rouleur->fw_data->cal_bit);
+	ret = wcd_cal_create_hwdep(rouleur->fw_data,
+				   WCD9XXX_CODEC_HWDEP_NODE, component);
+
+	if (ret < 0) {
+		dev_err(component->dev, "%s hwdep failed %d\n", __func__, ret);
+		goto done;
+	}
+
+	ret = rouleur_mbhc_init(&rouleur->mbhc, component, rouleur->fw_data);
+	if (ret) {
+		pr_err("%s: mbhc initialization failed\n", __func__);
+		goto done;
+	}
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC1");
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC2");
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC3");
+	snd_soc_dapm_ignore_suspend(dapm, "IN1_HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "IN2_HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "ADC1_OUTPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "ADC2_OUTPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "LO");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC1_OUTPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC2_OUTPUT");
+	snd_soc_dapm_sync(dapm);
+
+	rouleur_init_reg(component);
+
+	rouleur->version = ROULEUR_VERSION_1_0;
+       /* Register event notifier */
+	rouleur->nblock.notifier_call = rouleur_event_notify;
+	if (rouleur->register_notifier) {
+		ret = rouleur->register_notifier(rouleur->handle,
+						&rouleur->nblock,
+						true);
+		if (ret) {
+			dev_err(component->dev,
+				"%s: Failed to register notifier %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+done:
+	return ret;
+}
+
+static void rouleur_soc_codec_remove(struct snd_soc_component *component)
+{
+	struct rouleur_priv *rouleur = snd_soc_component_get_drvdata(component);
+
+	if (!rouleur)
+		return;
+
+	if (rouleur->register_notifier)
+		rouleur->register_notifier(rouleur->handle,
+						&rouleur->nblock,
+						false);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_rouleur = {
+	.name = DRV_NAME,
+	.probe = rouleur_soc_codec_probe,
+	.remove = rouleur_soc_codec_remove,
+	.controls = rouleur_snd_controls,
+	.num_controls = ARRAY_SIZE(rouleur_snd_controls),
+	.dapm_widgets = rouleur_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(rouleur_dapm_widgets),
+	.dapm_routes = rouleur_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(rouleur_audio_map),
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int rouleur_suspend(struct device *dev)
+{
+	struct rouleur_priv *rouleur = NULL;
+	int ret = 0;
+	struct rouleur_pdata *pdata = NULL;
+
+	if (!dev)
+		return -ENODEV;
+
+	rouleur = dev_get_drvdata(dev);
+	if (!rouleur)
+		return -EINVAL;
+
+	pdata = dev_get_platdata(rouleur->dev);
+
+	if (!pdata) {
+		dev_err(dev, "%s: pdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (test_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask)) {
+		ret = msm_cdc_disable_ondemand_supply(rouleur->dev,
+						rouleur->supplies,
+						pdata->regulator,
+						pdata->num_supplies,
+						"cdc-pa-vpos");
+		if (ret == -EINVAL) {
+			dev_err(dev, "%s: pa vpos is not disabled\n",
+				__func__);
+			return 0;
+		}
+		clear_bit(ALLOW_VPOS_DISABLE, &rouleur->status_mask);
+	}
+	return 0;
+}
+
+static int rouleur_resume(struct device *dev)
+{
+	return 0;
+}
+#endif
+
+static int rouleur_reset(struct device *dev, int reset_val)
+{
+	struct rouleur_priv *rouleur = NULL;
+
+	if (!dev)
+		return -ENODEV;
+
+	rouleur = dev_get_drvdata(dev);
+	if (!rouleur)
+		return -EINVAL;
+
+	pm2250_spmi_write(rouleur->spmi_dev, rouleur->reset_reg, reset_val);
+
+	return 0;
+}
+
+static int rouleur_read_of_property_u32(struct device *dev, const char *name,
+					u32 *val)
+{
+	int rc = 0;
+
+	rc = of_property_read_u32(dev->of_node, name, val);
+	if (rc)
+		dev_err(dev, "%s: Looking up %s property in node %s failed\n",
+			__func__, name, dev->of_node->full_name);
+
+	return rc;
+}
+
+static void rouleur_dt_parse_micbias_info(struct device *dev,
+					  struct rouleur_micbias_setting *mb)
+{
+	u32 prop_val = 0;
+	int rc = 0;
+
+	/* MB1 */
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv",
+				    NULL)) {
+		rc = rouleur_read_of_property_u32(dev,
+						  "qcom,cdc-micbias1-mv",
+						  &prop_val);
+		if (!rc)
+			mb->micb1_mv = prop_val;
+	} else {
+		dev_info(dev, "%s: Micbias1 DT property not found\n",
+			__func__);
+	}
+
+	/* MB2 */
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv",
+				    NULL)) {
+		rc = rouleur_read_of_property_u32(dev,
+						  "qcom,cdc-micbias2-mv",
+						  &prop_val);
+		if (!rc)
+			mb->micb2_mv = prop_val;
+	} else {
+		dev_info(dev, "%s: Micbias2 DT property not found\n",
+			__func__);
+	}
+
+	/* MB3 */
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv",
+				    NULL)) {
+		rc = rouleur_read_of_property_u32(dev,
+						  "qcom,cdc-micbias3-mv",
+						  &prop_val);
+		if (!rc)
+			mb->micb3_mv = prop_val;
+	} else {
+		dev_info(dev, "%s: Micbias3 DT property not found\n",
+			__func__);
+	}
+}
+
+struct rouleur_pdata *rouleur_populate_dt_data(struct device *dev)
+{
+	struct rouleur_pdata *pdata = NULL;
+	u32 reg;
+	int ret = 0;
+
+	pdata = kzalloc(sizeof(struct rouleur_pdata),
+				GFP_KERNEL);
+	if (!pdata)
+		return NULL;
+
+	pdata->spmi_np = of_parse_phandle(dev->of_node,
+					"qcom,pmic-spmi-node", 0);
+	if (!pdata->spmi_np) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed\n",
+				__func__, "qcom,pmic-spmi-node",
+				dev->of_node->full_name);
+		kfree(pdata);
+		return NULL;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "qcom,wcd-reset-reg", &reg);
+	if (ret) {
+		dev_err(dev, "%s: Failed to obtain reset reg value %d\n",
+			__func__, ret);
+		kfree(pdata);
+		return NULL;
+	}
+	pdata->reset_reg = reg;
+
+	/* Parse power supplies */
+	msm_cdc_get_power_supplies(dev, &pdata->regulator,
+				   &pdata->num_supplies);
+	if (!pdata->regulator || (pdata->num_supplies <= 0)) {
+		dev_err(dev, "%s: no power supplies defined for codec\n",
+			__func__);
+		kfree(pdata);
+		return NULL;
+	}
+
+	pdata->rx_slave = of_parse_phandle(dev->of_node, "qcom,rx-slave", 0);
+	pdata->tx_slave = of_parse_phandle(dev->of_node, "qcom,tx-slave", 0);
+	rouleur_dt_parse_micbias_info(dev, &pdata->micbias);
+
+	return pdata;
+}
+
+static int rouleur_wakeup(void *handle, bool enable)
+{
+	struct rouleur_priv *priv;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	priv = (struct rouleur_priv *)handle;
+	if (!priv->tx_swr_dev) {
+		pr_err("%s: tx swr dev is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (enable)
+		return swr_device_wakeup_vote(priv->tx_swr_dev);
+	else
+		return swr_device_wakeup_unvote(priv->tx_swr_dev);
+}
+
+static irqreturn_t rouleur_wd_handle_irq(int irq, void *data)
+{
+	pr_err_ratelimited("%s: Watchdog interrupt for irq =%d triggered\n",
+			   __func__, irq);
+	return IRQ_HANDLED;
+}
+
+static int rouleur_bind(struct device *dev)
+{
+	int ret = 0, i = 0;
+	struct rouleur_priv *rouleur = NULL;
+	struct rouleur_pdata *pdata = NULL;
+	struct wcd_ctrl_platform_data *plat_data = NULL;
+	struct platform_device *pdev = NULL;
+
+	rouleur = kzalloc(sizeof(struct rouleur_priv), GFP_KERNEL);
+	if (!rouleur)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, rouleur);
+
+	pdata = rouleur_populate_dt_data(dev);
+	if (!pdata) {
+		dev_err(dev, "%s: Fail to obtain platform data\n", __func__);
+		kfree(rouleur);
+		return -EINVAL;
+	}
+	rouleur->dev = dev;
+	rouleur->dev->platform_data = pdata;
+	pdev = of_find_device_by_node(pdata->spmi_np);
+	if (!pdev) {
+		dev_err(dev, "%s: platform device from SPMI node is NULL\n",
+				__func__);
+		ret = -EINVAL;
+		goto err_bind_all;
+	}
+
+	rouleur->spmi_dev = &pdev->dev;
+	rouleur->reset_reg = pdata->reset_reg;
+	ret = msm_cdc_init_supplies(dev, &rouleur->supplies,
+				    pdata->regulator, pdata->num_supplies);
+	if (!rouleur->supplies) {
+		dev_err(dev, "%s: Cannot init wcd supplies\n",
+			__func__);
+		goto err_bind_all;
+	}
+
+	plat_data = dev_get_platdata(dev->parent);
+	if (!plat_data) {
+		dev_err(dev, "%s: platform data from parent is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_bind_all;
+	}
+	rouleur->handle = (void *)plat_data->handle;
+	if (!rouleur->handle) {
+		dev_err(dev, "%s: handle is NULL\n", __func__);
+		ret = -EINVAL;
+		goto err_bind_all;
+	}
+	rouleur->update_wcd_event = plat_data->update_wcd_event;
+	if (!rouleur->update_wcd_event) {
+		dev_err(dev, "%s: update_wcd_event api is null!\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_bind_all;
+	}
+	rouleur->register_notifier = plat_data->register_notifier;
+	if (!rouleur->register_notifier) {
+		dev_err(dev, "%s: register_notifier api is null!\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_bind_all;
+	}
+
+	ret = msm_cdc_enable_static_supplies(dev, rouleur->supplies,
+					     pdata->regulator,
+					     pdata->num_supplies);
+	if (ret) {
+		dev_err(dev, "%s: wcd static supply enable failed!\n",
+			__func__);
+		goto err_bind_all;
+	}
+
+	rouleur_reset(dev, 0x01);
+	usleep_range(20, 30);
+	rouleur_reset(dev, 0x00);
+	/*
+	 * Add 5msec delay to provide sufficient time for
+	 * soundwire auto enumeration of slave devices as
+	 * as per HW requirement.
+	 */
+	usleep_range(5000, 5010);
+	rouleur->wakeup = rouleur_wakeup;
+
+	ret = component_bind_all(dev, rouleur);
+	if (ret) {
+		dev_err(dev, "%s: Slave bind failed, ret = %d\n",
+			__func__, ret);
+		goto err_bind_all;
+	}
+
+	ret = rouleur_parse_port_mapping(dev, "qcom,rx_swr_ch_map", CODEC_RX);
+	ret |= rouleur_parse_port_mapping(dev, "qcom,tx_swr_ch_map", CODEC_TX);
+
+	if (ret) {
+		dev_err(dev, "Failed to read port mapping\n");
+		goto err;
+	}
+
+	rouleur->rx_swr_dev = get_matching_swr_slave_device(pdata->rx_slave);
+	if (!rouleur->rx_swr_dev) {
+		dev_err(dev, "%s: Could not find RX swr slave device\n",
+			 __func__);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	rouleur->tx_swr_dev = get_matching_swr_slave_device(pdata->tx_slave);
+	if (!rouleur->tx_swr_dev) {
+		dev_err(dev, "%s: Could not find TX swr slave device\n",
+			__func__);
+		ret = -ENODEV;
+		goto err;
+	}
+
+	rouleur->regmap = devm_regmap_init_swr(rouleur->tx_swr_dev,
+					       &rouleur_regmap_config);
+	if (!rouleur->regmap) {
+		dev_err(dev, "%s: Regmap init failed\n",
+				__func__);
+		goto err;
+	}
+
+	/* Set all interupts as edge triggered */
+	for (i = 0; i < rouleur_regmap_irq_chip.num_regs; i++)
+		regmap_write(rouleur->regmap,
+			     (ROULEUR_DIG_SWR_INTR_LEVEL_0 + i), 0);
+
+	rouleur_regmap_irq_chip.irq_drv_data = rouleur;
+	rouleur->irq_info.wcd_regmap_irq_chip = &rouleur_regmap_irq_chip;
+	rouleur->irq_info.codec_name = "rouleur";
+	rouleur->irq_info.regmap = rouleur->regmap;
+	rouleur->irq_info.dev = dev;
+	ret = wcd_irq_init(&rouleur->irq_info, &rouleur->virq);
+
+	if (ret) {
+		dev_err(dev, "%s: IRQ init failed: %d\n",
+			__func__, ret);
+		goto err;
+	}
+	rouleur->tx_swr_dev->slave_irq = rouleur->virq;
+
+	mutex_init(&rouleur->micb_lock);
+	mutex_init(&rouleur->main_bias_lock);
+	mutex_init(&rouleur->rx_clk_lock);
+
+	ret = rouleur_set_micbias_data(rouleur, pdata);
+	if (ret < 0) {
+		dev_err(dev, "%s: bad micbias pdata\n", __func__);
+		goto err_irq;
+	}
+
+	/* Request for watchdog interrupt */
+	wcd_request_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHR_PDM_WD_INT,
+			"HPHR PDM WD INT", rouleur_wd_handle_irq, NULL);
+	wcd_request_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHL_PDM_WD_INT,
+			"HPHL PDM WD INT", rouleur_wd_handle_irq, NULL);
+	/* Disable watchdog interrupt for HPH */
+	wcd_disable_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHR_PDM_WD_INT);
+	wcd_disable_irq(&rouleur->irq_info, ROULEUR_IRQ_HPHL_PDM_WD_INT);
+
+	ret = snd_soc_register_component(dev, &soc_codec_dev_rouleur,
+				     NULL, 0);
+	if (ret) {
+		dev_err(dev, "%s: Codec registration failed\n",
+				__func__);
+		goto err_irq;
+	}
+
+	return ret;
+err_irq:
+	wcd_irq_exit(&rouleur->irq_info, rouleur->virq);
+	mutex_destroy(&rouleur->micb_lock);
+	mutex_destroy(&rouleur->main_bias_lock);
+	mutex_destroy(&rouleur->rx_clk_lock);
+err:
+	component_unbind_all(dev, rouleur);
+err_bind_all:
+	dev_set_drvdata(dev, NULL);
+	kfree(pdata);
+	kfree(rouleur);
+	return ret;
+}
+
+static void rouleur_unbind(struct device *dev)
+{
+	struct rouleur_priv *rouleur = dev_get_drvdata(dev);
+	struct rouleur_pdata *pdata = dev_get_platdata(rouleur->dev);
+
+	wcd_irq_exit(&rouleur->irq_info, rouleur->virq);
+	snd_soc_unregister_component(dev);
+	component_unbind_all(dev, rouleur);
+	mutex_destroy(&rouleur->micb_lock);
+	mutex_destroy(&rouleur->main_bias_lock);
+	mutex_destroy(&rouleur->rx_clk_lock);
+	dev_set_drvdata(dev, NULL);
+	kfree(pdata);
+	kfree(rouleur);
+}
+
+static const struct of_device_id rouleur_dt_match[] = {
+	{ .compatible = "qcom,rouleur-codec" },
+	{}
+};
+
+static const struct component_master_ops rouleur_comp_ops = {
+	.bind   = rouleur_bind,
+	.unbind = rouleur_unbind,
+};
+
+static int rouleur_compare_of(struct device *dev, void *data)
+{
+	return dev->of_node == data;
+}
+
+static void rouleur_release_of(struct device *dev, void *data)
+{
+	of_node_put(data);
+}
+
+static int rouleur_add_slave_components(struct device *dev,
+				struct component_match **matchptr)
+{
+	struct device_node *np, *rx_node, *tx_node;
+
+	np = dev->of_node;
+
+	rx_node = of_parse_phandle(np, "qcom,rx-slave", 0);
+	if (!rx_node) {
+		dev_err(dev, "%s: Rx-slave node not defined\n", __func__);
+		return -ENODEV;
+	}
+	of_node_get(rx_node);
+	component_match_add_release(dev, matchptr,
+			rouleur_release_of,
+			rouleur_compare_of,
+			rx_node);
+
+	tx_node = of_parse_phandle(np, "qcom,tx-slave", 0);
+	if (!tx_node) {
+		dev_err(dev, "%s: Tx-slave node not defined\n", __func__);
+			return -ENODEV;
+	}
+	of_node_get(tx_node);
+	component_match_add_release(dev, matchptr,
+			rouleur_release_of,
+			rouleur_compare_of,
+			tx_node);
+	return 0;
+}
+
+static int rouleur_probe(struct platform_device *pdev)
+{
+	struct component_match *match = NULL;
+	int ret;
+
+	ret = rouleur_add_slave_components(&pdev->dev, &match);
+	if (ret)
+		return ret;
+
+	return component_master_add_with_match(&pdev->dev,
+					&rouleur_comp_ops, match);
+}
+
+static int rouleur_remove(struct platform_device *pdev)
+{
+	component_master_del(&pdev->dev, &rouleur_comp_ops);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static const struct dev_pm_ops rouleur_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		rouleur_suspend,
+		rouleur_resume
+	)
+};
+#endif
+
+static struct platform_driver rouleur_codec_driver = {
+	.probe = rouleur_probe,
+	.remove = rouleur_remove,
+	.driver = {
+		.name = "rouleur_codec",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(rouleur_dt_match),
+#ifdef CONFIG_PM_SLEEP
+		.pm = &rouleur_dev_pm_ops,
+#endif
+		.suppress_bind_attrs = true,
+	},
+};
+
+module_platform_driver(rouleur_codec_driver);
+MODULE_DESCRIPTION("Rouleur Codec driver");
+MODULE_LICENSE("GPL v2");
+
diff --git a/asoc/codecs/rouleur/rouleur.h b/asoc/codecs/rouleur/rouleur.h
new file mode 100644
index 0000000..51abff3
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef _ROULEUR_H
+#define _ROULEUR_H
+
+#ifdef CONFIG_SND_SOC_ROULEUR
+extern int rouleur_info_create_codec_entry(struct snd_info_entry *codec_root,
+				    struct snd_soc_component *component);
+#else
+extern int rouleur_info_create_codec_entry(struct snd_info_entry *codec_root,
+				    struct snd_soc_component *component)
+{
+	return 0;
+}
+#endif /* CONFIG_SND_SOC_ROULEUR */
+
+#endif
diff --git a/asoc/codecs/rouleur/rouleur_slave.c b/asoc/codecs/rouleur/rouleur_slave.c
new file mode 100644
index 0000000..8fac8a6
--- /dev/null
+++ b/asoc/codecs/rouleur/rouleur_slave.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/component.h>
+#include <soc/soundwire.h>
+
+struct rouleur_slave_priv {
+	struct swr_device *swr_slave;
+};
+
+static int rouleur_slave_bind(struct device *dev,
+				struct device *master, void *data)
+{
+	int ret = 0;
+	struct rouleur_slave_priv *rouleur_slave = NULL;
+	uint8_t devnum = 0;
+	struct swr_device *pdev = to_swr_device(dev);
+
+	if (pdev == NULL) {
+		dev_err(dev, "%s: pdev is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	rouleur_slave = devm_kzalloc(&pdev->dev,
+				sizeof(struct rouleur_slave_priv), GFP_KERNEL);
+	if (!rouleur_slave)
+		return -ENOMEM;
+
+	swr_set_dev_data(pdev, rouleur_slave);
+
+	rouleur_slave->swr_slave = pdev;
+
+	ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+				"%s get devnum %d for dev addr %lx failed\n",
+				__func__, devnum, pdev->addr);
+		swr_remove_device(pdev);
+		return ret;
+	}
+	pdev->dev_num = devnum;
+
+	return ret;
+}
+
+static void rouleur_slave_unbind(struct device *dev,
+				struct device *master, void *data)
+{
+	struct rouleur_slave_priv *rouleur_slave = NULL;
+	struct swr_device *pdev = to_swr_device(dev);
+
+	if (pdev == NULL) {
+		dev_err(dev, "%s: pdev is NULL\n", __func__);
+		return;
+	}
+
+	rouleur_slave = swr_get_dev_data(pdev);
+	if (!rouleur_slave) {
+		dev_err(&pdev->dev, "%s: rouleur_slave is NULL\n", __func__);
+		return;
+	}
+
+	swr_set_dev_data(pdev, NULL);
+}
+
+static const struct swr_device_id rouleur_swr_id[] = {
+	{"rouleur-slave", 0},
+	{}
+};
+
+static const struct of_device_id rouleur_swr_dt_match[] = {
+	{
+		.compatible = "qcom,rouleur-slave",
+	},
+	{}
+};
+
+static const struct component_ops rouleur_slave_comp_ops = {
+	.bind   = rouleur_slave_bind,
+	.unbind = rouleur_slave_unbind,
+};
+
+static int rouleur_swr_up(struct swr_device *pdev)
+{
+	return 0;
+}
+
+static int rouleur_swr_down(struct swr_device *pdev)
+{
+	return 0;
+}
+
+static int rouleur_swr_reset(struct swr_device *pdev)
+{
+	return 0;
+}
+
+static int rouleur_swr_probe(struct swr_device *pdev)
+{
+	return component_add(&pdev->dev, &rouleur_slave_comp_ops);
+}
+
+static int rouleur_swr_remove(struct swr_device *pdev)
+{
+	component_del(&pdev->dev, &rouleur_slave_comp_ops);
+	return 0;
+}
+
+static struct swr_driver rouleur_slave_driver = {
+	.driver = {
+		.name = "rouleur-slave",
+		.owner = THIS_MODULE,
+		.of_match_table = rouleur_swr_dt_match,
+	},
+	.probe = rouleur_swr_probe,
+	.remove = rouleur_swr_remove,
+	.id_table = rouleur_swr_id,
+	.device_up = rouleur_swr_up,
+	.device_down = rouleur_swr_down,
+	.reset_device = rouleur_swr_reset,
+};
+
+static int __init rouleur_slave_init(void)
+{
+	return swr_driver_register(&rouleur_slave_driver);
+}
+
+static void __exit rouleur_slave_exit(void)
+{
+	swr_driver_unregister(&rouleur_slave_driver);
+}
+
+module_init(rouleur_slave_init);
+module_exit(rouleur_slave_exit);
+
+MODULE_DESCRIPTION("WCD937X Swr Slave driver");
+MODULE_LICENSE("GPL v2");